Enterprise Java Development@TOPIC@
Built on: 2014-03-07 00:33 EST
Copyright © 2014 jim stafford (jcstaff@apl.jhu.edu)
Abstract
This presentation contains introductory topics for packaging EJBs within the web tier and exposing thru RMI and JAX-RS interfaces.
Introduce key technology for exposing resources to the web (JAX-RS and JAXB)
Introduce flexible EJB deployment (WAR-based)
At the completion of this topic, the student shall
have more understanding of:
flexibility of EJB deployment
exposing resources using web-friendly HTTP facades
marshaling DTOs using web-friendly representations (XML)
be able to:
Deploy an EJB within a WAR
Expose an asset or functionality through a (web) resource and URI using JAX-RS
Use HTTP methods to interact with resources using JAX-RS
Implement representations with XML, JAXB, and JAX-RS
Prior to JavaEE 6
EJB beans could only be deployed in EJB modules
EJB modules integrated with WARs using common EAR
Starting with JavaEE 6
EJB beans can be embedded within WAR
EJB modules can be deployed within WAR
|-- index.html
`-- WEB-INF
...
|-- jboss-web.xml
|-- lib
`-- web.xml
index.html - example static web resource
WEB-INF/jboss-web.xml - container-specific configuration. Used here to control name of WAR
WEB-INF/lib - directory to place jars for classpath
WEB-INF/web.xml - standard WAR deployment descriptor
EJB beans developed external to WAR
Integrated using WEB-INF/lib
WEB-INF/lib/ `-- webejbCustomerEJB-3.0.2012.2-SNAPSHOT.jar
example EJB module contents
$ jar tf WEB-INF/lib/webejbCustomerEJB-3.0.2012.2-SNAPSHOT.jar | sort ejava/examples/ejbwar/customer/bo/Customer.class ejava/examples/ejbwar/customer/bo/CustomerRepresentation.class ejava/examples/ejbwar/customer/bo/Customers.class ejava/examples/ejbwar/customer/client/ ejava/examples/ejbwar/customer/client/CustomerClient.class ejava/examples/ejbwar/customer/client/CustomerClientImpl.class ejava/examples/ejbwar/customer/CustomerResources.class ejava/examples/ejbwar/customer/Customers.class ejava/examples/ejbwar/customer/dao/ ejava/examples/ejbwar/customer/dao/CustomerDAO.class ejava/examples/ejbwar/customer/dao/CustomerDAOImpl.class ejava/examples/ejbwar/customer/ejb/ ejava/examples/ejbwar/customer/ejb/CustomerMgmt.class ejava/examples/ejbwar/customer/ejb/CustomerMgmtEJB.class ejava/examples/ejbwar/customer/ejb/CustomerMgmtLocal.class ejava/examples/ejbwar/customer/ejb/CustomerMgmtRemote.class ejava/examples/ejbwar/customer/rs/ ejava/examples/ejbwar/customer/rs/CustomersResource.class META-INF/beans.xml META-INF/MANIFEST.MF META-INF/persistence.xml
JNDI name uses WAR name (minus.war)
java:global/jaxrsInventoryWAR/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtRemote java:app/jaxrsInventoryWAR/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtRemote java:module/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtRemote java:jboss/exported/jaxrsInventoryWAR/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtRemote java:global/jaxrsInventoryWAR/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtLocal java:app/jaxrsInventoryWAR/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtLocal java:module/CustomerMgmtEJB!ejava.examples.ejbwar.customer.ejb.CustomerMgmtLocal
jboss-web.xml used to express WAR name without version#
$ cat WEB-INF/jboss-web.xml
<jboss-web>
<!-- needed to always assure that version# does not get into JNDI name -->
<context-root>jaxrsInventoryWAR</context-root>
</jboss-web>
EJB module contains persistence.xml and CDI beans.xml
META-INF/beans.xml META-INF/persistence.xml
EJB beans developed as part of WAR
No need to declare new EJB module just to get EJB bean functionality
WEB-INF
|-- beans.xml
|-- classes
| |-- ejava
| | `-- examples
| | `-- ejbwar
| | `-- inventory
| | |-- bo
| | | |-- Categories.class
| | | |-- Category.class
| | | |-- InventoryRepresentation.class
| | | |-- Product.class
| | | `-- Products.class
| | |-- client
| | | |-- InventoryClient.class
| | | `-- InventoryClientImpl.class
| | |-- dao
| | | |-- InventoryDAO.class
| | | `-- InventoryDAOImpl.class
| | |-- ejb
| | | |-- InventoryMgmtEJB.class
| | | `-- InventoryResources.class
| | |-- rmi
| | | |-- InventoryMgmtRemote.class
| | | `-- InventoryMgmtRMIEJB.class
| | `-- rs
| | |-- Application.class
| | |-- CategoriesResource.class
| | |-- PrettyPrinter.class
| | |-- ProductsResource.class
| | |-- ResourceHelper.class
| | `-- TxFilter.class
| `-- META-INF
| `-- persistence.xml
...
CDI descriptor hosted in WEB-INF/beans.xml
persistence unit hosted in WEB-INF/classes/META-INF/persistence.xml
JNDI name uses WAR name (minus.war)
java:global/jaxrsInventoryWAR/InventoryMgmtRMIEJB!ejava.examples.ejbwar.inventory.rmi.InventoryMgmtRemote java:app/jaxrsInventoryWAR/InventoryMgmtRMIEJB!ejava.examples.ejbwar.inventory.rmi.InventoryMgmtRemote java:module/InventoryMgmtRMIEJB!ejava.examples.ejbwar.inventory.rmi.InventoryMgmtRemote java:jboss/exported/jaxrsInventoryWAR/InventoryMgmtRMIEJB!ejava.examples.ejbwar.inventory.rmi.InventoryMgmtRemote java:global/jaxrsInventoryWAR/InventoryMgmtRMIEJB java:app/jaxrsInventoryWAR/InventoryMgmtRMIEJB java:module/InventoryMgmtRMIEJB
Alternative: Use traditional RMI interface (specific to Java)
Alternative: Use Web-oriented HTTP interface (open to all platforms)
JAX-RS 1.x only addresses server side
Many vendor-specific client libraries (e.g., RESTEasy, Jersey, HttpClient)
HTTP/REST Client API will be part of JAX-RS 2.0
Class examples implements with Apache HttpClient
Notional example accessing /index.html
HttpClient client = new DefaultHttpClient();
URI appURI = "http://127.0.0.1:8080/jaxrsInventoryWAR";
URI uri = UriBuilder.fromUri(appURI).path("index.html").build();
//build the overall request
HttpGet get = new HttpGet(uri);
get.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML);
HttpResponse response = client.execute(get);
if (Response.Status.OK.getStatusCode() == response.getStatusLine().getStatusCode()) {
return ...
}
An asset to be exposed to the web
Document, set of properties, method, nearly anything
Web has limited methods but an unlimited number of resources
Example
Products
Categories
Customers
An address to access a particular resource
Can be expressed relative
index.html /rest/products /rest/categories /rest/customers
Can be expressed fully qualified
http://127.0.0.1:8080/jaxrsInventoryWAR/index.html http://127.0.0.1:8080/jaxrsInventoryWAR/rest/products http://127.0.0.1:8080/jaxrsInventoryWAR/rest/categories http://127.0.0.1:8080/jaxrsInventoryWAR/rest/customers
May have query parameters
http://127.0.0.1:8080/jaxrsInventoryWAR/rest/categories?name=&offset=0&limit=0
May have nested path parameters
http://127.0.0.1:8080/jaxrsInventoryWAR/rest/products/1
Building URIs with JAX-RS URIBuilder
//start with the URI for the WAR deployed to the server
//that ends with the context-root
return UriBuilder.fromUri(appURI)
//add path info from the
//javax.ws.rs.core.Application @ApplicationPath
.path("rest")
//add in @Path added by resource class
.path(resourceClass)
//add in @Path added by resource class' method
.path(resourceClass,method);
//marshall @PathParm into the URI
.build(id);
Bounded ("uniform interface")
Primary Set
GET - non-destructive read
POST - create and other methods
PUT - create or update
DELETE - delete
Secondary Set
HEAD - a GET without the data - metadata only
OPTIONS - lists which methods supported
Example: Get Product ID=1
GET http://127.0.0.1:8080/jaxrsInventoryWAR/rest/products/1
Standardized
2xx - success
3xx - success/redirect
4xx - client error
5xx - server error
Common Set
200 - OK
201 - CREATED
400 - BAD_REQUEST
404 - NOT_FOUND
500 - INTERNAL_ERROR
Annotated with @Path
Injected with implementation details and call context
@Path("/products")
public class ProductsResource {
private static final Log log = LogFactory.getLog(ProductsResource.class);
@Inject
private InventoryMgmtEJB ejb;
@Context
private Request request;
@Context
private UriInfo uriInfo;
...
Create new resource or tunnel service
Returns CREATED and URO of created resource
@POST @Path("")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Response addCustomer(Customer customer) {
log.debug(String.format("%s %s", request.getMethod(), uriInfo.getAbsolutePath()));
try {
Customer c = ejb.addCustomer(customer);
URI uri = UriBuilder.fromUri(uriInfo.getAbsolutePath())
.path(CustomersResource.class, "getCustomer")
.build(c.getId());
return Response.created(uri)
.entity(c)
.build();
} catch (Exception ex) {
return serverError(log, "creating person", ex).build();
}
}
Non-destructive read
Returns OK with payload
@GET @Path("{id}")
@Produces(MediaType.APPLICATION_XML)
public Response getProduct(@PathParam("id")int id) {
log.debug(String.format("%s %s", request.getMethod(), uriInfo.getAbsolutePath()));
try {
Product product = ejb.getProduct(id);
if (product != null) {
return Response.ok(product)
.build();
}
else {
return Response.status(Response.Status.NOT_FOUND)
.entity(String.format("unable to locate product %d", id))
.type(MediaType.TEXT_PLAIN)
.build();
}
} catch (Exception ex) {
return ResourceHelper.serverError(log, "getting product", ex).build();
}
}
Create new or update existing
Optional results
@PUT @Path("{id}")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Response updateProduct(@PathParam("id") int id, Product product) {
log.debug(String.format("%s %s", request.getMethod(), uriInfo.getAbsolutePath()));
try {
Product p = ejb.updateProduct(product);
return Response.ok(p)
.build();
} catch (Exception ex) {
return ResourceHelper.serverError(log, "update product", ex).build();
}
}
Deletes specified resource
Optional results
@DELETE @Path("{id}")
@Produces(MediaType.APPLICATION_XML)
public Response deleteCustomer(@PathParam("id") int id) {
log.debug(String.format("%s %s", request.getMethod(), uriInfo.getAbsolutePath()));
try {
ejb.deleteCustomer(id);
return Response.ok()
.build();
} catch (Exception ex) {
return serverError(log, "deleting person", ex).build();
}
}
Request supplies method, URI, optional payload, and other properties
GET /jaxrsInventoryWAR/rest/categories?name=snacks&offset=0&limit=0 HTTP/1.1 Accept: application/xml Host: 127.0.0.1:7080 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.1.3 (java 1.5)
Response provides status code, optional entity, and other properties]
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Tue, 06 Nov 2012 07:32:18 GMT
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:catageories xmlns:ns2="http://webejb.ejava.info/inventory" count="1" limit="0" offset="0" version="0">
<categories id="4" name="snacks" version="1">
<productCount>1</productCount>
</categories>
</ns2:catageories>
Elements
Attributes
Namespaces
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:catageory name="snacks" id="1" version="1" xmlns:ns2="http://webejb.ejava.info/inventory">
<productCount>1</productCount>
<products>
<product id="1" name="chips" version="0"/>
</products>
</ns2:catageory>
@XmlRootElement - needed to be able to manipulate object independently of graph
@XmlType - defines/names structure of XML construct
@XmlAccessorType - directs JAXB to invoke property or field accessors
@XmlRootElement(name="catageory", namespace=InventoryRepresentation.NAMESPACE)
@XmlType(name="Category", namespace=InventoryRepresentation.NAMESPACE)
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Category extends InventoryRepresentation {
private int id;
private String name;
private Integer productCount;
private List<Product> products=new ArrayList<Product>();
public Category() {}
public Category(String name) {
this.name=name;
}
...
}
Typically used for identifying properties
Single value, non-repeating
@XmlAttribute(required=true)
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
@XmlAttribute(required=true)
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
Typically used for descriptive properties
@XmlElement(required=true)
public int getProductCount() { return productCount!=null ? productCount :products.size();}
public void setProductCount(int productCount) {
this.productCount = productCount;
}
JAXBContext
Marshaller
JAXB_FORMATTED_OUTPUT
JAXBContext jbx = JAXBContext.newInstance(object.getClass());
Marshaller marshaller = jbx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
marshaller.marshal(object, writer);
return writer.toString();
JAX-RS requires built-in provider support for JAXB
JAX-RS defines marshalling framework to add extensions
Client request payload demarshalled into input JAXB class
@POST @Path("")
@Consumes(MediaType.APPLICATION_XML)
public Response addCustomer(Customer customer) {