REST Service Infrastructure
This article applies to the Product Manager Version 6 and following.
Overview
HPM server provides a standard way to contribute REST services based on the JAX-RS (JSR 311) standard. JAX-RS is a JEE standard and API to implement restfull services in Java. Thanks to standardization this is a perfect approach to system integration especially in enterprise landscapes. It allows to expose HPM data to the external systems using a well defined technology and thus provides a way to access HPM data by developers who are not familiar with HPM SDK and API.
HPM platform in Version 6.0 does not provide predefined REST services, rather it provides an infrastructure to develop and contribute services into HPM context. Future versions of the platform will contain a set of rest services to access and modify data in HPM.
The HPM platform provides REST infrastructure with the following components:
JAX-RS implementation called Jersey, the reference implementation developed by Oracle. It contains all necessary classes and interfaces to implement rest services.
Servlet container - Jetty 6. This container is integrated into the OSGi Equinox environment. Jetty properties like host, port and others can be configured in the server.properties file of HPM.
HTTP and HTTPS connectors (provided by Jetty)
Integration with HPM security and authentication.
Extension point in HPM platform to which REST services implementations can be contributed. This architecture brings clear separation between HPM API and JAX-RS API and makes REST services development easy and clear for developers.
Security
The security of REST-based access to Product Manager is based on HTTP Basic Authentication combined with Product Manager's own authorization mechanisms. It is required to create a HPM user which has internal authentication mode (not external or mixed mode). For security reasons external authentication (Active Directory authentication) must not be used from outside. We also strongly recommend to activate HTTPS protocol in production environment because HTTP Basic Authentication does not encrypt user password which is sent over HTTP.
Create your own REST service
In order to create a REST services in HPM you need to do following steps
Implement JAX-RS complaint service. Please consult Jersey documentation and other documentation how building JAX-RS services.
Contribute JAX-RS resources to com.heiler.ppm.rest.server extension point which is exported by com.heiler.ppm.rest.server bundle
Extension point com.heiler.ppm.rest.server is very simple and accepts implementations of ResourceProvider interface. Implementation should have only one method getResources() which returns a list of services implementation (resources in terms of JAX-RS api).
Extension point contribution example:
<extension point=
"com.heiler.ppm.rest"
>
<Resource
class
=
"com.heiler.ppm.custom.rest.CustomResourceProvider"
>
</Resource>
</extension>
Resource provider implementation
public
class
CustomResourceProvider
implements
ResourceProvider
{
@Override
public
Collection< Class< ? >> getResources()
{
List< Class< ? >> result =
new
LinkedList< Class< ? >>();
result.add( ArticleResource.
class
);
return
result;
}
}
REST Resource Example
You can find the complete source code of this example in the HPM 6.0 SDK. The project is called com.heiler.ppm.custom.rest
Find articles by EAN in a given catalog:
@Path
(
"/catalogs/{catalogId: [0-9]+}/articles"
)
public
class
ArticleResource
{
@GET
@Produces
( MediaType.APPLICATION_XML )
public
Articles findAll(
@PathParam
(
"catalogId"
) Long catalogId,
@QueryParam
(
"ean"
) String ean )
throws
CoreException, InterruptedException
{
Articles result =
new
Articles();
result.setArticles( findArticlesByEan( catalogId, ean ) );
return
result;
}
private
List< Article > findArticlesByEan( Long catalogId, String ean )
throws
CoreException, InterruptedException
{
ReportResult report = findAsReport( catalogId, ean );
return
loadByReport( report );
}
private
List< Article > loadByReport( ReportResult report )
throws
CoreException, InterruptedException
{
FieldPath articleIdentifier =
new
FieldPath(
"ArticleType.SupplierAid"
);
FieldPath shortDescriptionEN =
new
FieldPath(
"ArticleLangType.DescriptionShort"
);
shortDescriptionEN.getEntityPath()
.setLogicalKeyValue(
"ArticleLangType.LK.Language"
,
new
Long(
7
) );
ListModel listModel = loadArticleList( report, articleIdentifier, shortDescriptionEN );
List< Article > articles =
new
ArrayList( listModel.size() );
for
( ListEntry listEntry : listModel )
{
Article article =
new
Article();
article.setId( listEntry.getEntityProxy()
.getId() );
article.setIdentifier( ( String ) listEntry.getFieldValue( listModel.getColumn( articleIdentifier )
.getIndex() ) );
article.setShortDescription( ( String ) listEntry.getFieldValue( listModel.getColumn( shortDescriptionEN )
.getIndex() ) );
articles.add( article );
}
return
articles;
}
}
Article data container:
@XmlRootElement
public
class
Articles
{
private
List< Article > articles;
public
Articles()
{
this
.articles =
new
LinkedList< Article >();
}
public
List< Article > getArticles()
{
return
this
.articles;
}
public
void
setArticles( List< Article > articles )
{
this
.articles = articles;
}
}
...
@XmlRootElement
public
class
Article
{
private
long
id;
private
String identifier;
private
String shortDescription;
public
long
getId()
{
return
this
.id;
}
public
void
setId(
long
id )
{
this
.id = id;
}
public
String getIdentifier()
{
return
this
.identifier;
}
public
void
setIdentifier( String identifier )
{
this
.identifier = identifier;
}
public
String getShortDescription()
{
return
this
.shortDescription;
}
public
void
setShortDescription( String shortDescription )
{
this
.shortDescription = shortDescription;
}
}
Please, be careful when designing REST web services. Wrong design or incorrect use of rest-full principals may result a unmanageable solution which is difficult to use and integrate with other systems, which has poor performance and is not clear to other developers. Please remember the following principals of restfull services:
REST services are stateless. It means that service implementations should not remember any kind of session between requests.
In REST design everything is a resource. Every data structure received from the server represents an identifiable resource. Every request can be seen as a CRUD on a resource.
REST services can read and modify resources and usually should not look like remote procedure call (RPC).
You can find a lot of information about designing REST service in a book "Restful Web Services" and "Service Design Patterns: Fundamental Design Solutions for SOAP/WSDL and RESTful Web Services".