|
![]() |
||
|
Composite HR Application DemoIn this demo, we've got a full-blown composite human resources application that uses Xcalia Intermediation Core™'s different service & database mappers. Don't forget to download the source code and build files that accompany this article. OverviewThis demonstration shows how to use the Xcalia Intermediation Core™ (XIC) to dynamically orchestrate two services and a relational database in order to build a composite application. BuildingIn order to run the demo, you must first build it. See the build instructions for more information. ScenarioThe scenario for the demo should be familiar to many in enterprise development. There is a need for an application that provides a unified location that is responsible for maintaining data in a relational database for one part of the application while at the same time utilizing legacy services for other parts. This would be termed a composite application within an SOA. Business classesIn this particular example, we'll be creating an internal composite human resources application for a company that stores some of its data in a relational database, and uses a mock accounting service to maintain banking account information for employees and a mock banking service to make payments to the company's employees. Conceptually, the taxonomy of the application is as follows. Note that these are business concepts, independent of where and how they are stored and retrieved within (or without) the enterprise.
Business object model
Component diagram
EnvironmentIn the example's enterprise environment, there happen to exist three places where these instances will be stored.
SolutionThe presentation layer is a simple, model 1 web application using JSP and servlet technology. Since the focus of the demo is not on how to properly build web front ends, but instead on how XIC can be used to manipulate a cohesive business domain model, very little effort was expended on the architecture of the web application. This is not of serious consequence, as any presentation technology can work against XIC and the domain model created in this demonstration. Web application considerationsFor data operations, the web application will use XIC's JDO interface. A servlet initializes two JDO PersistenceManagerFactory instances, one for the relational database access, and one for service access. This is required because, at the time of this writing, XIC requires one PMF per database-oriented data sources, and one PMF for any and all service-oriented data sources; a future release will support a single, universal PMF for all data sources, both database- or service-oriented. Each PMF is placed in the application context. Additionally, the servlet populates the demo's data. Once the web application has been loaded, each request activates a persistence filter, which retrieves PMs for each of the two PMFs, places the PMs in request attributes, and reinitializes the helper class DomainContext (see below for more information). Person & Employee relational mappingIn the name of keeping the demo simple, XIC at build time generates a schema capable of storing Person and Employee instances. Note that schema generation is not required; XIC is capable of very sophisticated object-relational mappings. Account, Compensation & Payment service mappingAccount and Compensation instances are stored via the mock accounting service, and Payment instances are stored via the mock banking service. The metadata describing this can be found in the files /config/services.xsm and /config/services.xse. The file services.xsm is an XML file that contains two bodies of metadata: the service entity model (SEM) and the service method model (SMM). The SEM describes the entities that are stored and retrieved by the services described in the SMM. It is important to note that the entities described in the SEM are not Java classes themselves; they are abstract entities that are used by the SMM and that are mapped to Java classes.
<servicemodel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<entitymodel>
<!--=======================================-->
<!-- entities stored via AccountingService -->
<!--=======================================-->
<entityclass name="AccountSE">
<field name="bankId" type="String"/>
<field name="number" type="String"/>
<field name="holders" type="Set(String)"/>
</entityclass>
<entityclass name="CompensationSE">
<field name="id" type="Long"/>
<field name="annualSalary" type="Integer"/>
<field name="payPeriodsPerYear" type="Integer"/>
<field name="bonusPercentage" type="Integer"/>
<field name="employeeId" type="Long"/>
</entityclass>
<!--====================================-->
<!-- entities stored via BankingService -->
<!--====================================-->
<entityclass name="PaymentSE">
<field name="id" type="Long"/>
<field name="referenceNumber" type="String"/>
<field name="confirmationNumber" type="String"/>
<field name="postDate" type="java.util.Date"/>
<field name="requestInstant" type="java.util.Date"/>
<field name="amount" type="java.math.BigDecimal"/>
<field name="destination" type="AccountSE"/>
</entityclass>
</entitymodel>
<services>
...
The SMM describes each service, including a logical service name, the service's methods, and the service's behavior in terms of its effects on the SEM. Here is an example of a service method on the mock accounting service that creates an account. <services>
<service name="AccountingService">
<servicemethod>
<prototype>
<name>createAccount</name>
<return>void</return>
<parameters>
<parameter name="pBankId" type="String"/>
<parameter name="pNumber" type="String"/>
</parameters>
</prototype>
<behavior>
AccountSE account = AccountSE.create(); // create
account.bankId = pBankId; // set key field
account.number = pNumber; // set key field
// note nothing is returned from service method
</behavior>
</servicemethod>
The method's signature is described in the <prototype> element, and the net effect of the service method's behavior is described using the Xcalia service method model language, a Java-like syntax that declares what the method does in terms of the entities declared in the SEM. In this case, the service method, if the service were implemented in Java, would have the signature "void createAccount(String pBankId, String pNumber);". It is crucial to realize that the <behavior> element is not executable code; it is metadata describing what the service method is doing when called. The actual implementation of the service method is irrelevant to the composite application and to XIC. In other words, the <behavior> element describes what is happening, not how is it accomplished. In the example above, you can see that the net effect of the AccountingService's createAccount method call, which takes two String parameters pBankId & pNumber, is to create a new Account service entity, and its key fields are set to the values given by the two parameters, and nothing is returned by this particular service method. Here's another example, that is a bit more complex: <servicemethod>
<prototype>
<name>getAccountHolders</name>
<return>Set(String)</return>
<parameters>
<parameter name="pBankId" type="String"/>
<parameter name="pNumber" type="String"/>
</parameters>
</prototype>
<behavior>
// this Map will be used as an object id
Map identifier = new Map;
// key is AccountSE.class.getField("bankId"), value is pBankId
identifier->AccountSE.getField("bankId") = pBankId;
// key is AccountSE.class.getField("number"), value is pNumber
identifier->AccountSE.getField("number") = pNumber;
// retrieve object by object id
AccountSE account = AccountSE.get(identifier);
// return the set of people ids that hold the given account
return account.holders;
</behavior>
</servicemethod>
This method, still on the AccountingService, would have the signature "Set<String> getAccountHolders(String pBankId, String pNumber);" if it were implemented in Java. The method's behavior is to return all ids as Strings of those people (Person instances) that hold the given account. According to the <behavior> element, a Map is constructed with keys that are the Field objects for those fields comprising the primary key of the account entity (of type AccountSE) and those fields' corresponding values. Next, the Map is used as an object id to retrieve the account, whose "holders" member is then returned. The file services.xse is an XML file that describes the actual implementation of each named logical service: <enablers>
<javainvoker service="AccountingService">
<implementationClass>
xcalia.compositeapplicationdemo.serviceadapter.AccountingServiceLocalAdapter
</implementationClass>
</javainvoker>
<javainvoker service="BankingService">
<implementationClass>
xcalia.compositeapplicationdemo.serviceadapter.BankingServiceLocalAdapter
</implementationClass>
</javainvoker>
</enablers>
As you can see, the service named "AccountingService" and the service named "BankingService" in the SMM are both implemented as local Java objects, and the class to instantiate is given in the <implementationClass> element. Other kinds of services supported by XIC include Web Services, EJB session beans, remote Java objects, CICS and/or IMS transactions, JMS destinations, etc. -- really anything that Java can make a call on. DomainContextThe class DomainContext is a helper class in order to make managing object references that cross datasources within the domain model easier to manage and invisible to the user of the domain model. Until XIC supports a universal PMF for all data sources, the developer must manage Java references to objects that are stored in PMFs other that that managing the referencing object. For example, class Employee, stored via the relational PMF, references a Compensation, stored via the service PMF. In order to accomplish this, Employee has a reference of type Compensation; this reference in the relational PMF's metadata is marked as being transient (persistence-modifier="none"). Employee defines another, persistent field of type long that holds the Compensation's id. Employee then implements JDO's LoadCallback and StoreCallback. In jdoPostLoad(), the Employee instance gets the PM managing Compensation instances in this transaction and retrieves and populates the correct Compensation instance: public void jdoPostLoad() {
compensation = (Compensation)
DomainContext.get()
.getPersistenceManagerOf(Compensation.class)
.getObjectById(new CompensationId(compensationId), false);
}
In jdoPreStore(), the Employee ensures that the compensation's id is stored for later retrieval: public void jdoPreStore() {
if (compensation.getId() == Compensation.NO_ID) {
throw new IllegalStateException(
"can't store Employee without a compensation id");
}
// this ensures that a nonzero id is stored
compensationId = compensation.getId();
}
This is similar for all other references within the domain model that span PMFs. Again, note that this is only a temporary workaround, as XIC will offer in a subsequent release a universal PMF that will manage cross-datasource references. Data Access & Dynamic Service OrchestrationIn order to see the power of XIC's dynamically orchestrated data access, consider the following code snippet, adapted from methods initPMFs() & initData() in class InitiatorServlet and method populate() in class DemoPopulator: // NOTE: Error handling code omitted for brevity
// from InitiatorServlet.initPMFs
String pmfClassName = "xcalia.lido.PersistenceManagerFactory";
// set up relational PMF
String hrPropertiesFilename = "hr.properties";
Properties hrPMFProperties = new Properties();
hrPMFProperties.setProperty("javax.jdo.PersistenceManagerFactoryClass", pmfClassName);
hrPMFProperties.setProperty("lido.propertiesFilename", hrPropertiesFilename);
hrPMF = JDOHelper.getPersistenceManagerFactory(hrPMFProperties);
// set up service PMF
String servicePropertiesFilename = "services.properties";
Properties servicePMFProperties = new Properties();
servicePMFProperties.setProperty("javax.jdo.PersistenceManagerFactoryClass", pmfClassName);
servicePMFProperties.setProperty("lido.propertiesFilename", servicePropertiesFilename);
servicePMF = JDOHelper.getPersistenceManagerFactory(servicePMFProperties);
// FUTURE: only one universal PMF will be necessary
// from InitiatorServlet.initData()
// get PMs
PersistenceManager hrPM = hrPMF.getPersistenceManager();
PersistenceManager servicePM = servicePMF.getPersistenceManager();
// begin transactions
hrPM.currentTransaction().begin();
servicePM.currentTransaction().begin();
// reset DomainContext for use by domain objects
DomainContext.get().resetWith(hrPM, servicePM);
// from DemoPopulator.populate()
// now just use your object/data model
Person person = new Person();
person.setFirstName("Paul");
person.setLastName("McCartney");
Calendar birthDateCal = Calendar.getInstance();
birthDateCal.set(1942, Calendar.JUNE, 18, 0, 0, 0);
person.setBirthDate(birthDateCal.getTime());
Employee employee = person.createEmployee(30000);
Calendar hireDateCal = Calendar.getInstance();
hireDateCal.set(1964, Calendar.FEBRUARY, 7, 0, 0, 0); // Beatles first date on first US tour
Date hireDate = hireDateCal.getTime();
employee.setHireDate(hireDate);
Compensation compensation = employee.getCompensation();
compensation.setBonusPercentage(5);
Account account1 = person.createAccount("Checking", "983745983", BANK_OF_AMERICA);
Account account2 = person.createAccount("Savings", "85092385", BANK_OF_AMERICA, false);
Payment payment = new Payment("87785358370", employee, account1, new BigDecimal("100.10"));
// done using object/data model
// persistence-related calls
// FUTURE: only one PM and one call necessary via persistence by reachability
DomainContext.get().getPersistenceManagerOf(Person.class).makePersistent(person);
DomainContext.get().getPersistenceManagerOf(Compensation.class).makePersistent(compensation);
DomainContext.get().getPersistenceManagerOf(Account.class).makePersistent(account1);
DomainContext.get().getPersistenceManagerOf(Account.class).makePersistent(account2);
DomainContext.get().getPersistenceManagerOf(Payment.class).makePersistent(payment);
// from InitiatorServlet.initData() again
// commit the transactions
servicePM.currentTransaction().commit();
hrPM.currentTransaction().commit();
// close PMs
servicePM.close();
hrPM.close();
// close PMFs
servicePMF.close();
hrPMF.close();
ConclusionNow you have a better understanding of how you can benefit from Xcalia's data access software, give it a try on your projects:
|
||
|
|
|||