What is CUBA The HelloWorld Component HelloWorld Stand-alone HelloWorld as EJB 3.0 HelloWorld as EJB 2.1 HelloWorld as an interoperable AXIS WebService HelloWorld as a WebService Component Directory Structure More Details
package hello; import java.rmi.RemoteException; public interface HelloContractI { public String hello(String who) throws RemoteException; } |
package hello; import cuba.AbstractComponent; public class HelloImpl extends AbstractComponent implements HelloContractI { public String hello(String who) { System.out.println(who + " says Hello"); return "Hello " + who; } } |
<?xml version="1.0"
encoding="UTF-8"?> <component-jar> <component> <component-name>hello</component-name> <external-interface>hello.HelloContractI</external-interface> <component-class>hello.HelloImpl</component-class> </component> </component-jar> |
As you can see, the implementation class properly implements the
component interface. Just like EJBs, CUBA components may have lifecycle
methods being invoked by the container. In the example above
a set of useful lifecycle methods are already
implemented by the convenience class AbstractComponent,
which
is part of the framework. The deployment descriptor is very similar to
what
you might know from the EJB standard's ejb-jar.xml. It
actually has the same purpose of specifying meta information that
includes the specification about what is the interface, what is the
implementation class and what is the component's name at least. You can
provide a lot more information, but there are reasonable defaults
applied if you do not. From a first sight you may find it
inconvenient to write so many parts by hand and keep them
consistent. On the other hand there are some good reasons to keep
particular meta information in a separate descriptor - e.g. security
restrictions or aspect-oriented instrumentations. However, according to
the EJB 3 standard, users of a Java 5 environment may specify meta
information in the code rather then in an XML descriptor.
The HelloWorld component above could be annoted as follows to
substitute the descriptor:
package hello; import java.rmi.RemoteException; import cuba.annotation.External; @cuba.annotation.External public interface HelloContractI { public String hello(String who) throws RemoteException; } |
package hello; import cuba.AbstractComponent; import cuba.annotation.Stateless; @cuba.annotation.Stateless public class HelloImpl extends AbstractComponent implements HelloContractI { public String hello(String who) { System.out.println(who + " says Hello"); return "Hello " + who; } } |
java cuba.util.codegen.AdapterGenerator –cw META-INF/component-jar.xml
The result is a file HelloImpl_WiredEx.java providing the adapter code to access the HelloWorld component in a stand-alone application. Of course, this file must be compiled too. In addition, we need a suitable descriptor for the embedded container being generated with a descriptor generator like this:
java cuba.util.ddgen.DDGenerator –cw META-INF/component-jar.xml
This will generate the file wired-jar.xml,
which is
created in sub directory META-INF.
This step is also required
if you are working with annotations because CUBAs mini container always
works with descriptors to make it suitable for both Java 1.4 and Java 5
environments. If you use annotations rather than descriptors, you
simply pass option -n and the component class names to the descriptor
generator, e.g.
java cuba.util.ddgen.DDGenerator –cwn hello.HelloImpl
The output is exactly the same, because code annotations and XML
descriptors
are just two different but absolutely equivalent notation styles for
the same meta information.
Taking a look at the
generated descriptor reveals
nothing new, except that the generated adapter is declared here rather
than the actual implementation class. But all that
doesn't really matter as it is generated code which should be
used as a black box. Having finished the steps described above, you
should now have (among others) the following files at hand. The blue
ones mark generated files:
hello/ HelloContractI.class HelloImpl.class HelloImpl_WiredEx.class META-INF/ component-jar.xml wired-jar.xml |
The original code and all generated classes and adapters are now packed to a single Jfile by simply archiving the directories hello and META-INF:
jar cvf hello.jar hello META-INF
That's all, you now have a complete 'wired' component archive at
hand. Application archives like EAR files in the J2EE world are not
required for CUBA stand-alone applications (actually they are not even
defined). Nevertheless you need an application descriptor which at
least names the component archives to load. This descriptor has the
predefined name wired-application.xml
and must also be
available in a directory META-INF.
In addition to the EJB
standard's application.xml, it also contains component references and
the definition of DataSources.
Of course all these things also have to be defined in a J2EE
environment, but there is no standard for that. The application
descriptor for the HelloWorld example looks like this:
<?xml
version="1.0"
encoding="UTF-8"?> <wired-application> <modules> <wired>hello.jar</wired> </modules> </wired-application> |
The component archive, the application descriptor and the CUBA runtime library cuba.jar make up the container-side part of the application and all of them must be accessible through the classpath somehow. CUBA also provides its own specialized class loader to simplify path definitions, but that's just an optional extension (see chapter Class Loading). Using ordinary class loader mechanisms here has an important advantage for the development phase: If you specify your class folders before the component archives in the classpath, the classes are of course loaded from there rather than from the archives. I.e. rapid editing, compiling and testing without building archives and especially no deployment cycles as long as the component descriptors don't change.
Finally, a client application must be implemented which calls the
HelloWorld component. The property list in the example client below is
not of interest yet for the stand-alone example, but is required when
using the same client in EJB mode.
public
class HelloClient { public HelloClient() throws Exception { ClientContextI context = new WiredClientContext(); HelloContractI hello = (HelloContractI)context.getComponent("hello"); System.out.println(hello.hello("CUBA")); }
public static void
main(String[] args) throws
Exception { |
In a stand-alone application you have to instanciate a WiredClientContext
and perform a lookup of the required component. The result is a
proxy to a component instance.
Calling the client causes both outputs - the one from the client and
the one from the component - to be printed on the caller's console. The
application is running locally in a single JVM and thus is very fast to
develop and
test. But this is more than a test environment. You can also use the
components productively that way without any functional limitations,
e.g. for batch
processing or in a rich client or direct usage in a servlet engine.
Now that you have finished the development of the HelloWorld component, you can also run it as an EJB in both EJB 2.1 and EJB 3 containers. For EJB 3 you just have to run the same generators as above with option -e 3.0 to generate EJB-compliant adapters:
java
cuba.util.codegen.AdapterGenerator –c -e 3.0
META-INF/component-jar.xml
java cuba.util.ddgen.DDGenerator –c -e 3.0
META-INF/component-jar.xml
Looking at the generator output we now find a class HelloImpl_EJB3 which is a
stateless EJB 3 SessionBean delegating
all
method invocations to the actual implementation class. The descriptor
generator actually produced nothing because 3.0 EJBs are
generated completely on a code annotation basis, not regarding if the
CUBA meta data is provided by a descriptor or by annotations. This
allows subsequent detail configuration by EJB deployment descriptors.
After
compilation,
the directories contain the following files:
hello/ HelloContractI.class HelloImpl.class HelloImpl_EJB3.class META-INF/ component-jar.xml |
The files above can be combined with the output from the wired mode
into a single JAR file, resulting in a hybrid
component archive which allows to run the component in both JEE and
J2SE environments. To deploy the component on a JEE application server
you should build an EAR file from the component JAR and the library cuba.jar. Alternatively you can
add the packages cuba, cuba.util.common, and cuba.ejb from the CUBA library
to the component JAR.
public
class HelloClient { public HelloClient() throws Exception { Properties props = new Properties(); props.put(javax.naming.Context.PROVIDER_URL, "jnp://localhost:1099"); props.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); ClientContextI context = new EJB3ClientContext(props); HelloContractI hello = (HelloContractI)context.getComponent("hello"); System.out.println(hello.hello("CUBA")); }
public static void
main(String[] args) throws
Exception { |
Invoking the client causes only the
client's
output to be printed in the caller's console. The component's output -
which
is now running as an EJB - appears in the console of the application
server. The
stand-alone
component turned to a full-fledged EJB just by a few automated
generator
steps. Stresslessly developed, locally debugged and run as an EJB only
when
actually required. The development can be done without an
application
server.
java
cuba.util.codegen.AdapterGenerator –c -e 2.1
META-INF/component-jar.xml
java cuba.util.ddgen.DDGenerator –c -e
2.1 META-INF/component-jar.xml
Having a look at the generator output we now find a
standard-compliant EJB
2.1
deployment descriptor META-INF/ejb-jar.xml.
The code
generator produced a
remote interface, a home interface and a SessionBean which serves as a
delegate, a dependency injector, and a method interception dispatcher.
After
compilation,
the directories contain the following files:
hello/ HelloContractI.class HelloImpl.class HelloImpl_EJB2.class HelloContractI_RemoteI.class HelloContractI_HomeI.class META-INF/ component-jar.xml ejb-jar.xml |
A nasty detail of the EJB 2.1 world is the fact that almost every
applicaton server requires an additional vendor-specific descriptor
which often has to be edited manually. Therefore, the deployment looks
very
different in every application server. The following steps describe the
process using the interactive deployment tool of the Sun Java System
Application Server 8 which is the reference implementation for
the J2EE 1.4
specification:
public
class HelloClient { public HelloClient() throws Exception { Properties props = new Properties(); props.put(javax.naming.Context.PROVIDER_URL, "iiop://localhost:3700"); props.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory"); ClientContextI context = new EJB2ClientContext(props); HelloContractI hello = (HelloContractI)context.getComponent("hello"); System.out.println(hello.hello("CUBA")); }
public static void
main(String[] args) throws
Exception { |
java
cuba.util.codegen.AdapterGenerator –c -a 1.4 META-INF/component-jar.xml
java cuba.util.ddgen.DDGenerator –c -a
1.4 META-INF/component-jar.xml
The invokation of the descriptor generator requires a valid AXIS
installation (version 1.1 or higher) with its libraries being
accessible through the
classpath. The resulting code must be compiled again and added to the
component
archive which should then include at least the following files:
hello/ HelloContractI.class HelloImpl.class HelloImpl_WiredEx.class HelloContractI_WSI.class HelloImpl_WS.class META-INF/ component-jar.xml wired-jar.xml hello-deploy.wsdd hello-undeploy.wsdd hello.wsdl |
The descriptive files do not actually have to be archived, from a
technical point of view. However, they can be seen as integral parts of
a component module for the case that it should be completely
self-contained and suitable for all environments supported by CUBA. As
you can see above, the descriptor generator also produced a suitable
WSDL file for the creation of stubs in any client environment willing
to access the WebService. This is also required for Java clients,
because in interoperable mode CUBA doesn't provide a client library for
WebService
access. By default, CUBA produces a WSDL in rpc/encoded style
which can be changed to rpc/literal using option -s of the descriptor
generator.
However, rpc/encoded is the only style which is garuanteed to work in
all AXIS versions
-
rpc/literal is recommended but should only be used with AXIS version
1.2 or higher.
Taking a closer look at the generator output, it turns out that also
wired-adapters have been generated. This has a simple reason: the
WebService standard currently specifies WSDL and SOAP only, which is
nothing more but a description format and a communication protocol like
CORBA's IDL and IIOP. Absolutely nothing is mentioned about a component
model or things like garuanteed container services. I.e. there is
nothing more in a WebService environment than in an ordinary J2SE
environment and therefore CUBA simply uses the
same embedded container. In fact, the WebService adapter is just an
additional
layer built on top which performs the same as the Java client above:
instantiation of a WiredClientContext, lookup of a component and
delegation of all method invocations to that component. Accordingly,
you also make use of the same wired-application.xml as in a stand-alone
application. I.e. the container-side of the WebService application is
already finished and needs to be deployed. Details concerning the
deployment process can be found in the AXIS documentation. The
following steps just give a short overview:
java WSDL2Java -p axis META-INF/hello.wsdl
The examples of the CUBA distribution provide an Ant target "stubs"
to simplify that step. The following code shows a HelloWorld client
accessing the component via AXIS. Again, only the client output appears
on the caller's console. The component output is written on the
server-side, and in a Tomcat engine by default appears in a file
catalina.out in the installation's logging directory.
package
axis;
public class AxisClient {
public AxisClient() throws
Exception {
public static void
main(String[] args) throws Exception { |
public
class HelloClient { public HelloClient() throws Exception { Properties props = new Properties(); props.put(WebserviceClientContext.WS_URL_PROPERTY, "http://localhost:8080/axis"); ClientContextI context = new WebserviceClientContext(props); HelloContractI hello = (HelloContractI)context.getComponent("hello"); System.out.println(hello.hello("CUBA")); }
public static void
main(String[] args) throws
Exception { |
java
cuba.util.codegen.AdapterGenerator –c -a 1.4 -t 1
META-INF/component-jar.xml
java cuba.util.ddgen.DDGenerator –c -a 1.4 -t 1
META-INF/component-jar.xml
javac hello/*.java
java
cuba.util.codegen.AdapterGenerator –c -a 1.4 -t 2
META-INF/component-jar.xml
java cuba.util.ddgen.DDGenerator –c -a 1.4 -t 2
META-INF/component-jar.xml
hello/ HelloContractI.class HelloImpl.class Hello_WSTClient.class Hello_WSTI.class Hello_WSTIService.class Hello_WSTIServiceLocator.class HelloImpl_WiredEx.class HelloImpl_WST.class HelloSoapBindingStub.class META-INF/ component-jar.xml wired-jar.xml Hello-deploy.wsdd Hello-undeploy.wsdd |
The code for the HelloWorld example above is available under examples/hello.
The distribution also contains Ant scripts to simplify the adapter
generation for the various modes. You just have to edit the file common.build.xml in the
installation root directory to provide the build scripts with
installation paths for AXIS and/or a JEE installation, depending on the
generator modes you would like to work with.
myapp/ src/ module1/ common/ comp1_interface comp1_valuetypes internal/ comp1_impl META-INF/ component-jar.xml module2/ common/ comp2_interface internal/ comp2_impl internal_comp3_interface internal_comp3_impl META-INF/ component-jar.xml META-INF/ wired-application.xml |
A mentionable application consists of multiple components which are structured in multiple modules. Each module requires its own meta information and should therefore have its own META-INF directory with a suitable component-jar.xml associated. A module's sources should be structured into different packages in order to separate the publically visible interfaces from the implementation and internal sub components. An application's meta information should not be mixed up with those of its modules and should therefore be located in an additional META-INF directory on the top level. The directory structure above is only an example, of course. However, no matter what you do, just ensure to structure you code in a way that it is suitable for a component-based architecture.
Home | Introduction | Javadoc |