} public static Dispatcher newDispatcher {...} // inner class private static class ACTPredicate implements PredicatedLists.Predicate {...} } In the next section you'll begin an examp
Trang 1XLST transformations using JDOM
So far you've used JAXP to transform rich_iii.xml using program.xsl You could also have written your Javaprogram using JDOM instead of JAXP Just like the JAXP program Transform1, Transform2 is an adaptation
of CueMyLine5 In Chapter 13, you created this program to make changes to rich_iii.xml by using JDOM andthen saved the changes You may need to refer back to Chapter 13 to review setting up your computer to runJDOM applications Here's a partial listing of the JDOM version of CueMyLine5.java:
package cue;
// imports
Trang 2public class CueMyLine5 {
Document document;
public CueMyLine5() {
try{
SAXBuilder builder = new SAXBuilder();
document = builder.build(new File("rich_iii.xml"));
public static void main(String[] args) {
CueMyLine5 cueMyLine = new CueMyLine5();
cueMyLine.addPrologue();
cueMyLine.saveTheDocument();
}
}
As with Transform1, other than renaming the class and methods, you need to make surprisingly few
alterations You will create Transform2.java in the change directory You will parse rich_iii.xml and thentransform it using the XSLT style sheet program.xsl Finally, you will output the transformed file as
JDOMalteredRichard.html The differences between Transform2 and CueMyLine5 are shown in boldface inthe following code:
Trang 3document = builder.build(new File("rich_iii.xml"));
JDOMResult out = new JDOMResult();
transformer.transform(new JDOMSource(document), out);
XMLOutputter xmlOutputter = new XMLOutputter(" ", true);
public static void main(String[] args) {
Transform2 transform2 = new Transform2();
In this section, you are concerned with transforming XML documents so that they can be read and understood.This time, however, you aren't concerned with how they look but in how the data is structured Although this
type of transformation is usually applied to data−centric XML documents, you can continue with the Richard
III example by converting a document that conforms to the existing play.dtd to a document that conforms to a
new DTD that you'll define
Trang 4A second DTD for Shakespeare's plays
You've no doubt noticed by now that play.dtd defines elements but no attributes There continue to be
arguments about what belongs in an attribute and what belongs in an element At one extreme are those whobelieve you should never use attributes At the other are those who put anything they consider to be
non−displayable data in attributes The DTD play.dtd takes the "never use attributes" approach As a
reminder, here's play.dtd:
<!−− DTD for Shakespeare J Bosak 1994.03.01, 1997.01.02 −−>
<!−− Revised for case sensitivity 1997.09.10 −−>
<!−− Revised for XML 1.0 conformity 1998.01.27 (thanks to Eve
Maler) −−>
<!−− <!ENTITY amp "&#38;"> −−>
<!ELEMENT PLAY (TITLE, FM, PERSONAE, SCNDESCR, PLAYSUBT,
INDUCT?,PROLOGUE?, ACT+, EPILOGUE?)>
<!ELEMENT TITLE (#PCDATA)>
<!ELEMENT FM (P+)>
<!ELEMENT P (#PCDATA)>
<!ELEMENT PERSONAE (TITLE, (PERSONA | PGROUP)+)>
<!ELEMENT PGROUP (PERSONA+, GRPDESCR)>
<!ELEMENT PERSONA (#PCDATA)>
<!ELEMENT GRPDESCR (#PCDATA)>
<!ELEMENT SCNDESCR (#PCDATA)>
<!ELEMENT PLAYSUBT (#PCDATA)>
<!ELEMENT INDUCT (TITLE, SUBTITLE*,
<!ELEMENT PROLOGUE (TITLE, SUBTITLE*, (STAGEDIR | SPEECH)+)>
<!ELEMENT EPILOGUE (TITLE, SUBTITLE*, (STAGEDIR | SPEECH)+)>
<!ELEMENT SPEECH (SPEAKER+, (LINE | STAGEDIR | SUBHEAD)+)>
<!ELEMENT SPEAKER (#PCDATA)>
<!ELEMENT LINE (#PCDATA | STAGEDIR)*>
<!ELEMENT STAGEDIR (#PCDATA)>
<!ELEMENT SUBTITLE (#PCDATA)>
<!ELEMENT SUBHEAD (#PCDATA)>
Now it's time to create a second DTD for specifying one of Shakespeare's plays This exercise is not intended
to suggest that this DTD needs improvements; the point of this section is to arrive at a different DTD In thenext section, you'll construct an XSLT style sheet to convert rich_iii.xml into an XML document that
conforms to this new DTD
The structure of a <SCENE>, <EPILOGUE>, and <PROLOGUE> are similar enough that you can treat themall as if they were the same thing The description of <ACT> in play.dtd restricts each act to having one or noprologue, followed by at least one scene, followed by one or no epilogue Although this new DTD can'tenforce this existing structure, the new DTD will only be used for Shakespeare's plays, none of which violatethis structure Although it would be a bit more problematic, you can eliminate <INDUCT> and treat it as atype of <SCENE> Doing this will require you to revise the new DTD for plays with introductions that consist
of multiple scenes For your purposes in this Richard III example, you can get away with the
oversimplification of the new DTD A tradeoff is that the specification of <PLAY> will be a little less clear as
<scene> can refer to more than one type of element The first optional <scene> is the introduction, the second
is the prologue, and the third is the epilogue
Trang 5In the revised DTD, you can take advantage of a decision not to display the front matter or the list of
characters in the play Elements such as <TITLE>, <SUBTITLE>, and <SPEAKER> are now treated asattributes Here's the new DTD, which you can save as newPlay.dtd (to avoid confusion later as to which DTD
is being discussed, this one uses lower case for all elements and attributes):
<!−− DTD example derived from the revised version of J Bosak's
DTD for Shakespeare −−>
<!ELEMENT play (scndescr, scene?,scene?, act+, scene?)>
<!ELEMENT p (#PCDATA)>
<!ELEMENT scndescr (#PCDATA)>
<!ELEMENT act (scene+)>
<!ELEMENT scene ((speech | stagedir | subhead)+)>
<!ELEMENT speech ((line | stagedir | subhead)+)>
<!ELEMENT line (#PCDATA | stagedir)*>
<!ELEMENT stagedir (#PCDATA)>
<!ELEMENT subhead (#PCDATA)>
<!ATTLIST play title CDATA #REQUIRED
subtitle CDATA #IMPLIED >
<!ATTLIST act title CDATA #REQUIRED
subtitle CDATA #IMPLIED >
<!ATTLIST scene title CDATA #REQUIRED
subtitle CDATA #IMPLIED >
<!ATTLIST speech speaker CDATA #REQUIRED >
Translating with a style sheet
You can think of the two DTDs as defining different dialects Your next job is to provide the translation
When Midwesterners refer to a carbonated beverage, they call it a pop When they offer one to a New Yorker, they have to ask if the New Yorker would like a soda if they wish to be understood You could use an XSLT
style sheet to convert a <POP> element to a <SODA> element Compare the two DTDs and look for ways inwhich you might map the elements in play.dtd to the elements and attributes in newPlay.dtd
Creating elements
Start by considering the simplest sort of map A <LINE> as it is defined by play.dtd is exactly mapped to a
<line> as it is defined by newPlay.dtd Whenever the translator encounters a <LINE> element, you want it tocreate a <line> element and put the contents of <LINE> into the newly created <line> Here's how you
<xsl:template match="LINE">
<xsl:element name="line">
</xsl:element>
</xsl:template>
Trang 6Because you haven't placed any content between the start and end <xsl:element> tags, the translator is smartenough to replace these with the empty tag <line />.
Creating attributes
In play.dtd the element <SPEECH> had <SPEAKER> as a child element In newPlay.dtd the element
<speech> has an attribute named speaker In addition, <SPEECH> has <LINE>, <STAGEDIR>, and
<SUBHEAD> elements that need to be mapped across to the corresponding children of <speech> You can dothis mapping with the following code:
Leaving elements out
Not all of the elements in play.dtd are being mapped over For example, in the <PLAY> element, you won't
be keeping the front matter or the dramatis personae You could specify this in either of two ways The first
way is by explicitly listing the children of <PLAY> that you will keep in the target XML document, as
Trang 7Setting the document type declaration
When you were transforming rich_iii.xml into an HTML document, your XSLT style sheet included thefollowing element:
<xsl:output method="html" />
Now you are transforming one XML document into another one, so the value of method is now xml Set thedoctype−system attribute to newPlay.dtd to point to your new DTD Here's your new <output> element:
<xsl:output method="xml" doctype−system="newPlay.dtd" />
The XSLT translator will turn this input into the following output in alteredRichard.xml:
<!DOCTYPE play SYSTEM "newPlay.dtd">
You can also set the doctype−public attribute to include a PUBLIC declaration in your document typedeclaration
The complete style sheet
Now that you know how to construct the pieces of the style sheet, you can put them all together into a filecalled translate.xsl, as shown in Listing 14−1
Listing 14−1: The translate.xsl style sheet
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" doctype−system="newPlay.dtd" />
Trang 9Transform1.java Figure 14−7 shows part of alteredRichard.xml viewed in IE 5.5.
Figure 14−7: The results of applying translate.xsl
Trang 10Binding with JAXB
Thus far in this chapter, you've looked at two types of transformations You transformed elements of an XMLdocument into HTML so that you could easily display the resulting document in a browser You also
transformed one XML document into another so an organization using a different DTD would be able to use
it Now you will look at transforming XML into Java objects so that Java developers can use them moreeasily In this section, you'll create a binding schema and examine the generated files In the next section,you'll use this binding in a sample application
The point of binding is that you are creating a mapping This will enable you to more quickly translate datastored in XML into Java objects, manipulate those objects, and then translate them back to persist them As astarting point, you'll transform the play.dtd that you've been working with to get a feel for what is created byJAXB Once you understand the default behavior of the schema compiler, you'll customize this behavior with
an example that features different datatypes
Installing and running JAXB
Currently JAXB is available as an early−access release You can download the distribution from
http://java.sun.com/xml/jaxb/index.html The release notes describe the installation on a UNIX platform.There is no quick launcher for Windows, but you can easily run JAXB on a Windows box: Either copy thetwo jar files in the lib directory of the distribution into your \jre\lib\ext directory, or change your
CLASSPATH to point to these two jars Now you can run the schema compiler that generates the Java classesfrom the command line
In any case, you will need a binding schema that contains instructions for the schema compiler At the veryminimum this binding schema must specify a root element For example, the Shakespearean plays have a rootelement of <PLAY> Create a binding schema called Shakespeare.xjs that contains the following three lines:
<xml−java−binding−schema version="1.0ea">
<element name="PLAY" type="class" root="true" />
</xml−java−binding−schema>
Save the file in the same directory as play.dtd Shakespeare.xjs has a root element called
<xml−java−binding−schema> with the version specified as early−access 1.0 For now, the only other content
of this file is the element <element> that has the name attribute set to the play.dtd element <PLAY> withinstructions that this element will become a class and that it can be the root element of a document You canspecify that multiple elements can be root elements by using more than one <element> tag
You can now use play.dtd to generate Java classes by using the schema compiler and passing it the name ofthe DTD as well as the name of the binding schema Open a command window and navigate to the directorycontaining play.dtd and Shakespeare.xjs Enter this command:
java com.sun.tools.xjc.Main "play.dtd" "Shakespeare.xjs"
This command runs the schema−compiler application and passes in two parameters The first is for theschema specified, in this case, by play.dtd The second is for the binding schema Shakespeare.xjs After amoment you'll see feedback in the console window to let you know that java files are being generated withthe names ACT.java, EPILOGUE.java, FM.java, INDUCT.java, LINE.java, PERSONAE.java,
PGROUP.java, PLAY.java, PROLOGUE.java, SCENE.java, and SPEECH.java
Trang 11The generated code PLAY.java contains instance variables for each of the contained elements Any of theelements declared to contain #PCDATA are variables of type String in the generated Java code The elements(such as <PERSONAE>) that contain other elements become classes in their own right This means that youend up with a PERSONAE.java file, and that the PLAY class contains a variable named _PERSONAE of typePERSONAE You'll notice that, because play.dtd allows the element <PLAY> to contain one or more of the
<ACT> elements, the schema compiler generates a class named ACT.java and the PLAY class contains a Listnamed _ACT created using a utility inner class called PredicatedLists Listing 14−2 shows a piece of thegenerated source code
Listing 14−2: Play class source code (excerpt)
private PERSONAE _PERSONAE;
private String _SCNDESCR;
private String _PLAYSUBT;
private INDUCT _INDUCT;
private PROLOGUE _PROLOGUE;
private List _ACT = PredicatedLists.createInvalidating(
this, new ACTPredicate(), new ArrayList());
private PredicatedLists.Predicate pred_ACT =
new ACTPredicate();
private EPILOGUE _EPILOGUE;
// accessor methods begin
public String getTITLE() { }
public void setTITLE(String _TITLE) { }
// accessors for FM, PERSONAE, SCNDESCR,PLAYSUBT, INDUCT,
// PROLOGUE, EPILOGUE are same ACT is different it's a List.
//methods for validation
public void validateThis()
throws LocalValidationException { }
public void validate(Validator v)
throws StructureValidationException { }
// marshal methods turn content trees into XML documents
public void marshal(Marshaller m) throws IOException { }
public void unmarshal(Unmarshaller u)
throws UnmarshalException { }
// unmarshal methods turn XML files into Java objects
public static PLAY unmarshal(InputStream in)
Trang 12throws UnmarshalException { }
// customized methods for equals() hashCode() and toString()
public boolean equals(Object ob) { }
public int hashCode() { }
public String toString() { }
public static Dispatcher newDispatcher() { }
// inner class
private static class ACTPredicate
implements PredicatedLists.Predicate { }
}
In the next section you'll begin an example that will give you better control over the generated code
Introducing the user−stories example
One set of artifacts from the extreme programming (XP) methodology is user stories These small descriptions
of how a user will interact with the application are traditionally kept on index cards so that they can easily besorted, added to, and, if necessary, torn in half What follows is not a recommendation to store user storieselectronically, but simply a manageable and easily understood example of specifying the type of the databeing turned into Java objects
In this example a user story will consist of an identifying number along with a name, a description, and anestimate of how long the story might take to complete In XP a user story is written by the customer
Programmers can break the story down into tasks they will need to perform in order to deliver the
functionality described in the story A task will again have some sort of identifying number, name,
description, and estimate A task will also have a programmer assigned to it If you want to track the
programmer's velocity, you may want to include the actual time the programmer spends on the task
The DTD for user stories
Here's UserStories.dtd, a possible DTD for user stories
<!ELEMENT stories (userStory)* >
<!ELEMENT userStory (idNumber, name, description, task*,
estimate) >
<!ELEMENT task (idNumber, name, description, programmer,
estimate, actual?) >
<!ELEMENT idNumber (#PCDATA) >
<!ELEMENT name (#PCDATA) >
<!ELEMENT description (#PCDATA) >
<!ELEMENT estimate (#PCDATA) >
<!ELEMENT programmer (#PCDATA) >
<!ELEMENT actual (#PCDATA) >
<!ATTLIST userStory isCompleted CDATA "false" >
<!ATTLIST task isCompleted CDATA "false"
dateAssigned CDATA #REQUIRED >
A minimal binding schema for user stories
You'll need to start with a minimal binding schema that you can save as UserStories.xjs in the same directory
as your XML document You can build in some flexibility by specifying that the root of a tree can be either
Trang 13the <stories> element or the <userStory> element Here's the starting point for UserStories.xjs:
<xml−java−binding−schema version="1.0ea">
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" />
</xml−java−binding−schema>
Use the schema compiler to generate the Java classes with the following command:
java com.sun.tools.xjc.Main "UserStories.dtd" "UserStories.xjs"
This command causes the source files Stories.java, Task.java, and UserStory.java to be generated
Refining the binding schema
One of the benefits of programming in Java is that it is a strongly typed language The more accurately youcan specify the types of the variables you are using, the more the compiler and runtime can help you InChapter 11 you saw that XML Schemas enable you to more precisely specify the datatypes Unfortunately,JAXB can't yet work with XML Schemas, and so you have to add the information in the binding schema
Assigning an element a primitive type
In the current example, you might prefer that an <idNumber> be a number and not just a String Look at therelevant code generated by default for UserStory.java The variable idNumber is a String:
private String _IdNumber;
public String getIdNumber() {
You can add the following line to the binding schema to specify that <idNumber> should be treated as an int:
<element name="idNumber" type="value" convert="int" />
As a result the generated variable _IdNumber is now of type int and the accessor methods are also changed.Notice in the following code that the changes include the addition of a boolean flag for the existence of_IdNumber An exception is now thrown if getIdNumber() is called when has_IdNumber is false
private int _IdNumber;
private boolean has_IdNumber = false;
public int getIdNumber() {
Trang 14public void setIdNumber(int _IdNumber) {
As expected, the same changes are made to the class Task.java
Assigning an element a non−primitive type
You must perform an extra step in order to assign a non−primitive type to an element You have to specify aname for the type you're converting to, and then use a <conversion> element to specify the actual type Thissounds more confusing than it is To treat the variable _IdNumber as an Integer, you can change
UserStories.xjs to the following:
<xml−java−binding−schema version="1.0ea">
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" />
<element name="idNumber" type="value"
convert="DummyPlaceholder" />
<conversion name="DummyPlaceholder"
type="java.util.Integer" />
</xml−java−binding−schema>
As a general coding practice, you should choose a more descriptive name (Integer perhaps) for your
DummyPlaceholder Run then preceding code through the schema compiler and your _IdNumber will havetype Integer
Working with attributes
When working with elements you can make a single change to the element <idNumber> and change the type
of the generated variable _IdNumber in both Task.java and UserStory.java Now consider the attributeisCompleted It makes more sense to treat isCompleted as a boolean than as a String Unlike with elements,however, you have to make this change twice: once for the isCompleted attribute of the element <task> andonce for the isCompleted attribute of the element <userStory>
The resulting binding schema now looks like this:
<xml−java−binding−schema version="1.0ea">
<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" >
<attribute name="isCompleted" convert="boolean"/>
</element>
<element name="task" type="class" >
<attribute name="isCompleted" convert="boolean" />
</element>
<element name="idNumber" type="value" convert="int" />
</xml−java−binding−schema>
Trang 15In Task.java the following variables and methods are related to the attribute isCompleted:
private boolean _IsCompleted;
private boolean isDefaulted_IsCompleted = true;
private final static boolean DEFAULT_ISCOMPLETED = false;
public boolean defaultedIsCompleted() {
Handling multiple occurrences
A user story may have more than one task associated with it Change UserStories.dtd so that a <userStory>can contain only one <task> by removing the asterisk following task, like this:
<!ELEMENT userStory (idNumber, name, description, task,
estimate) >
Use the schema compiler to once again generate Stories.java, Task.java, and UserStory.java You can see thatthe items in UserStory.java that correspond to the element <task> are the following:
private Task _Task;
public Task getTask() {
Trang 16<!ELEMENT userStory (idNumber, name, description, task*,
private List _Task = PredicatedLists.createInvalidating(this,
new TaskPredicate(), new ArrayList());
private PredicatedLists.Predicate pred_Task = new
TaskPredicate();
private static class TaskPredicate
implements PredicatedLists.Predicate {
public void check(Object ob) {
if (!(ob instanceof Task)) {
throw new InvalidContentObjectException(ob,
public void emptyTask() {
_Task = PredicatedLists.createInvalidating(this, pred_Task,
new ArrayList());
}
A final version of the binding schema
The elements <estimate> and <actual> refer to time If you think in terms of ideal engineering hours, thisshould be good enough for the purposes of this example So convert the types generated by these two
elements to ints The attribute dateAssigned should be a date The final binding schema could look like this:
<xml−java−binding−schema version="1.0ea">
Trang 17<element name="stories" type="class" root="true" />
<element name="userStory" type="class" root="true" >
<attribute name="isCompleted" convert="boolean"/>
</element>
<element name="task" type="class" >
<attribute name="isCompleted" convert="boolean" />
<attribute name="dateAssigned" convert="Date" />
</element>
<element name="idNumber" type="value" convert="int" />
<element name="estimate" type="value" convert="int" />
<element name="actual" type="value" convert="int" />
<conversion name="Date" type="java.util.Date" />
</xml−java−binding−schema>
The file UserStory.java
The files generated by the schema compiler tend to be quite long, and so there's no point in listing all of them
It is, however, instructive to look at one of them UserStory.java, shown in Listing 14−3, includes all thefeatures you've seen already in this chapter and, in addition, contains code for functionality such as validating,marshalling, and unmarshalling, which you'll use in the next section
private boolean _IsCompleted;
private boolean isDefaulted_IsCompleted = true;
private final static boolean DEFAULT_ISCOMPLETED = false;
Trang 18private int _IdNumber;
private boolean has_IdNumber = false;
private String _Name;
private String _Description;
private List _Task = PredicatedLists.createInvalidating(
this, new TaskPredicate(), new ArrayList());
private PredicatedLists.Predicate pred_Task = new
TaskPredicate();
private int _Estimate;
private boolean has_Estimate = false;
public boolean defaultedIsCompleted() {
Trang 23if (!(ob instanceof UserStory)) {
Trang 24public String toString() {
StringBuffer sb = new StringBuffer("<<userStory");
Trang 25}
private static class TaskPredicate
implements PredicatedLists.Predicate
{
public void check(Object ob) {
if (!(ob instanceof Task)) {
throw new InvalidContentObjectException(ob, (Task.class));
}
}
}
}
Using the JAXB Bindings
You can think of DTDs and Java classes as having the following correspondence Java classes are used toproduce objects; you can think of a class as a template for an object In the same way, a valid XML documentcorresponds to a schema (in this case a DTD) In the preceding section, you created Java classes from a DTD
so that you now have a mapping of what you can think of as templates In this section, you'll work with theinstances of the DTD and these classes You will take an XML file and convert it to a Java object or createfrom scratch a Java object that could have come from a valid XML file Conversely, you will save the Javaobject as a valid XML file You'll look at both procedures in this section
Unmarshalling: Java objects from XML documents
In this section, you'll begin to look at the process of serializing and deserializing In this context, you willrefer to this process as marshalling and unmarshalling Start with this valid XML document called
SampleStories.xml, which you can use to create Java objects:
<?xml version="1.0" encoding="UTF−8"?>
<!DOCTYPE userStory SYSTEM "UserStories.dtd">
<userStory isCompleted="false">
<idNumber> 7 </idNumber>
<name> Make Lunch </name>
<description> Prepare something to eat </description>
<estimate> 1 </estimate>
</userStory>
You can convert this document to a Java object by calling the unmarshal() method with the signature shown inthis snippet:
public static UserStory unmarshal(InputStream in)
This method calls the unmarshal() method in javax.xml.bind.Dispatcher, which in turn builds the content treeand then validates it against the DTD that the schema compiler uses to generate the Java classes
In your custom application, you will use the static unmarshal() method in an instance of UserStory You willpass the method a FileInputStream constructed with the File constructed from your XML document In thefollowing small example, you can see how the content tree is built from the file SampleStories.xml and then
Trang 26saved as aUserStory:
import java.io.File;
import java.io.FileInputStream;
public class UserStoriesApp {
public static UserStory aUserStory = new UserStory();
public static void main(String[] args) throws Exception {
File story = null;
FileInputStream fileInputStream=null;
try {
story = new File("SampleStories.xml");
fileInputStream = new FileInputStream(story);
For kicks, the toString() method prints the resulting UserStory to the console window Here's what you'll see
in the console window:
<<userStory isCompleted=false idNumber=7 name=MakeLunch
description=Prepare something to eat task=[] estimate=1>>
Adding to the content tree
You just created a content tree from a valid XML file; now you'll use Java code to add a Task to the
UserStory You can use this technique to create the entire content tree from scratch Here the addNewTask()method adds all the required elements and attributes The changes are in boldface in the following code:
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import java.util.Date;
public class UserStoriesApp2 {
public static UserStory aUserStory = new UserStory();
public static void main(String[] args) throws Exception {
UserStoriesApp2 ua2 = new UserStoriesApp2();
File story = null;
FileInputStream fileInputStream=null;
try {
story = new File("SampleStories.xml");
fileInputStream = new FileInputStream(story);
Trang 27System.out.print(aUserStory.toString());
}
private void addNewTask(UserStory taskUserStory){
List myTasks = taskUserStory.getTask();
Task purchaseTask = new Task();
purchaseTask.setIdNumber(1);
purchaseTask.setName("Buy Lunch Items");
purchaseTask.setDescription("Get a jar of peanutbutter and"
<<userStory isCompleted=false idNumber=7 name=Make Lunch
description=Prepare something to eat task=[<<task
isCompleted=false dateAssigned=Sat Sep 22 17:31:31 EDT 2001
idNumber=1 name=Buy Lunch Items description=Get a jar of
peanutbutter and a loaf of bread programmer=Justin
estimate=2>>] estimate=1>>
Notice that although you didn't set isCompleted in the Java code it has the correct default in the generatedcontent tree This behavior is taken care of for you because you provided a default value in the DTD If youwere to leave out the <estimate> element for the <task> element, none would be furnished for you To makecertain that you haven't left anything out, your next step should be to validate the object
Validating your objects
When you are moving from XML to Java code you know that a valid XML document will correctly map tothe objects for the classes that JAXB has generated from the DTD When you start with a Java object,however, you can't be sure of this Any time you have created your own content tree or modified an existingcontent tree, you should validate before you save the object back to XML
In this example you merely need to add a call to the object's validate() method to be sure that the
corresponding XML document will be valid against the DTD The new main() should look like this:
public static void main(String[] args) throws Exception {
UserStoriesApp2 ua2 = new UserStoriesApp2();
File story = null;
FileInputStream fileInputStream=null;
try {
story = new File("SampleStories.xml");
fileInputStream = new FileInputStream(story);
Trang 28Marshalling: Java objects to XML documents
To convert the validated content tree to an XML document, you need only call the marshal() method As youwould expect, this process is similar to unmarshalling You'll create a File that you'll write to, and then you'llcreate a FileOutputStream that you'll use to write your content tree to an XML file Listing 14−4 shows thefinal version of UserStoriesApp2.java with the changes in boldface
Listing 14−4: Final version of UserStoriesApp2.java
public class UserStoriesApp2 {
public static UserStory aUserStory = new UserStory();
public static void main(String[] args) throws Exception {
UserStoriesApp2 ua2 = new UserStoriesApp2();
//File story = null;
FileInputStream fileInputStream=null;
FileOutputStream fileOutputStream=null;
try {
// turn the XML file into a content tree
File story = new File("SampleStories.xml");
fileInputStream = new FileInputStream(story);
aUserStory = aUserStory.unmarshal(fileInputStream);
// add to the tree and validate it
ua2.addNewTask(aUserStory);
aUserStory.validate();
// turn the content tree into an XML file
File alteredStories = new File("alteredStories.xml");
fileOutputStream = new FileOutputStream(alteredStories);
private void addNewTask(UserStory taskUserStory){
List myTasks = taskUserStory.getTask();
Task purchaseTask = new Task();
purchaseTask.setIdNumber(1);
Trang 29purchaseTask.setName("Buy Lunch Items");
purchaseTask.setDescription("Get a jar of peanutbutter and
<description>Prepare something to eat.</description>
<task dateAssigned="Sat Sep 22 18:06:33 EDT 2001">
<idNumber>1</idNumber>
<name>Buy Lunch Items</name>
<description>Get a jar of peanutbutter and a loaf of
Extending the classes the schema compiler generates
When you use your favorite RAD tools to quickly generate a GUI for your desktop application, the next step
is to customize it The same is true when you're dealing with JAXB The schema compiler gets you up andrunning quickly, but it can't provide the custom functionality that you're looking for Your next task is toextend functionality by subclassing the Java classes generated by the schema compiler In this example, you'llcalculate the total time estimated for a user story by totaling up the individual estimates for the tasks
The SampleStories2.xml document
To keep the Java code clean, you won't add to the XML content in your application Here's a document calledSampleStories2.xml, which includes a user story that already has several tasks defined:
<?xml version="1.0" encoding="UTF−8"?>
<!DOCTYPE userStory SYSTEM "UserStories.dtd">
<userStory isCompleted="false">
<idNumber> 7 </idNumber>
<name> Have Lunch </name>
<description> Enjoy a mid−day meal </description>
<task dateAssigned="Sat Sep 22 18:06:33 EDT 2001">
<idNumber>1</idNumber>
<name>Buy Lunch Items</name>
Trang 30<description>Get a jar of peanutbutter and a loaf of
The source code for UserStory.java was produced for you by the schema compiler It provides basic
functionality that you can easily extend Create a subclass of UserStory called ChangedUserStory In this classyou will introduce a method called calculateEstimate(), which will calculate the value of the <estimate> childelement of <userStory> by totaling up the values of the <estimate> elements in the <task> children elements
of <userStory> While you're at it you can change getEstimate() so that it recalculates the value of _Estimate.ChangedUserStory.java isn't very big Here it is in its entirety:
import java.util.Iterator;
import java.util.List;
public class ChangedUserStory extends UserStory {
public int getEstimate() {
Trang 31}
}
Using the subclass
What remains is for you to get an instance of ChangedUserStory from the XML file instead of an instance ofUserStory The key to changing your application can be found in the unmarshal() methods of UserStory.Remember that the UserStory.unmarshal() methods end up calling the unmarshal() method in the class
javax.xml.bind.Dispatcher You can override these methods in ChangedUserStory, or you can make thefollowing changes to UserStoriesApp.java
First, get a handle to a Dispatcher object and call its register() method to indicate that you want a call tounmarshal a UserStory to be replaced by a call to unmarshal a ChangedUserStory Second, call the Dispatcherobject's unmarshal() method and cast the returned object to type ChangedUserStory The remainder of thechanges in the file UserStoriesApp3.java are name changes, except for the call to the getEstimate() methodthat you overrode This call will tell you whether the application is actually using the ChangedUserStoryclass
import java.io.File;
import java.io.FileInputStream;
import javax.xml.bind.Dispatcher;
public class UserStoriesApp3 {
public static ChangedUserStory anotherUserStory
= new ChangedUserStory();
public static void main(String[] args) throws Exception {
UserStoriesApp2 ua2 = new UserStoriesApp2();
FileInputStream fileInputStream=null;
try {
File story = new File("SampleStories2.xml");
fileInputStream = new FileInputStream(story);
Trang 32The theme of this chapter is that XML is easily changed Because an XML document is self−describing, it iseasy to change it into another format or into another XML document that conforms to someone else's DTD.You took a closer look at how to move back and forth between XML documents and Java objects representingthe same data You learned the following:
How to use Cascading Style Sheets (CSS) to display XML documents The advantage of this method
is that the presentation is more attractive in browsers that support this feature The disadvantage isthat the document itself isn't changed, and the way in which the document is rendered is very
browser−dependent
•
How to use Extensible Style Sheet Language Transformations (XSLT) to actually change an XMLdocument into other formats In this chapter's example, you changed an XML document into anHTML document
•
How to use XSLT style sheets to transform an XML document conforming to one DTD into an XMLdocument conforming to another DTD This is a particularly useful ability when you're sharing databetween organizations that have developed different ways of thinking about how the data should beorganized If you can come up with a reasonable mapping from one organization to the other, thenthis process can be automated
•
That the Java APIs for XML Binding (JAXB) enable you to take a DTD and transform it into Javaclasses that can be used to represent data stored in an XML document, which can then be validatedagainst that DTD The transformation of data stored in these XML documents to and from Java
objects is called marshalling and unmarshalling, respectively This transformation simplifies the task
of working with data persisted in XML documents
•
Trang 33Chapter List
Chapter 15: Exploring the RMI Mechanism
Chapter 16: Introducing Enterprise JavaBeans
Chapter 17: Using Advanced EJB Techniques
Chapter 18: Introducing CORBA
Chapter 19: CORBA Applications in the Enterprise
Chapter 20: Why Dream of Jini?
Trang 34If you have Java on the client and the server, you can use Remote Method Invocation (RMI) to communicatebetween them The client will have a piece of stub code that acts as a proxy for particular classes on theserver The client interacts with this code as if it were the remote object itself By this we mean that you usethe same methods, not that there aren't additional considerations that could affect your performance You arepassing objects back and forth across the network: You should consider carefully what it is you are passingand how frequently you are passing it
In this chapter, we'll explore the RMI mechanism by presenting an example of having a remote Java clientinteract with Java classes on the server We'll look at developing a distributed application first in a singledirectory, then in two directories on a single machine, then on two different machines, and finally using theRMI daemon Each iteration of this example will introduce a new issue Initially, nothing is really beingpassed back and forth, as everything is in the same CLASSPATH This approach will enable us to discussspecific features of RMI rather than introducing them all at once
The Components of a Basic RMI Application
We'll begin our examination of RMI with an example that enables you to create each of the components in abasic RMI application In the next section, we will expand upon this example to create an actual distributed,albeit small, application For now, we will concentrate on creating the interface In this case, you will pass in a
String called name, and the Greeter will respond with the String Hello name Of course, you don't need a
distributed application to print a personalized greeting for a user You can accomplish the same task using aservlet or JSP as we mentioned in Chapter 3, "Creating Dynamic Content with Servlets," and Chapter 4,
"Using JavaServer Pages," even in an enterprise setting Because you have gotten so adept at greeting others
by name, this example will enable you to concentrate on the RMI implementation details
After creating the remote interface Greeter, you will then create an implementation of it that will act as yourremote object The convention is to name this class GreeterImpl The final class that you will hand−code forthe server is GreeterMain You'll use this to start up the Greeter service Finally, you will need to write
WelcomeGuest to act as the client requesting the service
Note The logistics of actually running an RMI application are slightly more complicated than those shown inthis section We start with this example to clearly show the roles played by the different components Inthe next section, we will explain how to specify the security, the codebase, and how to run the exampleover a network We have found that many online tutorials only include examples wherein all the files are
on a single machine in the same directory
Place all the files in this example in the following directory, which we will refer to as the Greetings directoryfor short:
C:\J2EEBible\rmi\Greetings
Even with four classes running from a single directory, you will still need to start three processes to run theexample (We will explain how to run this example at the end of this section.)
Trang 35The Remote interface
In order for the client and server to communicate, they have to agree on what messages may be
communicated Frequently, you want to expose the methods that can be invoked without sharing how they areimplemented This is best accomplished in Java by means of an interface In this example, the Greeter
interface will contain the single method greetByName(), which takes a String as an argument and returnsanother String The following is the entire Greeter.java file:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Greeter extends Remote {
String greetByName( String name) throws RemoteException;
}
Of course, you could have combined the two import statements into the single statement import java.rmi.*; ifyou'd wanted Notice that our interface extends the Remote interface, meaning that it is what is known as a
marker interface Remote doesn't contain any methods or constants and is just used to mark the implementing
classes as being of a type that extends Remote Notice also that every method must throw a RemoteException.(You'll see this again in Chapter 20, when we discuss Jini.) Whenever you are using remote objects or
communicating across a network, Java requires you to allow for network failures and problems retrievingwhat you need from the remote JVM
The Greeter interface serves two purposes First, it specifies what must be contained in an implementation ofthe interface This tells you or whoever will be writing the GreeterImpl class what must be included in it.Second, it tells the client what it can expect from an implementation In a distributed system, you would have
a copy of Greeter.class on the server and another copy on each client
Implementing the Remote interface on the server
Your next task is to create the class that will live on the server and provide the implementation of the Greeterservice The highlighted portion in the following code completes the promise made by the Greeter interface.We'll call this GreeterImpl.java and create it with the following code, which you save in the same Greetingsdirectory:
public String greetByName( String name) {
return "Hello "+ name;
}
}
The class GreeterImpl also extends the class java.rmi.server.UnicastRemoteObject This is the easiest way tocreate a remote object in this situation; you could also implement your own remote object or extend the classjava.rmi.server.RemoteServer The point is that UnicastRemoteObject implements the details needed for RMI,