Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Java.JDK.5.Edition (Wrox)

.pdf
Скачиваний:
31
Добавлен:
29.02.2016
Размер:
12.07 Mб
Скачать

Chapter 5

«interface»

PointType

+getYCoord() : int +getXCoord() : int

+setYCoord(in value : int) : void +setXCoord(in value : int) : int

<xs:complexType name="pointType"> <xs:sequence>

<xs:element name="x-coord" type="xs:int: />

<xs:element name="y-coord" type="xs:int: /> </xs:sequence>

</xs:complexType>

Figure 5-13

«interface»

ColorType

+getRed() : int +getBlue() : int +getGreen() : int +getAlpha() : int

+setRed(in value : int) : void +setBlue(in value : int) : void +setGreen(in value : int) : void +setAlpha(in value : int) : void

<xs:complexType name="colorType"> <xs:sequence>

<xs:element name="red" type="xs:int: />

<xs:element name="green" type="xs:int: />

<xs:element name="blue" type="xs:int: />

<xs:element name="alpha" type="xs:int" default="255" /> </xs:sequence>

</xs:complexType>

Since XML documents are hierarchical in nature, the structure the generated JAXB classes naturally take on is hierarchical. JAXB serializes and deserializes root elements in an XML document. It is the beginning of an object graph. The XML complex type pointType, for instance, has subelements x-coord and y-coord; they are therefore properties of pointType. In the generated JAXB class, these coordinates become properties of the PointType interface. Figure 5-14 shows the generated JAXB object graph from the root element of your configuration data model, configuration. The Configuration interface not only extends javax.xml.bind.Element because it is a root element, but since it is an instance of XML complex type configurationType, it also extends the org.book.configuration.ConfigurationType interface. This interface has the properties as shown in the diagram. The object graph then extends out from there.

The complete class diagram of all of the JAXB generated interfaces from your XML schema are pictured in Figure 5-15. Notice how only the Configuration interface extends javax.xml.bind.Element. This means that it is the only element that can be serialized or deserialized by JAXB. The rest are all subtypes and elements branching off from Configuration. By looking at the object graph above and the class diagram below, you can match up the XML element and its place in the object graph with the JAXB interface that implements it.

266

Persisting Your Application Using Files

<recent-files>

<show-tabs>

<user-settings>

<user-home-directory>

<foreground-color>

<background-color>

<configuration>

<ui-settings>

<palette-window-position>

<tools-window-position>

Figure 5-14

267

«interface» RecentFilesType

+getRecentFile():List

 

«interface» ColorType

+getRed():int +getBlue():int +getGreen():int +getAlpha():int +setRed(invalue:int):void +setBlue(invalue:int):void +setGreen(invalue:int):void +setAlpha(invalue:int):void

 

«interface» PointType

+getYCoord():int +getXCoord():int +setYCoord(invalue:int):void +setXCoord(invalue:int):int

 

 

 

 

 

 

 

 

 

+getBackgroundColor():ColorType +getForegroundColor():ColorType +getPaletteWindowPosition():PointType +getToolsWindowPosition():PointType +isShowTabs():boolean +setBackgroundColor(invalue:ColorType):void +setForegroundColor(invalue:ColorType):void +setPaletteWindowPositioni(invalue:PointType):void +setToolsWindowPosiition(invalue:PointType):void +setShowTabs(invalue:boolean):void

 

 

«interface»

UserSettings

+getRecentFiles():RecentFilesType +getUserHomeDirectory():String +setRecentFiles(invalue:RecentFilesType):void +setUserHomeDirectory(invalue:String):void

 

 

 

«interface»

UiSettings

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

«interface» ConfigurationType

getUiSettings():UiSettings

getUserSettings():UserSettings

setUiSettings(invalue:UiSettings):void

setUserSettings(invalue:UserSettings):void

 

 

 

 

 

javax.xml.bind.Element

 

 

 

 

+

+

+

+

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

«interface»

 

 

 

 

 

 

 

 

 

«interface» Configuration

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 5-15

268

Persisting Your Application Using Files

JAXB API Key Classes

The classes that the xjc compiler generates are not the classes that are used by the developer to serialize or unserialize any data, or in JAXB terms, marshall or unmarshall any data. The classes and resources generated by JAXB from an XML schema merely provide the rules and data structure necessary for the JAXB runtime libraries to marshall and unmarshall XML data conforming to that schema. Because the JAXB run time must be made aware of the particular constraints and rules for each individual schema, a JAXBContext object must first be created with the particular context of the particular schema and data structure classes to be used. From there, Marhsallers and Unmarshallers can be created to actually serialize and deserialize XML data.

Class or Interface (From javax.xml.bind)

Function

 

 

JAXBContext

The JAXBContext is the initial class; one creates

 

Marshaller and Unmarshaller classes for various

 

JAXB-generated types

Marshaller

Interface that allows for the marshalling of

 

JAXB-generated objects to XML in various formats

 

(stream, DOM nodes, SAX events, and so on)

Unmarshaller

Interface that allows for the unmarshalling of vari-

 

ous XML representations (from a stream, a DOM

 

tree, or SAX events) to populate instances of JAXB-

 

generated classes

Validator

Interface through which JAXB-generated class

 

instances can be verified that the data they contain

 

conforms to the XML schema they were generated

 

against

 

 

Marshalling and Unmarshalling XML Data

The process of marshalling and unmarshalling data into and from JAXB classes occurs through three classes: JAXBContext, Marshaller, and Unmarshaller. Both Marshaller and Unmarshaller are created from an instance of JAXBContext, and they do the actual work of marshalling and unmarshalling the data, respectively. A different JAXBContext is required for every different XML namespace from XML schemas, or more specifically, the Java package that contains the generated JAXB classes. This allows the JAXBContext to set up the Marshaller and Unmarshaller objects with that particular schema’s rules and constraints. There are three steps to unmarshalling XML instance data conforming to

aschema into a JAXB-generated object graph:

1.Retrieve an instance of JAXBContext specific to the root package of the generated JAXB classes.

2.Create an Unmarshaller object from the JAXBContext instance.

3.Use the Unmarshaller to unmarshall XML data into instances of the generated JAXB classes.

You will now unmarshall XML data conforming to your configuration.xsd schema into your generated JAXB classes. First, the JAXBContext is retrieved:

JAXBContext ctx = JAXBContext.newInstance(“org.book.configuration”);

269

Chapter 5

Then Unmarshaller can then be created from the context:

Unmarshaller u = ctx.createUnmarshaller();

Now that you have an Unmarshaller, various representations of XML data can be passed to it to transform the XML into instances of the JAXB-generated object graph. In this example, you will pass it a FileInputStream corresponding to an XML file saved on disk that conforms to your schema:

org.book.configuration.Configuration conf = (org.book.configuration.Configuration) u.unmarshal (new

FileInputStream(“c:\\mark\\configuration.xml”));

The Unmarshaller returns a populated instance of Configuration, which represents the root node of the XML file, and is the root of your object graph. The XML data can now be used as necessary in your application. Marshalling data back into XML is just as straightforward as unmarshalling. The three steps to marshall data mirror the three steps to unmarshall it:

1.Retrieve an instance of JAXBContext specific to the root package of the generated JAXB classes.

2.Create a Marshaller object from the JAXBContext instance.

3.Use the Marshaller to marshall XML data into instances of the generated JAXB classes.

Now instances of org.book.configuration.Configuration can be marshalled back to disk (or to DOM or SAX representations). Just like before, the JAXBContext particular for your package of JAXBgenerated classes must be obtained:

JAXBContext ctx = JAXBContext.newInstance(“org.book.configuration”);

The Marshaller can then be created from the context:

Marshaller m = ctx.createMarshaller();

The Marshaller instance can now be used to serialize the information in your conf instance of org.book.configuration.Configuration to a FileOutputStream (and hence a file on the file system):

m.marshal (conf, new FileOutputStream(“c:\\mark\\configuration.xml”);

That’s all there is to marshalling and unmarshalling data. As you can see, the difficult part of using JAXB is writing the schema.

Note: If the org.book.configuration.Configuration type is not populated with all the data the schema requires, the instance will not be able to be marshalled into XML. By the same token, XML documents containing errors — in other words not exactly conforming to the schema — will not be able to be unmarshalled. Exceptions will be thrown and the instance document will have to be fixed.

Creating New XML Content with JAXB-Generated Classes

You have looked at how to load XML data into a JAXB object graph. You have looked into saving an existing JAXB object graph back into XML. How would you create a new JAXB graph and populate it

270

Persisting Your Application Using Files

programmatically (to later save to XML)? Unfortunately, this is one area where JAXB becomes a little unwieldy. In JAXB, every set of generated classes comes with an ObjectFactory class at the

root package of the generated classes. You may have noticed the class org.book.configuration

.ObjectFactory back when you generated your set of classes for your configuration.xsd schema. This is the class necessary to create blank new instances of every JAXB object. Since every JAXB representation of either an element or complex type definition corresponds to a Java interface, the ObjectFactory finds the right implementation class (from the generated package’s subpackage, impl) and creates it. In any given JAXB object graph, there are potentially many element and complex type definitions turned into interfaces, and each of these must be created with the ObjectFactory. Once these types are created, though, it is easy to populate them with data, since they all follow Java Bean conventions. The example below shows the creation and population of an org.book.configuration

.Configuration instance:

ObjectFactory factory = new ObjectFactory();

ConfigurationType configType = factory.createConfiguration(); UiSettingsType uiSettingsType = factory.createUiSettingsType(); UserSettingsType userSettingsType = factory.createUserSettingsType();

configType.setUiSettings(uiSettingsType);

configType.setUserSettings(userSettingsType); ColorType fgColorType = factory.createColorType(); fgColorType.setRed(255);

fgColorType.setBlue(255);

fgColorType.setGreen(0);

uiSettingsType.setForegroundColor(fgColorType);

uiSettingsType.setShowTabs(true);

userSettingsType.setUserHomeDirectory(“c:\\mark”);

... // continue on as such, populating the entire object graph

One thing to take into consideration when manually populating JAXB object graphs is completeness and conformance to the schema. While it is easy to populate your JAXB objects and use the data in a Java application, if you want to save the data you are populating out to disk (or somewhere else) as XML, every schema-required piece of data must exist in your newly created object graph. In the example above, if you did not create a UserSettingsType instance and set it on your Configuration instance, JAXB exceptions would be thrown when the instance was later marshalled to disk.

Using JAXB-Generated Classes in Your Application

One of the potential issues that arise whenever information is saved and loaded from a file is that the information must be turned into objects used by the application. The nice thing about the Java Serialization API and XMLEncoder/Decoder is that they save the actual Java class instances used by an application,

so there is no need to transform the data loaded into a format used internally by the application — it is already in the format used by the application. The classes that JAXB generates can be used as the

in-memory data model for your application, but generally, there is a need to perform at least some transformations. The Java classes in the JDK are rich and full of functionality — and it would be wasteful to ignore them. Why store URLs as Strings? Why store File objects as Strings? Why not represent a

271

Chapter 5

color with a java.awt.Color object? Because it makes sense to use the classes in the JDK, a lot of the time you will find yourself taking data from the Java Beans generated by JAXB, and putting them into your own data structures. You will find yourself adding JAXB classes to your own lists, maps, trees, and other data structures, especially since java.util.List is the only collection class ever used by JAXB-generated classes. This is the added burden of using JAXB over using Java Serialization or XMLEncoder/Decoder. Not only do you have to create a schema, but also it is often a necessity to transform some of the data from the JAXB classes into classes more usable by your application. In the example configuration data model used throughout this chapter for the Imager Application, you use an instance of book.Configuration to represent the model. It contains Java representations of points and colors that could be used by the AWT and Swing UI frameworks. To use your JAXB-generated configuration data model in your application, you will as such have to transform it to and from your book

.Configuration data model. It is not a difficult task, but must be done for things like your color and point representations to have any meaning to your application. The diagram in Figure 5-16 that follows shows where your transformations fit into the bigger picture of your application.

JAXB-Generated Data Model

 

XML Document

Our Transformer

conforming to

configuration.xsd

 

book.Configuration Data Model

Figure 5-16

In your original Configuration data model example, you wrapped your serialization code into Swing actions to use in the UI for the Imager Application. This let you easily add your code to save and load configuration data to your menus and buttons in your application. You will do the same for your code to save and load your configuration data, this time with your XML format based on your configuration

.xsd schema file. The key difference, though, will be that you need to integrate transformation functionality into these actions, since a conversion needs to be done between your JAXB-generated data model and your original Configuration data model (as shown in Figure 5-16). Other than this transformation, your new XML save and load Swing actions will be very similar in structure and nature to your older actions.

272

Persisting Your Application Using Files

Implementing Your Save Action

As shown in the code that follows, your save action’s actionPerformed() method will start out the same way as your original save action — by prompting the user for a file in which to save the configuration information:

package book;

...

import org.book.configuration.ColorType;

import org.book.configuration.ConfigurationType; import org.book.configuration.ObjectFactory; import org.book.configuration.PointType;

import org.book.configuration.RecentFilesType; import org.book.configuration.UiSettingsType; import org.book.configuration.UserSettingsType;

public class SaveXMLConfigurationAction extends AbstractAction {

private Application myApp;

public SaveXMLConfigurationAction(Application app) { super(“Export XML Configuration”);

this.myApp = app;

}

public void actionPerformed(ActionEvent arg0) { JFileChooser fc = new JFileChooser();

if (JFileChooser.APPROVE_OPTION == fc.showSaveDialog(myApp)) { try {

If the user chooses a file to save the configuration to, the application’s book.Configuration object is retrieved, and the process of mapping its data to a new JAXB org.book.configuration

.Configuration object is begun. The first step to completing this mapping is to create the ObjectFactory. After the factory is created, all of the types necessary, starting with ConfigurationType, can be created. Notice in the code that follows how values are then retrieved from the application’s book.Configuration data model, and then mapped into their appropriate place in the JAXB-generated

ConfigurationType data model:

Configuration conf = this.myApp.getConfiguration();

JAXBContext ctx = JAXBContext.newInstance(“org.book.configuration”);

Marshaller m = ctx.createMarshaller();

ObjectFactory factory = new ObjectFactory();

ConfigurationType configType = factory.createConfiguration(); UiSettingsType uiSettingsType = factory.createUiSettingsType(); UserSettingsType userSettingsType = factory.createUserSettingsType();

configType.setUiSettings(uiSettingsType);

configType.setUserSettings(userSettingsType);

273

Chapter 5

Color fgColor = conf.getForegroundColor(); if (fgColor != null) {

ColorType fgColorType = factory.createColorType(); fgColorType.setRed(fgColor.getRed()); fgColorType.setBlue(fgColor.getBlue()); fgColorType.setGreen(fgColor.getGreen()); fgColorType.setAlpha(fgColor.getAlpha());

uiSettingsType.setForegroundColor(fgColorType);

}

Color bgColor = conf.getBackgroundColor(); if (bgColor != null) {

ColorType bgColorType = factory.createColorType(); bgColorType.setRed(bgColor.getRed()); bgColorType.setBlue(bgColor.getBlue()); bgColorType.setGreen(bgColor.getGreen()); bgColorType.setAlpha(bgColor.getAlpha());

uiSettingsType.setBackgroundColor(bgColorType);

}

Point ppPoint = conf.getPaletteWindowPosition(); if (ppPoint != null) {

PointType ppPointType = factory.createPointType(); ppPointType.setXCoord(ppPoint.x); ppPointType.setYCoord(ppPoint.y);

uiSettingsType.setPaletteWindowPosition(ppPointType);

}

Point tpPoint = conf.getToolsWindowPosition(); if (ppPoint != null) {

PointType tpPointType = factory.createPointType(); tpPointType.setXCoord(tpPoint.x); tpPointType.setYCoord(tpPoint.y);

uiSettingsType.setToolsWindowPosition(tpPointType);

}

uiSettingsType.setShowTabs(conf.isShowTabs());

userSettingsType.setUserHomeDirectory(conf.getUserHomeDirectory()); String[] recentFiles = conf.getRecentFiles();

if (recentFiles != null) {

RecentFilesType rFilesType = factory.createRecentFilesType();

Collections.addAll(rFilesType.getRecentFile(), recentFiles);

userSettingsType.setRecentFiles(rFilesType);

}

274

Persisting Your Application Using Files

Finally, after you finish mapping the data, the JAXB data model is marshalled to XML in the file specified by the user:

m.marshal (configType, new FileOutputStream(fc.getSelectedFile()));

}catch (IOException ioe) {

JOptionPane.showMessageDialog(this.myApp, ioe.getMessage(), “Error”,

JOptionPane.ERROR_MESSAGE);

ioe.printStackTrace();

}catch (JAXBException jaxbEx) { JOptionPane.showMessageDialog(this.myApp, jaxbEx.getMessage(), “Error”,

JOptionPane.ERROR_MESSAGE);

jaxbEx.printStackTrace();

}

}

}

}

Note how you must catch JAXBException in the above code. Most JAXB operations can throw a JAXBException — when saving it can mean that you did not populate all the information that was required in your generated object structure as specified in the originating XML schema.

Implementing Your Load Action

The load action is of course similar to your original load action — and probably most actions that load files, actually. As shown in the code that follows, the user is prompted for a file from which to load the configuration at the beginning of the actionPerformed() method:

package book;

...

import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller;

import org.book.configuration.ColorType;

import org.book.configuration.ConfigurationType;

import org.book.configuration.PointType; import org.book.configuration.RecentFilesType;

public class LoadXMLConfigurationAction extends AbstractAction {

private Application myApp;

public LoadXMLConfigurationAction(Application app) { super(“Import XML Configuration”);

this.myApp = app;

275

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]