|
In This Issue
Welcome to the Enterprise Java Technologies Tech Tips for June 24, 2006. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java Platform, Enterprise Edition (Java EE). This issue covers: These tips were developed using an open source reference implementation of Java EE 5 called GlassFish. You can download GlassFish from the GlassFish Community Downloads page. You can view this Tech Tip as simple text. You can download the sample archive for the tip Writing a Handler in JAX-WS. You can download the sample archive for the tip Inheritance and the Java Persistence API. Any use of this code and/or information below is subject to the license terms. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms.
Handlers are interceptors that can be easily plugged into the Java API for XML-Based Web Services (JAX-WS) 2.0 runtime environment to do additional processing of inbound and outbound messages. JAX-WS defines two types of handlers: protocol handlers and logical handlers. Protocol handlers are specific to a protocol such as SOAP. They can access or change any part of the message, including protocol-specific parts such as the message header. Logical handlers are protocol-agnostic. They cannot change any protocol-specific parts of a message. Logical handlers act only on the payload of the message. This tip shows you how to write a SOAP protocol handler and a logical handler for use with JAX-WS.
Handler Basics
SOAP handlers are generally used to process SOAP-specific
information, such as SOAP headers. For example, a SOAP Handler
can process security headers in a message and pass the request
to the endpoint if the message has the required credentials.
Logical handlers are commonly used if the processing does not
need access to SOAP headers, for validation of the payload, and
with Representational State Transfer ("REST") style web
services. In addition, logical handlers can use Java API for XML
Binding (JAXB) for processing the payload. If you have the
Handlers are invoked with a message context, which provides methods that the handler uses to access and modify inbound or outbound messages. The message context also has properties that the handler can process. These properties can be used to communicate additional information or metadata that is not specified in the message. The additional information can be exchanged between a handler and service implementation or between a handler and a web service client. (For more information about message context in JAX-WS, see the article A little bit about Message Context in JAX-WS.)
SOAP handlers extend
Logical handlers extend The following figure illustrates the relationship between the message contexts, the objects they can be used to retrieve, and the parts of a message those objects cover.
Logical handlers can coexist with SOAP handlers in a handler chain. During runtime, the handler chain is reordered so that for an outbound message the logical handlers execute before the SOAP handlers. For an inbound message, the SOAP handlers execute before the logical handlers. The following figure shows how logical and SOAP handlers are invoked during a request and response.
Writing a SOAP Message Handler
Let's look at a simple SOAP handler. A sample package accompanies this tip. Download the sample package and extract its contents. You'll see two source files. One of them, SOAPLoggingHandler, is a SOAP handler that logs calls to a web service. Here's a code snippet from SOAPLoggingHandler:
public class SOAPLoggingHandler implements
SOAPHandler<SOAPMessageContext> {
...
public boolean handleMessage(SOAPMessageContext smc) {
logToSystemOut(smc);
return true;
}
public boolean handleFault(SOAPMessageContext smc) {
logToSystemOut(smc);
return true;
}
public void close(MessageContext messageContext) {
}
...
Like all SOAP handlers,
The logging is done in the handler's
private void logToSystemOut(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
out.println("\nOutbound message:");
} else {
out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
message.writeTo(out);
out.println("");
} catch (Exception e) {
out.println("Exception in handler: " + e);
}
}
Notice that the
Writing a Logical Message Handler
As its name suggests, the
public class LogicalLoggingHandler implements
LogicalHandler<LogicalMessageContext> {
...
public boolean handleMessage
(LogicalMessageContext context) {
return processMessage(context);
}
public boolean handleFault
(LogicalMessageContext context) {
return processMessage(context);
}
public void close(MessageContext context) {
// Clean up Resources
}
Like all logical handlers,
The logging is done in the handler's
private boolean processMessage
(LogicalMessageContext context) {
Boolean outboundProperty = (Boolean)
context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty) {
out.println("\nOutbound message:");
} else {
out.println("\nInbound message:");
}
LogicalMessage lm = context.getMessage();
Source payload = lm.getPayload();
// Process Payload Source
printSource(payload);
// ....
// If the payload is modified,
// Do lm.setPayload(source) to be safe. Without it,
// behavior may vary on the kind of source returned in
// lm.getPayload().
// See LogicalMessage JavaDoc for more details.
// lm.setPayload(modifiedPayload);
return true;
The
Notice the commented out portion of
You can also pass a LogicalMessage lm = context.getMessage(); Object jaxbObject = lm.getPayload(jaxbContext); // Modify JAXB Object lm.setPayload(modifiedJaxbObject,jaxbContext);
Note that there is no connection between the returned object
and the message payload -- to change the payload, you need to
call Handlers are flexible to plug-in and can be a powerful addition to your application. For another JAX-WS handler example, see Handler example using JAXWS 2.0 in Stephen DiMilla's blog.
About the Author
Rama Pulavarthi is a Member of Technical Staff in the Java Web Services group at Sun Microsystems. He currently works on the development of the JAX-WS Reference Implementation. He previously lead the Software Quality Engineering effort for JAX-RPC.
Inheritance, the ability of a subclass to derive state and behavior from its superclass, was not supported previously in Enterprise JavaBeans (EJB) technology. In the EJB 2.1 persistence model, an entity could not derive from another entity. However, the new Java Persistence API, a part of the EJB 3.0 specification (JSR 220), changes that. By supporting inheritance, the Java Persistence API allows you to write more modular, cohesive, and extensible code. It also allows you to take advantage of polymorphism, the ability of an object to take different forms. This Tech Tip presents some of the features of inheritance supported in the Java Persistence API. A sample package accompanies the Tech Tip. It demonstrates some of the features discussed in the tip. The examples in the tip are taken from the source code for the sample (which is included in the package). The sample uses an open source reference implementation of Java EE 5 called GlassFish. You can download GlassFish from the GlassFish Community Downloads page.
A Simple Example of Inheritance
Let's say you made a resolution this year to be more organized.
In particular, you resolved to better organize your financial
accounts. Assume you have the following accounts: checking,
savings, credit card, brokerage, and margin. All of these are
accounts, so you can model them based on a class called
public abstract class Account{
String name;
String actNum;
String created;
Status status; //OPEN/CLOSE
//let's assume we are not that rich yet and stick to float
float balance;
String description;
}
Inheriting from Abstract Entities
Notice that
@Entity
@Inheritance(strategy=InheritanceStrategy.SINGLE_TABLE)
public abstract class Account{
public enum Status { OPEN, CLOSED }
@Id
private String acctNum;
private String name;
private String created;
private Status status; //Open/Close
//let's assume we are not that rich yet and stick to float
private float balance;
private String description;
}
Notice also, the
The strategy element value
There is a way to inherit mapping attributes from
Inheriting from Abstract Non-Entities (Mapped Superclass)
Both the checking and savings accounts are bank accounts, so for
these two kinds of accounts you can define yet another abstract
class,
public abstract class BankAccount extends Account{
String bankName;
}
There's no
@MappedSuperclass
public abstract class BankAccount extends Account{
String bankName;
}
You might ask why use that approach if it doesn't allow you to
use
There are two kinds of concrete bank account entities --
@Entity
public class SavingsAccount extends BankAccount{
float savingsRate;
...
}
@Entity
public class CheckingAccount extends BankAccount{
float maintFee;
...
}
The attribute
Similarly, for
Polymorphic Queries
Because the Java Persistence API supports polymorphic queries,
you can find any concrete instance of the Account = em.find(Account.class, acctNum);
In the statement,
The query looks up a specific account object instance using the
Overriding Mapping in Inherited Classes
In an inherited class you can override the mappings that you
specified in a mapped superclass. For example, you can override
the mapping attribute
@Entity
@AttributeOverride(
name="bankName" column=@Column("name="bank_name"))
public class CheckingAccount extends BankAccount{
boolean isOverDraftAllowed;
}
After the override,
Inheritance Strategy
The Java Persistence API allows for three different inheritance strategies that dictate how subclasses are mapped to database tables. The three strategies are single table per class hierarchy, joined subclass, and single table per class.
Single Table Per Class Hierarchy Strategy
This strategy, which is specified by the strategy value
"
The default name for the discriminator column name is
@Entity
@Inheritance(strategy=InheritanceStrategy.SINGLE_TABLE)
@DiscriminatorColumn(name="DCOL",
discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("BaseAccount");
public abstract class Account{
...
}
@Entity
@DiscriminatorValue("Checking_Account");
public class CheckingAccount extends BankAccount{
...
float maintFee;
}
After these specifications, the discriminator column has the name
"
Here are the column names and their types for the table that
would be generated based on specifications for Name Null? Type ---------------------------------------------- ACCTNUM NOT NULL VARCHAR2(255) DCOL VARCHAR2(31) CREATED VARCHAR2(255) STATUS NUMBER(10) NAME VARCHAR2(255) BALANCE NUMBER(19,4) DESCRIPTION VARCHAR2(255) TRADEFEES NUMBER(19,4) MAXLOANALLOWED NUMBER(19,4) CREDITCARDNUMBER VARCHAR2(255) ISSUINGBANK VARCHAR2(255) EXPIRESON VARCHAR2(255) BANKNAME VARCHAR2(255) SAVINGSRATE NUMBER(19,4) BANK_NAME VARCHAR2(255) ISOVERDRAFTALLOWED NUMBER(1) Table: ACCOUNT
Notice that the table contains all the attributes of the entire
class hierarchy. So for an account of type
Join Subclass Strategy
In the joined subclass strategy, which is specified by the
strategy value "
@Entity
@Inheritance(strategy=InheritanceStrategy.JOINED)
@DiscriminatorColumn(name="DCOL",
discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("BaseAccount");
public abstract class Account{
....
}
the database tables would like the following: Name Null? Type ---------------------------------------------- ACCTNUM NOT NULL VARCHAR2(255) DCOL VARCHAR2(31) CREATED VARCHAR2(255) STATUS NUMBER(10) NAME VARCHAR2(255) BALANCE NUMBER(19,4) DESCRIPTION VARCHAR2(255) Table: Account Name Null? Type ----------------------------------------------- ACCTNUM NOT NULL VARCHAR2(255) BANKNAME VARCHAR2(255) SAVINGSRATE NUMBER(19,4) Table: SAVINGSACCOUNT Name Null? Type ---------------------------------------------- ACCTNUM NOT NULL VARCHAR2(255) BANK_NAME VARCHAR2(255) ISOVERDRAFTALLOWED NUMBER(1) Table: CHECKINGACCOUNT Name Null? Type ---------------------------------------------- ACCTNUM NOT NULL VARCHAR2(255) TRADEFEES NUMBER(19,4) Table: BROKERAGEACCOUNT Name Null? Type ---------------------------------------------- ACCTNUM NOT NULL VARCHAR2(255) MAXLOANALLOWED NUMBER(19,4) Table: MARGINACCOUNT
Even though no primary key was specified in the subclasses of
Single Table Per Class
In single table per class, each class is mapped to a separate table. A persistence provider is not required to support this strategy with this release of the Java Persistence API specification.
Overriding a Join Column
In the joined subclass strategy you saw that the join between the base class and the subclasses happen on the primary keys. The primary key of the subclass has the same type and name as the base class. It is possible to specify a primary key join column in a subclass and override the default. This is demonstrated in the credit card account entity:
@Entity
@PrimaryKeyJoinColumn(name="cca_id")
public class CreditCardAccount extends Account{
String issuingBank;
String creditCardNumber;
String expiresOn;
}
When the persistence provider does the join between the tables
representing the
Overriding Inheritance Strategy
It's possible for an entity to specify a different inheritance
strategy for its subclasses. The
@Entity
@Inheritance(strategy=InheritanceStrategy.JOINED)
public class BrokerageAccount extends Account{
float tradeFees;
}
@Entity
public class MarginAccount extends BrokerageAccount{
float maxLoanAllowed;
}
Because the
Notice that
Polymorphic Association
The Java Persistence API allows polymorphic associations. Every
account has an owner and an account owner typically has multiple
accounts. These accounts do not have to be of the same kind.
Also, you don't want to have a member variable in
@Entity
public class AccountOwner {
String name;
Collection <Account> accounts;
}
A mapped superclass cannot be the target of a persistent
relationship. So if you made
Inheriting From a Non-Entity Class
Typically you inherit an entity from a non-entity to inherit
its behavior. The attributes you inherit from a non-entity are
not persisted. For example, the
public class BaseAccount {
public float computeBalance(float principal, float rate){
return principal * (1.0 + rate/100.0);
}
}
@Entity
public class Account extends BaseAccount{
....
}
Summary
The new Java Persistence API enables you to write more modular, cohesive, and extensible code through inheritance, polymorphic queries, and polymorphic associations. It benefits both developers who are creating an application data model from scratch and developers who want to migrate their current persistence implementation to move to a standardized persistence API.
Running the Sample Code
To install and run the sample code that accompanies this tip:
Try creating a new account and searching for an account. Underlying the functions of the application is the support for inheritance in the Java Persistence API.
About the Author
Rahul Biswas is a member of the Java Performance Engineering group at Sun. He is currently involved in the development of a generic performance benchmark for Java Persistence and the performance improvement of the persistence implementation in GlassFish. | ||||||||||||
|
| ||||||||||||