Access to add and change pages is restricted. See: https://cwiki.apache.org/confluence/display/OFBIZ/Wiki+access

Overview

This tutorial documents the steps to expose an ofbiz service using REST. The ofbiz ping service is exposed.  The ping service returns a copy on the input message to the response.  If the input message is null, ping returns PONG.


This is related: REST Service Implementation

The following steps show the REST implementation on base of Apache Wink.

Apache Wink is retired

As of April 2017, Apache Wink is retired. For new REST implementations, it is not recommended to use it.

This tutorial does not work with OFBIz Release 16.11 and above.

This page will be either updated with an alternative or deprecated in the future.

Assumptions

This tutorial assumes you have followed the ofbiz developer tutorial: http://cwiki.apache.org/confluence/x/cQFk

Step 1 - Create a new component

ant create-component

Component name: restcomponent
Component resource name: RestComponent
Webapp name: restcomponent
Base permission: RESTCOMPONENT

Step 2 - Grab Apache Wink*

Download apache wink from here: https://wink.apache.org/downloads.html

Unzip and copy lib/* and dist/* to your ofbiz component restcomponent/lib

\* read this https://dzone.com/articles/apache-cxf-vs-apache-axis-vs for a comparison between 2 Apache solutions regarding REST and Spring WS

Step 3 - Create java files and update web.xml

The steps followed were taken from the wink user (i.e. developer) guide:  apache-wink-developer-guide.html

In the folder restcomponent/src/restcomponent, create:

PingApplication.java
PingResource.java

package restcomponent;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

public class PingApplication extends Application {
  @Override
  public Set<Class<?>> getClasses() {
      Set<Class<?>> classes = new HashSet<Class<?>>();
      classes.add(PingResource.class);
      return classes;
  }
}
package restcomponent;


import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import javolution.util.FastMap;

import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.entity.DelegatorFactory;
import org.ofbiz.entity.GenericDelegator;
import org.ofbiz.service.GenericDispatcher;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;

@Path("/ping")
public class PingResource {

	@GET
	@Produces("text/plain")
	@Path("{message}")
    public Response sayHello(@PathParam("message") String message) {

    	GenericDelegator delegator = (GenericDelegator) DelegatorFactory.getDelegator("default");
    	LocalDispatcher dispatcher = GenericDispatcher.getLocalDispatcher("default",delegator);

    	Map<String, String> paramMap = UtilMisc.toMap( "message", message );

		Map<String, Object> result = FastMap.newInstance();
		try {
			result = dispatcher.runSync("ping", paramMap);
		} catch (GenericServiceException e1) {
			Debug.logError(e1, PingResource.class.getName());
			return Response.serverError().entity(e1.toString()).build();
		}

		if (ServiceUtil.isSuccess(result)) {
			return Response.ok("RESPONSE: *** " + result.get("message") + " ***").type("text/plain").build();
		}

		if (ServiceUtil.isError(result) || ServiceUtil.isFailure(result)) {
			return Response.serverError().entity(ServiceUtil.getErrorMessage(result)).build();
		}

		// shouldn't ever get here ... should we?
		throw new RuntimeException("Invalid ");
    }
}

Update restcomponent/webapp/restcomponent/WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <display-name>Open For Business - RestComponent Component</display-name>
    <description>RestComponent Component of the Open For Business Project</description>

  	<servlet>
		<servlet-name>restServlet</servlet-name>
		<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
                <init-param>
                  <param-name>javax.ws.rs.Application</param-name>
                  <param-value>restcomponent.PingApplication</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>restServlet</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>

</web-app>

Step 4 - compile and run

compile: ./ant

run: ./ant run

Test using a web browser:

http://localhost:8080/restcomponent/ping/abc

Step 5 - turn on authentication in the service

Turn on auth="true" in framework/common/servicedef/services_test.xml

    <service name="ping" engine="java" export="true" require-new-transaction="true"
            location="org.ofbiz.common.CommonServices" invoke="ping" auth="true">

Now change PingResource to take http headers with the login.username and login.password and pass the values to the service.

package restcomponent;


import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import javolution.util.FastMap;

import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.entity.DelegatorFactory;
import org.ofbiz.entity.GenericDelegator;
import org.ofbiz.service.GenericDispatcher;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;

@Path("/ping")
public class PingResource {

	@Context
	HttpHeaders headers;

	@GET
	@Produces("text/plain")
	@Path("{message}")
    public Response sayHello(@PathParam("message") String message) {

		String username = null;
		String password = null;

		try {
			username = headers.getRequestHeader("login.username").get(0);
			password = headers.getRequestHeader("login.password").get(0);
		} catch (NullPointerException e) {
			return Response.serverError().entity("Problem reading http header(s): login.username or login.password").build();
		}

		if (username == null || password == null) {
			return Response.serverError().entity("Problem reading http header(s): login.username or login.password").build();
		}

    	GenericDelegator delegator = (GenericDelegator) DelegatorFactory.getDelegator("default");
    	LocalDispatcher dispatcher = GenericDispatcher.getLocalDispatcher("default",delegator);

    	Map<String, String> paramMap = UtilMisc.toMap(
    			"message", message,
    			"login.username", username,
    			"login.password", password
    		);

		Map<String, Object> result = FastMap.newInstance();
		try {
			result = dispatcher.runSync("ping", paramMap);
		} catch (GenericServiceException e1) {
			Debug.logError(e1, PingResource.class.getName());
			return Response.serverError().entity(e1.toString()).build();
		}

		if (ServiceUtil.isSuccess(result)) {
			return Response.ok("RESPONSE: *** " + result.get("message") + " ***").type("text/plain").build();
		}

		if (ServiceUtil.isError(result) || ServiceUtil.isFailure(result)) {
			return Response.serverError().entity(ServiceUtil.getErrorMessage(result)).build();
		}

		// shouldn't ever get here ... should we?
		throw new RuntimeException("Invalid ");
    }
}

Now trying accessing with web browser:

Access to the specified resource () has been forbidden.

Step 6 - add http headers for authentication

If using firefox, install RESTClient

https://addons.mozilla.org/en-US/firefox/addon/9780

In firefox, open the RESTClient addon, and set:

Method = GET
URL = http://localhost:8080/restcomponent/ping/abc
Request Headers:
login.username = admin
login.password = ofbiz

Click send - you should see the ping response.

Example files

Attached to this page is a backup of my restcomponent.tgz

Next Steps

I am currently working on making REST configurable in the service definition file (similar to export="true").  This could remove the need to manually code any REST services.

  • No labels

1 Comment

  1. Apache Juneau has just become a top level project, might be worth a look juneau.apache.org