JAX-RPC Coffee Supplier Service

The Coffee Break servers are clients of the JAX-RPC coffee supplier service. The service code consists of the service interface, the service implementation class, and several JavaBeans components that are used for method parameters and return types.

Service Interface

The service interface, SupplierIF, defines the methods that can be called by remote clients. The parameters and return types of these methods are the JavaBeans components listed in the preceding section.

The source code for the SupplierIF interface, which follows, resides in the <INSTALL>/j2eetutorial14/examples/cb/jaxrpc/src/ directory.

package com.sun.cb;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface SupplierIF extends Remote {

  public ConfirmationBean placeOrder(OrderBean order) 
    throws RemoteException;
  public PriceListBean getPriceList() throws RemoteException;
} 

Service Implementation

The SupplierImpl class implements the placeOrder and getPriceList methods, which are defined by the SupplierIF interface. So that you can focus on the code related to JAX-RPC, these methods are short and simplistic. In a real world application, these methods would access databases and would interact with other services, such as shipping, accounting, and inventory.

The placeOrder method accepts as input a coffee order and returns a confirmation for the order. To keep things simple, the placeOrder method confirms every order and sets the ship date in the confirmation to the next day. The source code for the placeOrder method follows:

public ConfirmationBean placeOrder(OrderBean order) {

  Date tomorrow = DateHelper.addDays(new Date(), 1);
  ConfirmationBean confirmation = 
    new ConfirmationBean(order.getId(), 
      DateHelper.dateToCalendar(tomorrow));
  return confirmation;
} 

The getPriceList method returns a PriceListBean object, which lists the name and price of each type of coffee that can be ordered from this service. The getPriceList method creates the PriceListBean object by invoking a private method named loadPrices. In a production application, the loadPrices method would fetch the prices from a database. However, our loadPrices method takes a shortcut by getting the prices from the SupplierPrices.properties file. Here are the getPriceList and loadPrices methods:

public PriceListBean getPriceList() {

  PriceListBean priceList = loadPrices(); 
  return priceList;
}

private PriceListBean loadPrices() {

  String propsName = "com.sun.cb.SupplierPrices";
  Date today = new Date();
  Date endDate = DateHelper.addDays(today, 30);

  PriceItemBean[] priceItems = 
    PriceLoader.loadItems(propsName);
  PriceListBean priceList = 
    new PriceListBean(DateHelper.dateToCalendar(today),
      DateHelper.dateToCalendar(endDate), priceItems);

  return priceList;
} 

Publishing the Service in the Registry

Because we want customers to find our service, we publish it in a registry. When the JAX-RPC web application is started and stopped, the context listener object ContextListener publishes and removes the service in the contextInitialized and contextDestroyed methods respectively.

The contextInitialized method begins by retrieving the registry and endpoint URLs and coffee registry properties. Both the context initializer and destroyer call the makeConnection method, which creates a connection to the registry. See Establishing a Connection for more information. To do this, it first specifies a set of connection properties using the registry URLs retrieved from a resource bundle.

Properties props = new Properties();
props.setProperty("javax.xml.registry.queryManagerURL",
  queryUrl);
props.setProperty("javax.xml.registry.lifeCycleManagerURL",
  publishUrl);
props.setProperty("com.sun.xml.registry.http.proxyHost", 
  httpProxyHost);
props.setProperty("com.sun.xml.registry.http.proxyPort", 
  httpProxyPort);
props.setProperty("com.sun.xml.registry.https.proxyHost", 
  httpsProxyHost);
props.setProperty("com.sun.xml.registry.https.proxyPort", 
  httpsProxyPort); 

Next, the makeConnection method creates the connection using a connection factory it looks up using JNDI:

context = new InitialContext();
factory = (ConnectionFactory) 
  context.lookup("java:comp/env/eis/JAXR");
factory.setProperties(props);
connection = factory.createConnection(); 

Next, the program instantiates a utility class named JAXRPublisher. To publish the service, the contextInitialized method invokes the executePublish method, which accepts as input connection, username, password, and endpoint. The username and password values are required by the registry. The endpoint value is the URL that remote clients will use to contact our JAX-RPC service. The executePublish method of JAXRPublisher returns a key that uniquely identifies the service in the registry. The contextInitialized method saves this key in a text file named orgkey.txt. The contextDestroyed method reads the key from orgkey.txt so that it can delete the service. See Deleting the Service From the Registry. The source code for the contextInitialized method follows.

public void contextInitialized(ServletContextEvent event) {
  ResourceBundle registryBundle =
    ResourceBundle.getBundle("com.sun.cb.CoffeeBreak");

  String queryURL = registryBundle.getString("query.url");
  String publishURL = registryBundle.getString("publish.url");
  logger.info(queryURL);
  logger.info(publishURL);

  String username = 
    registryBundle.getString("registry.username");
  String password = 
    registryBundle.getString("registry.password");
  String keyFile = registryBundle.getString("key.file");

  JAXRPublisher publisher = new JAXRPublisher();
  ServletContext context = event.getServletContext();  

  String endpointURL = URLHelper.getEndpointURL();

  Connection connection = 
    makeConnection(queryURL, publishURL);

  if (connection != null) {
    String key = publisher.executePublish(connection,
      username, password, endpointURL);
    try {
      FileWriter out = new FileWriter(keyFile);
      out.write(key);
      out.flush();
      out.close();
  }   catch (IOException ex) {
      logger.severe(ex.getMessage());
    }
    try {
      connection.close();
    } catch (Exception je) {}
  }
} 

The JAXRPublisher class is almost identical to the sample program JAXRPublish.java, which is described in Managing Registry Data.

The executePublish method takes four arguments: the connection to the registry, a user name, a password, and an endpoint. It begins by obtaining a RegistryService object and then a BusinessQueryManager object and a BusinessLifeCycleManager object, which enable it to perform queries and manage data:

rs = connection.getRegistryService();
blcm = rs.getBusinessLifeCycleManager();
bqm = rs.getBusinessQueryManager(); 

Because it needs password authentication in order to publish data, the executePublish method then uses the username and password arguments to establish its security credentials:

PasswordAuthentication passwdAuth = 
  new PasswordAuthentication(username, 
    password.toCharArray());
Set creds = new HashSet();
creds.add(passwdAuth);
connection.setCredentials(creds); 

It then creates an Organization object with the name JAXRPCCoffeeSupplier, and a User object that will serve as the primary contact. This code is almost identical to the code in the JAXR examples.

ResourceBundle bundle =
  ResourceBundle.getBundle("com.sun.cb.CoffeeBreak");

// Create organization name and description 
Organization org =
  blcm.createOrganization(bundle.getString("org.name"));
InternationalString s = 
  blcm.createInternationalString
  (bundle.getString("org.description"));
org.setDescription(s);

// Create primary contact, set name 
User primaryContact = blcm.createUser();
PersonName pName =
  blcm.createPersonName(bundle.getString("person.name"));
primaryContact.setPersonName(pName); 

The executePublish method adds a telephone number and email address for the user, then makes the user the primary contact:

org.setPrimaryContact(primaryContact); 

It gives JAXRPCCoffeeSupplier a classification using the North American Industry Classification System (NAICS). In this case it uses the classification "Other Grocery and Related Products Wholesalers."

Classification classification = (Classification)
  blcm.createClassification(cScheme,
    bundle.getString("classification.name"), 
    bundle.getString("classification.value"));
Collection classifications = new ArrayList();
classifications.add(classification);
org.addClassifications(classifications); 

Next, it adds the JAX-RPC service, called JAXRPCCoffee Service, and its service binding. The access URL for the service binding contains the endpoint URL that remote clients will use to contact our service:

http://localhost:8080/jaxrpc-coffee-supplier/jaxrpc 
Collection services = new ArrayList();
Service service =
  blcm.createService(bundle.getString("service.name"));
InternationalString is = 
  blcm.createInternationalString
  (bundle.getString("service.description"));
service.setDescription(is);

// Create service bindings
Collection serviceBindings = new ArrayList();
ServiceBinding binding = blcm.createServiceBinding();
is = blcm.createInternationalString
  (bundle.getString("service.binding"));
binding.setDescription(is);
binding.setValidateURI(false);
binding.setAccessURI(endpoint);
serviceBindings.add(binding);

// Add service bindings to service
service.addServiceBindings(serviceBindings);

// Add service to services, then add services to organization
services.add(service);
org.addServices(services); 

Then it saves the organization to the registry:

Collection orgs = new ArrayList();
orgs.add(org);
BulkResponse response = blcm.saveOrganizations(orgs); 

The BulkResponse object returned by saveOrganizations includes the Key object containing the unique key value for the organization. The executePublish method first checks to make sure that the saveOrganizations call succeeded.

If the call succeeded, the method extracts the value from the Key object and displays it:

Collection keys = response.getCollection();
Iterator keyIter = keys.iterator();
if (keyIter.hasNext()) {
  javax.xml.registry.infomodel.Key orgKey =
    (javax.xml.registry.infomodel.Key) keyIter.next();
  id = orgKey.getId();
  logger.info("Organization key is " + id);
} 

Finally, the method returns the string id so that the contextInitialized method can save it in a file for use by the contextDestroyed method.

Deleting the Service From the Registry

The contextDestroyed method deletes the service from the registry. Like the contextInitialized method, the contextDestroyed method starts by fetching the registry URL and other values from the CoffeeBreak.properties file. One these values, keyFile, is the name of the file that contains the key that uniquely identifies the service. The contextDestroyed method reads the key from the file, connects to the registry by invoking makeConnection, and then deletes the service from the registry by calling executeRemove. Here is the source code for the contextDestroyed method:

public void contextDestroyed(ServletContextEvent event) {
  String keyStr = null;

  ResourceBundle registryBundle =
    ResourceBundle.getBundle("com.sun.cb.CoffeeBreak");

  String queryURL = registryBundle.getString("query.url");
  String publishURL = registryBundle.getString("publish.url");
  String username = 
    registryBundle.getString("registry.username");
  String password = 
    registryBundle.getString("registry.password");
  String keyFile = registryBundle.getString("key.file");

  try {
    FileReader in = new FileReader(keyFile);
    char[] buf = new char[512];
    while (in.read(buf, 0, 512) >= 0) { }
    in.close();
    keyStr = new String(buf).trim();
  } catch (IOException ex) {
    logger.severe("contextDestroyed: Exception: " + 
      ex.toString());
  }

  JAXRRemover remover = new JAXRRemover();
  Connection connection = makeConnection(queryURL, 
    publishURL);
  if (connection != null) {
    javax.xml.registry.infomodel.Key modelKey = null;
    modelKey = remover.createOrgKey(connection, keyStr);
    remover.executeRemove(connection, modelKey, username,
      password);
    try {
      connection.close();
    } catch (Exception je) {}
  }
} 

Instantiated by the contextDestroyed method, the JAXRRemover class contains the createOrgKey and executeRemove methods. It is almost identical to the sample program JAXRDelete.java, which is described in Removing Data from the Registry.

The createOrgKey utility method takes two arguments: the connection to the registry and the string value extracted from the key file. It obtains the RegistryService object and the BusinessLifeCycleManager object, and then creates a Key object from the string value.

The executeRemove method takes four arguments: a connection, a user name, a password, and the Key object returned by the createOrgKey method. It uses the username and password arguments to establish its security credentials with the registry, just as the executePublish method does.

The method then wraps the Key object in a Collection and uses the BusinessLifeCycleManager object's deleteOrganizations method to delete the organization.

Collection keys = new ArrayList();
keys.add(key);
BulkResponse response = blcm.deleteOrganizations(keys); 

The deleteOrganizations method returns the keys of the organizations it deleted, so the executeRemove method then verifies that the correct operation was performed and displays the key for the deleted organization.

Collection retKeys = response.getCollection();
Iterator keyIter = retKeys.iterator();
javax.xml.registry.infomodel.Key orgKey = null; 
if (keyIter.hasNext()) { 
  orgKey = (javax.xml.registry.infomodel.Key) keyIter.next();
  id = orgKey.getId();
  logger.info("Organization key was " + id);
}