CUBA Component Model
The component and programming
model for CUBA components closely follows the EJB 3 SessionBean model.
This
is a matter of nature due to the fact, that the EJB standard defines
the
most powerful component model available for Java-based systems. The API
looks
a little different sometimes to keep it independent from using it only
within a J2EE 5 environment. E.g. all code annotation types known from
the EJB 3 model have been redefined in package cuba.annotation and some have
been renamed to better express a concept rather than a technical issue
(like "External" instead of "Remote"). Nevertheless, developing CUBA
components is just like developing version 3 EJBs. This chapter
explains the central aspects of the CUBA component model
in relation to the EJB 3 model which are important to know for
efficient programming. In addition it is very helpful to have a look at
CUBA's JUnit test suite if there occur any questions in practical use.
Notes for users of CUBA version 1 and 2
Cuba version 1 and 2 already applied the most important improvements
which
came up in the EJB model with version 3. Therefore, the CUBA 3 model is
almost completely compatible with the CUBA 1 model. Migrating from CUBA
1 or 2 usually only requires to regenerate adapters and descriptors.
The
only non-compatible difference in the API is that the propriatery
interface cuba.SQLSourceI
from CUBA 1 has been removed and substituted by the Java standard type javax.sql.DataSource. Since DataSource has been moved to
the Java core API, it can be used without becoming dependent on JEE.
All functions of the older interface SQLSourceI are available in DataSource too, so any
migration work at this point is fairly simple.
Lifecycle Management
A CUBA component has the same lifecycle as a Stateless SessionBean from
the
EJB standard, being managed by either an EJB container (when running
the
component as an EJB) or by CUBA's wired container for all other
environments. Components may be declared stateful but do not have
support for activation and passivation yet as it is known from EJBs.
Lifecycle callback functions can optionally be defined by PostConstruct and PreDestroy annotations in the
code or in the XML descriptor. As an alternative, a component may
implement the interface ComponentI which defines a
minimal set of default lifecycle methods as they are known from
CUBA 1 (respectively from interface javax.ejb.SessionBean in EJB
2.1). In this case it is recommended to use the base class AbstractComponent for
convenience purposes, which defines empty implementations
for init() and drop() and
keeps the component context passed in setContext()
as a member. Lifecycle control can also be seperated from a component's
implementation class by means of interceptors. Interceptor
classes work exactly as it is specified in the EJB 3 standard.
Stateful components with container-managed transactions may implement
the interface cuba.StateSynchronizationI
to get informed about the begin and the completion of transactions the
component participates in. The interface defines identical methods as javax.ejb.SessionSynchronization
and has been redefined for the sake of independence from J2EE in every
development and runtime environment supported by CUBA.
Dependency Injection
Dependency injection in CUBA is the same as in the EJB 3 model. As a
tiny difference, a reference to another component is expressed by the
code annotation @Component
or the descriptor element component-ref
rather than @EJB and ejb-ref. If you
prefer lookups for components and resources, CUBA's container provides
the interface ComponentContextI.
In EJB mode, this is only a compatibility wrapper for the underlying
JEE SessionContext.
Transaction Managment
Transaction boundaries can be set by a component itself fetching the
current transaction context by function ComponentContextI.getTransaction().
A more convenient way is to use container-managed transactions being
declared on method level in a component's deployment descriptor.
Details are described in chapter Database
Access.
Database access
CUBA provides basic access to SQL databases by means
of the interface javax.sql.DataSource.
In an EJB environment, the actual
resource management is delegated to the application server. The
embedded
container
provides its own simple resource management including a connection
pooling
facility. Details are described in chapter Database Access. Convenient
access is provided by the Java Persistence API, which is also supported
by CUBA (see chapter JPA).
Callchain Management
Container-managed transactions in a wired container are based on a
general callchain management propagating a transaction and security
context down the
component call stack. This allows the wired container to 'emulate'
application
server functionality in unmanaged environments. Future extensions e.g.
for
container-managed security will be based on this feature. The devloper
usually
does not have to know about this technical detail.
Single-Thread Model
Like in the EJB component model, CUBA components can rely on a
single-thread model for the implementation class and are not allowed to
spawn any threads on their own behalf. Single thread programming for
server components is one
of the biggest simplifications in EJB development and therefore is kept
up
in CUBA as well. However, there is an important detail to know: while
the
wired container is completely thread-safe, the generated wired adapters
are
not. As a consequence, it is in the responsibility of the programmer
not
to share one component reference by multiple threads. As any component
lookup
returns a new adapter, it should be very easy to ensure this condition.
Deployment Descriptors
As it is known from the EJB standard, CUBA components are described by
a
set of annotations, declared in an XML-based deployment descriptor. The
source
descriptor is independent from the target environment and only serves
as
the input for the descriptor generator to generate either EJB standard
descriptors or descriptors for the wired container. The descriptor at
least mentions the
component name, interface and implementation class. Additional content
are e.g. configuration parameters (see chapter Environment Entries), depedency
injections and container-managed transaction
attributes (see chapter Database
Access). Users of a Java 5 environment may specify all meta
information for a component by means of Java code annotations as well.
Both description styles are fully equivalent and produce the same
output from CUBA's code and descriptor generators.
CUBA's embedded container solely uses the generated
XML descriptors to make it work in both JDK 1.4 and Java 5
environments. In addition to component descriptors, the embedded
container
needs an
application-level descriptor like the application.xml of a JEE
application.
The generated EJB 3 adapters are based completely on code annotations,
not regarding wether the CUBA component uses XML or code annotations.
This allows to override the component's configuration details by an EJB
deployment descriptor without changing the CUBA descriptor or CUBA
annotations. For EJB 2.1 adapters, the generated deployment descriptor
ejb-jar.xml is mandatory. The file name is defined by the EJB standard
and is the same as for EJB 3 alltough it is not fully compatible. A
component JAR can therefore not by equiped with adapters being suitable
for both EJB 2.1 and EJB 3 while there is no interference with the
adapters (and descriptors) for webservice and wired mode.
Environment Entries
Basic component configuration can be achieved by environment entries,
i.e.
named values, defined in a component's deployment descriptor. See
chapter
Environment Entries for
details.
Internal und external Interfaces
A component may have defined an internal
and an external interface.
Internal interfaces can only be used within the component container
having a call-by-reference semantic. External interfaces can also be
used by clients outside the container
and have a call-by-value semantic, no matter if the interface is later
accessed through interprocess communication or from within the same
process. Due to
tha fact, that CUBA component interfaces do not have to be derived from
any
base interface, one interface class can be used as both internal and
external
interface as long as all method signatures are suitable (i.e. all
parameters
are serializable and interoperable). This is especially useful when
building
up a catalogue of reusable components which must efficiently be
accessed
in different contexts.
Security
CUBA currently only supports component-managed security based on the
functions getCallerName()
and isCallerInRole()
in interface ComponentContextI.
In EJB environments, these functions are based on the appropriate
functions in interface EJBContext which in turn are based on
JAAS. The wired container follows a much simpler approach, assuming
authentication is either not required at all or is performed before
actually accessing the components.
Container-managed security will be added in future CUBA releases. See
chapter
Security for details.
Method interceptors
Method interceptors can be defined in the same way as in the EJB
standard using the code annotation @AroundInvoke or the descriptor
element around-invoke.
Method interceptors in CUBA get passed a cuba.InvocationContextI
which
is an equivalent to J2EE's InvocationContext
interface.
API
Not regarding the differences between the capabilities of an EJB
container and the simple embedded container, the programming model is
completely identical
in every environment. Components can be developed in one and safely be
run
in any other supported environment. This is one of the key features of
CUBA.
CUBA provides its own Java code annotation types rather than using the
ones from EJB 3. This is a matter of concept as CUBA components are
supposed to be completely independend from any of the target
environments. Additionally it would mess up the deployment if both, the
core implementations and generated adapters were EJB-annotated.
However, the available CUBA annotations are equivalent to what is
available in the EJB 3 standard.
Asynchronous Invocation
CUBA defines a component model which is not necessarily based on a
server environment and therefore has no concept for asynchronous method
invocation as it is known from EJB's message-driven beans. If
asynchronous invocation is required, the actual core component must be
invoked from
a self-written JMS consumer.