27c27
< Beta 1 Version
---
> 1.0 Version
32c32,34
<
System Management
---
>
Components
>
38a41
>
WSDL Parser and Code Generator Framework
52a56,205
> The first subsection details a number of pluggable components in general.
> More details are provided for other components in the remaining
> subsections.
>
>
/META-INF/services/<componentPackage>.<interfaceName>
.Compiler
interface is already provided for the Jikes
compiler./META-INF/services/org.apache.axis.components.compiler.Compiler
org.apache.axis.components.compiler.Jikes
org.apache.axis.components.compiler.Jikes
> is packaged with AXIS, all that needs to be done is to ensure
> that the service definition file is loadable by a class loader.
> SocketFactory
interface,
> for example your.package.YourSocketFactory
org.apache.axis.components.net.SocketFactory
> your.package.YourSocketFactory
> This can be done by using the JVM commandline
>
> -Dorg.apache.axis.components.net.SocketFactory=your.package.YourSocketFactory
>
>
Component/Package | >Factory | >Interface | >Optional System Property | >Default Implementation | >
---|---|---|---|---|
org.apache.axis.components.compiler |
> CompilerFactory.getCompiler() |
> Compiler |
> axis.Compiler |
> Javac |
>
org.apache.axis.components.image |
> ImageIOFactory.getImageIO() |
> ImageIO |
> axis.ImageIO |
> MerlinIO, JimiIO, JDK13IO |
>
org.apache.axis.components.jms |
> JMSVendorAdapterFactory.getJMSVendorAdapter() |
> JMSVendorAdapter |
>
|
> JNDIVendorAdapter |
>
org.apache.axis.components.net |
> SocketFactoryFactory.getFactory() |
> SocketFactory |
> axis.socketFactory |
> DefaultSocketFactory |
>
org.apache.axis.components.net |
> SocketFactoryFactory.getSecureFactory() |
> SocketFactory |
> axis.socketSecureFactory |
> JSSESocketFactory |
>
org.apache.axis.EngineConfiguration
.
>
> The EngineConfiguration is provided by an implementation of
> the interface org.apache.axis.EngineConfigurationFactory
,
> which currently provides methods that return client and server
> configurations.
220a379,382
>
Our focus will be how to define the implementation class for
> EngineConfigurationFactory
.
>
>
EngineConfigurationFactory factory = EngineConfigurationFactoryFinder(someContext);
> EngineCongfiguration config = factory.getClientEngineConfig();
> AxisClient = new AxisClient(config);
> EngineConfigurationFactoryFinder(someContext)
> and ensuring that the results are handed to AXIS.
> someContext
is key to how the factory finder
> locates the appropariate implementation of
> EngineConfigurationFactory to be used, if any.
226a413,418
> EngineConfigurationFactoryFinder works as follows:
>
>
org.apache.axis.EngineConfigurationFactory
,
> in the following order:
229c421,450
< Life cycleaxis.EngineConfigFactory
.
>
> org.apache.axis.EngineConfigurationFactory
.
> META-INF/services/org.apache.axis.EngineConfigurationFactory
.
> Each line of such a resource identifies the name of a class
> implementing the interface ('#' comments, through end-of-line).
> org.apache.axis.configuration.EngineConfigurationFactoryServlet
> org.apache.axis.configuration.EngineConfigurationFactoryDefault
> public static EngineConfigurationFactory newFactory(Object)
> someContext
as the parameter.
> newFactory()
may return in instance of that factory.
> Otherwise, newFactory()
must return null.
>
234a462
> org.apache.axis.configuration.EngineConfigurationFactoryServlet
> newFactory(obj)
is called.
> If obj instanceof javax.servlet.ServletContext
is true,
> then an instance of this class is returned.
> The default Servlet factory is expected to function as a server
> (as a client it will incorrectly attempt
> to load the WSDD file client-config.wsdd
> from the current working directory!).
>
>
The default Servlet factory will open the Web Application resource
> /WEB-INF/server-config.wsdd
> (The name of this file may be changed using the
> system property axis.ServerConfigFile
):
>
org.apache.axis.server.server-config.wsdd
> as a data stream.
> org.apache.axis.configuration.EngineConfigurationFactoryDefault
> newFactory(obj)
is called.
> If obj
is null
> then an instance of this class is returned.
> A non-null obj
is presumed to
> require a non-default factory.
>
> The default factory will load the WSDD files
> client-config.wsdd
or server-config.wsdd
,
> as appropriate, from the current working directory.
> The names of these files may be changed using the
> system properties axis.ClientConfigFile
> and axis.ServerConfigFile
,
> respectively.
>
>
AXIS makes use of the Java internationalization mechanism - > i.e., a java.util.ResourceBundle backed by a properties file - > and the java.text.MessageFormat class to substitute > parameters into the message text. 264,266c579 <
X
is
> the number of the variable, starting at 0.
>
275,276c598
< For example: myMsg00=My {0} is {1}.
278,279c600,609
<
Translation requires creating an
> alternate version of the property file provided by AXIS
> for a target language.
> The JavaDoc for java.utils.ResourceBundle
> provides details on how to identify different property
> files for different locales.
>
>
For details on using AXIS's internationalization tools,
> see the Developer's Guide.
>
282,283c612
< Example
<
public static java.util.ResourceBundle < getMessageResourceBundle(); <
public static String < getMessage(String key) throws java.util.MissingResourceException; <
public static String < getMessage(String key, String var) throws java.util.MissingResourceException; <
public static String < getMessage(String key, String var1, String var2) throws java.util.MissingResourceException; <
public static String < getMessage(String key, String[] vars) throws java.util.MissingResourceException; <
AXIS programmers can work with the resource bundle directly via a call
< to JavaUtils.getMessageResourceBundle,
< but the getMessage
< methods should be used instead for two reasons:
<
<
java/src/org/apache/axis/i18n/Messages.java
> to your project/package, say
> my/project/package/path/Messages.java
.
> package
declaration in the copied file
> to the correct package name.
> projectName
> to "my.project"
:
> the portion of the package name that is common to your project.
> projectName
must be equal to or be a prefix of the
> copied Messages package name.
> my/project/package/path/resource.properties
.
> Add new message key/value pairs to this file.
> import org.apache.axis.i18n.Messages
> statement to import my.project.package.path.Messages
.
>
If you have a message with variables, use the syntax "{X}"
< where X is
< the number of the variable, starting at 0. For example:
<
You could also call the String array version of getMessage: <
The String array version of getMessage
< is all that is necessary, but the vast majority of messages will have 0,
< 1 or 2 variables, so the other getMessage
< methods are provided as a convenience to avoid the complexity of the String
< array version.
<
Note that the getMessage < methods throw MissingResourceException < if the resource cannot be found. And ParseException if there are < more {X} entries than arguments. These exceptions are RuntimeException's, < so the caller doesn't have to explicitly catch them. <
The resource bundle properties file is org/apache/axis/utils/resources.properties.
<
---
>
Messages
begins looking for a key's value in
> the resources.properties
> resource in it's (Messages) package.
> Entries in the properties file must follow the pattern: <string><2-digit < suffix>. <
Entries should be ordered in the properties file alphabetically by key. <
Entries in the properties file must never be changed. If a code < change requires a message change, don't change the existing message; instead < create a new entry, incrementing the 2-digit suffix. This must be < done for two reasons: 1. You don't know whether the message < is being used elsewhere. 2. So the translator only has to be < aware of, and translate, the new strings. Without this restriction, < every time translators are given the properties file to translate, they < would have to translate all strings all the time. <
Messages
cannot locate
> either the key, or the resource file,
> it walks up the package hierarchy until it finds it.
> The top of the hierarchy, above which it will not search,
> is defined by the projectName
attribute,
> set above.
> parent
attribute of the
> Messages
class copied to your extensions directory.
> Unless changed, the default behavior, meaning what happens when a key > isn't defined in the new properties file, > is to fall back to the AXIS properties file > (org.apache.axis.i18n.resource.properties). 394,411c713,714 <
< if ( operationName == null )
<
< throw new AxisFault( "No operation name specified" );
<
We will have to add an entry into org/apache/axis/utils/resources.properties. < Something like: <
noOperation=No operation < name specified. <
And change the code to read: <
< if ( operationName == null )
<
< throw new AxisFault(JavaUtils.getMessage("noOperation"));
What follows immediately is a description of the framework. If > you would rather dive down into the dirt of examples, > you could learn a good deal just from them. Then you could come back > up here and learn the gory details. >
There are three parts to WSDL2Java: >
NOTE: Needs lots of description here. >
The symbol table is not extensible, but you can add fields to > it by using the Dynamic Variables construct: >
The basic behavior of this class is simple: you instantiate a > Parser, then you run it. >
There are various options on the parser that have accessor methods:
>
Other miscellaneous methods on the parser:
>
An extension of this class would ...
>
> NOTE: continue this sentiment...
>
>
public class WSDL2 {
>
protected WSDL2();
>
protected Parser createParser();
>
protected Parser getParser();
>
protected void addOptions(org.apache.axis.utils.CLOptionDescriptor[]);
>
protected void parseOption(org.apache.axis.utils.CLOption);
>
protected void validateOptions();
>
protected void printUsage();
>
protected void run(String[]);
>
public static void main(String[]);
>
}
>
Like all good command line tools, it has a main method. Unlike
> some command line tools, however, its methods are not static. Static
> methods are not extensible. WSDL2's main method constructs an instance
> of itself and calls methods on that instance rather than calling static
> methods. These methods follow a behavior pattern. The main
> method is very simple:
>
>
The constructor calls createParser to construct a Parser or an extension
> of Parser.
>
run calls: >
If an extension has additional options, then it is expected to call
> addOptions before calling run. So extensions will call, as necessary,
> getParser, addOptions, run. Extensions will override, as necessary,
> createParser, parseOption, validateOptions, printUsage.
>
>
The generator framework consists of 2 files: >
public interface Generator
>
{
>
public void generate() throws java.io.IOException;
>
}
>
>
public interface GeneratorFactory
>
{
>
public void generatorPass(javax.wsdl.Definition,
> SymbolTable);
>
public Generator getGenerator(javax.wsdl.Message,
> SymbolTable);
>
public Generator getGenerator(javax.wsdl.PortType,
> SymbolTable);
>
public Generator getGenerator(javax.wsdl.Binding,
> SymbolTable);
>
public Generator getGenerator(javax.wsdl.Service,
> SymbolTable);
>
public Generator getGenerator(TypeEntry, SymbolTable);
>
public Generator getGenerator(javax.wsdl.Definition,
> SymbolTable);
>
public void setBaseTypeMapping(BaseTypeMapping);
>
public BaseTypeMapping getBaseTypeMapping();
>
}
>
The GeneratorFactory interface defines a set of methods that the parser > uses to get generators. There should be a generator for each of the > WSDL constructs (message, portType, etc - note that these depend on the > WSDL4J classes: javax.xml.Message, javax.xml.PortType, etc); a generator > for schema types; and a generator for the WSDL Definition itself. > This last generator is used to generate anything that doesn't fit into > the previous categories >
In addition to the getGeneratorMethods, the GeneratorFactory defines > a generatorPass method which provides the factory implementation a chance > to walk through the symbol table to do any preprocessing before the actual > generation begins. >
Accessors for the base type mapping are also defined. These are
> used to translate QNames to base types in the given target mapping.
>
NOTE: Need lots more description here... >
public class MyListPortsWriter extends JavaWriter {
>
private Service service;
>
public MyListPortsWriter(
>
> Emitter emitter,
>
> ServiceEntry sEntry,
>
> SymbolTable symbolTable) {
>
super(emitter,
>
> new QName(
>
> sEntry.getQName().getNamespaceURI(),
>
> sEntry.getQName().getLocalPart() + "Lst"),
>
> "", "lst", "Generating service port list file", "service list");
>
this.service = sEntry.getService();
>
}
>
protected void writeFileHeader() throws IOException
> {
>
}
>
protected void writeFileBody() throws IOException
> {
>
Map portMap = service.getPorts();
>
Iterator portIterator
> = portMap.values().iterator();
>
while (portIterator.hasNext())
> {
>
> Port p = (Port) portIterator.next();
>
> pw.println(p.getName());
>
}
>
pw.close();
>
}
>
}
>
>
public class MyWSDL2Java extends WSDL2Java { >
public static void main(String args[]) {
>
MyWSDL2Java myWSDL2Java
> = new MyWSDL2Java();
>
JavaGeneratorFactory
> factory =
>
> (JavaGeneratorFactory) myWSDL2Java.getParser().getFactory();
>
factory.addGenerator(Service.class,
> MyListPortsWriter.class);
>
myWSDL2Java.run(args);
>
}
>
}
Note that we've also overridden the generate method. The parser
> always calls generate, but since this is a server-side artifact, we don't
> want to generate it unless we are generating server-side artifacts (in
> other words, in terms of the command line options, we've specified the
> --serverSide option).
>
public class MyDeployWriter extends JavaWriter {
>
public MyDeployWriter(Emitter emitter, Definition
> definition,
>
> SymbolTable symbolTable) {
>
super(emitter,
>
> new QName(definition.getTargetNamespace(), "deploy"),
>
> "", "useless", "Generating deploy.useless", "deploy");
>
}
>
public void generate() throws IOException {
>
if (emitter.isServerSide())
> {
>
> super.generate();
>
}
>
}
>
protected void writeFileHeader() throws IOException
> {
>
}
>
protected void writeFileBody() throws IOException
> {
>
MyEmitter myEmitter
> = (MyEmitter) emitter;
>
if (myEmitter.getSong()
> == MyEmitter.RUM) {
>
> pw.println("Yo! Ho! Ho! And a bottle of rum.");
>
}
>
else if (myEmitter.getSong()
> == MyEmitter.WORK) {
>
> pw.println("Hi ho! Hi ho! It's off to work we go.");
>
}
>
else {
>
> pw.println("Feelings... Nothing more than feelings...");
>
}
>
pw.close();
>
}
>
}
>
>
public class MyGeneratorFactory extends JavaGeneratorFactory
> {
>
protected void addDefinitionGenerators() {
>
addGenerator(Definition.class,
> JavaDefinitionWriter.class); // WSDL2Java's JavaDefinitionWriter
>
addGenerator(Definition.class,
> MyDeployWriter.class); // our DeployWriter
>
addGenerator(Definition.class,
> JavaUndeployWriter.class); // WSDL2Java's JavaUndeployWriter
>
}
>
}
>
>
Here is our programmatic API. It adds song accessors to Emitter.
> It also, in the constructor, lets the factory know about the emitter and
> the emitter know about the factory.
>
public class MyEmitter extends Emitter {
>
public static final int RUM = 0;
>
public static final int WORK = 1;
>
private int song = -1;
>
public MyEmitter() {
>
MyGeneratorFactory factory
> = new MyGeneratorFactory();
>
setFactory(factory);
>
factory.setEmitter(this);
>
}
>
public int getSong() {
>
return song;
>
}
>
public void setSong(int song) {
>
this.song = song;
>
}
>
}
>
And here is our command line API. It's a bit more complex that > our previous example's main program, but it does 2 extra things: >
public class WSDL2Useless extends WSDL2Java {
>
protected static final int SONG_OPT = 'g';
>
protected static final CLOptionDescriptor[]
> options = new CLOptionDescriptor[]{
>
new CLOptionDescriptor("song",
>
> CLOptionDescriptor.ARGUMENT_REQUIRED,
>
> SONG_OPT,
>
> "Choose a song for deploy.useless: work or rum")
>
};
>
public WSDL2Useless() {
>
addOptions(options);
>
}
>
protected Parser createParser() {
>
return new MyEmitter();
>
}
>
protected void parseOption(CLOption option)
> {
>
if (option.getId() ==
> SONG_OPT) {
>
> String arg = option.getArgument();
>
> if (arg.equals("rum")) {
>
> ((MyEmitter) parser).setSong(MyEmitter.RUM);
>
> }
>
> else if (arg.equals("work")) {
>
> ((MyEmitter) parser).setSong(MyEmitter.WORK);
>
> }
>
}
>
else {
>
> super.parseOption(option);
>
}
>
}
>
public static void main(String args[]) {
>
WSDL2Useless useless
> = new WSDL2Useless();
>
useless.run(args);
>
}
>
}
>
Let's go through this one method at a time. >