The reader may find useful background information in the Architecture Guide.
/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.commons.logging.Log
interface.
In addition, an implementation of the
org.apache.commons.logging.LogFactory
interface
can be provided to meet
specific requirements for connecting to, or instantiating, a logger.
Log
interface defines the following methods for use
in writing log/trace messages to the log:
log.fatal(Object message);
log.fatal(Object message, Throwable t);
log.error(Object message);
log.error(Object message, Throwable t);
log.warn(Object message);
log.warn(Object message, Throwable t);
log.info(Object message);
log.info(Object message, Throwable t);
log.debug(Object message);
log.debug(Object message, Throwable t);
log.trace(Object message);
log.trace(Object message, Throwable t);
log.isFatalEnabled();
log.isErrorEnabled();
log.isWarnEnabled();
log.isInfoEnabled();
log.isDebugEnabled();
log.isTraceEnabled();
Semantics for these methods are such that it is expected that the severity of messages is ordered, from highest to lowest:
org.apache.commons.logging.LogFactory
interface can be overridden,
allowing the JDK 1.3 Service Provider discovery process
to locate and create a LogFactory specific to the needs of the application.
Review the Javadoc for the LogFactoryImpl.java
for details.
LogFactory
provided by JCL
can be configured to instantiate a specific implementation of the
org.apache.commons.logging.Log
interface
by setting the property org.apache.commons.logging.Log
.
This property can be specified as a system property,
or in the commons-logging.properties
file,
which must exist in the CLASSPATH.
org.apache.commons.logging.Log
interface specified by the system property
org.apache.commons.logging.Log
.
If the property is not specified or the class is not available then the JCL
provides access to a default logging toolkit by searching the CLASSPATH
for the following toolkits, in order of preference:
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.
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.
EngineConfigurationFactoryFinder works as follows:
org.apache.axis.EngineConfigurationFactory
,
in the following order:
axis.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.
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.
X
is
the number of the variable, starting at 0.
For example: myMsg00=My {0} is {1}.
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.
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
.
Messages
begins looking for a key's value in
the resources.properties
resource in it's (Messages) package.
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).
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.