Introduction
Options to create RESTful Services on top ob ADF BC Model
Creating a RESTful Service Fassade on top of existing ADF Business Components Model is not that straightforward. From different „announcements" we know that Oracle plans to generate RESTful Services ADF BC SDO (Service Data Object) in the future. But for now we need a custom solution.
Exploring various possibilities I came up with the following
Note that the Service Facade is optional but recommended in terms of „separation of concerns", „service virtualization", „clean code", etc. Watch the youtube ADF Insider Essentials for the detailed explanation of the Service Facade Pattern.
Going from top to bottom in the diagram we have the following options
a) ADF BC -> SDO Service Interface -> WebService -> SOAP
b) ADF BC -> SDO Service Interface -> Inject EJB in REST-Resource -> REST
c) ADF BC -> Use AM Instance programmatically in REST-Resource ->REST
d) this one is planned for some 12.1.3+ release (and in future will be the default option to expose RESTful Services for a given ADF BC Model).
In this post I am covering case b. I did not find a sample on the web yet. So I give it a try cause it looks like THE pragmatic approach so far.
Howto
For the example I am using JDeveloper 12.1.2 and the corresponding Runtim ADF 12.1.2. In order to understand the next steps I assume you know the basics of ADF and JDeveloper.
1. Create ADF BC SDO SOAP Service for an Application Module:
Open the AM in „Overview", goto "Web Service" Tab. Now open the dialog to create the service interface.
Choose the desired VO-Instances and apply you changes.
JDeveloper should generate some files now. If you take a look in the *ServiceImpl.java Class you will notice that it is exposed as Stateless Session EJB. This is great!
Because from now on you can use the EJB in a REST-Resource to expose it as RESTful-Service. That's what we are planning to do next.
But before this step test your generated (SOAP-based) service first: Make sure you do not ran into the „StackOverflowException" because the SDO Service will try to traverse the ViewLinks recursively. So the following exception might be the one you will run into.
To fix this, open the corresponding ViewLink and uncheck the property „Generate Property in SDO"
2. Next: Create a new Project for the RESTful Service
Use the right project template from the NEW-Wizard
Open the NEW-Wizard again and create a RESTful service from new
Deselect the Checkbox in front of GET-Method. We are going to do that later in Sourcecode.
Finish. => This should generate a Java class and configure the project with the Jersey Library.
For the sample to complete the project needs on more adjustment in the project properties.
- One more Library (Context and Dependency Injection CDI)
and a dependency to the Model-Project because the RESTfulWebService project need access to the application module.
Thats all for the project setup.
3. Add some classes, annotations and the methods you want to expose
import javax.ejb.EJB;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import oracle.jbo.service.errors.ServiceException;
@Singleton
@Path("api")
public class HrRESTResource {
@EJB
HRAppModuleService serviceBean;
public HrRESTResource() {
}
/**
* getEmployeesView1: generated method. Do not modify.
*/
@GET
@Path("emp/{empId}")
@Produces(value = { "application/json", "application/xml" })
public EmpResult getEmployeesView1(@PathParam("empId") Integer employeeId) throws ServiceException {
EmployeesViewSDO empSDO = serviceBean.getEmployeesView1(employeeId);
EmpResult result = new EmpResult();
result.setEmployee(empSDO);
return result;
}
@GET
@Path("depts")
@Produces(value = { "application/json", "application/xml" })
public DeptResult findAllDepartments() throws ServiceException {
DeptResult result = new DeptResult();
result.setDepartmentList(serviceBean.findDepartmentsView1(null, null));
return result;
}
}
Important note: For what ever reason you need to annotate the REST-Resource with @javax.inject.Singleton. Otherwise the EJB-Injection won't work.
Just reuse the SDO-Entity-classes (eg. EmployeesViewSDO), but wrap them inside a custom „Result"-class which should be annotated with @XmlRootElement (JAX-B Standard).
@XmlRootElement
public class DeptResult {
public DeptResult() {
super();
}
@XmlElement(name="departments")
private List<DepartmentsViewSDO> departmentList;
public void setDepartmentList(List<DepartmentsViewSDO> departmentList) {
this.departmentList = departmentList;
}
public List<DepartmentsViewSDO> getDepartmentList() {
return departmentList;
}
}
See attached sample application for the whole source code.
4. Run an see the RESTful service in action
As a result we will get the proper JSON structure
if changing the accept- Header to: application/xml we will get XML-Result.
This is pretty cool. Because we can reuse the generated ADF BC SDO SOAP Service Layer as EJB for the REST Fassade.
More Information