6.3 Start-of-Day Sequence
This section describes start of day processing - starting up applications - in the CloudTran environment.
The good news about this section is that you do not need to worry about it if your application
does not do anything special at start of day. In that case, CloudTran will take care of bringing up the grid
and loading data as required.
If you do need to initialise your application before it starts,
you will need to understand the options presented here and choose the appropriate startup method.
6.3.1 Clients
|
In discussing the start of day sequence, we first have to introduce the notion of clients.
A client is a PU that is part of an application but does not have any spaces or services.
In this case, it cannot provide any passive ("sit there and wait to be asked something") facilities,
so it must be a client to the spaces and services: it must initiate action.
A PU that does have a space or service is called a non-client.
An important design difference between clients and non-clients is that clients may start later, or come and go,
whereas for the complete application to be there, a quorum of the non-clients will be continuously active.
6.3.2 Compile-Time Dependencies
|
The first issue in considering the build and deployment of an application
is that the compilation of one Jar or PU may depend on classes in another Jar or PU.
There must be a way of structuring the application so that the compiler can resolve references at build time.
The solution is twofold.
(1) CloudTran automatically generates the "CtFramework.jar" -
a lowest common denominator jar. This contains
- the definitions of all Java Beans defined across all the PUs.
In general, Java Beans will be shared between services and clients, or between
services and spaces, so it makes the build process a lot simpler to understand if all the classes
go into a common jar.
- core framework classes (utility methods, logging helper) used by CloudTran as part of its operation
- interfaces to all the services you define.
(2) Each PU can define PUs on which it depends at compile time, using the 'dependsOn' property.
This affects the order in which PUs are built:
if PUa has "dependsOn='PUb'", then PUb will be built before PUa in the generated build scripts.
It used to be necessary to use this feature to make things compile.
Now in a typical CloudTran application, you should not have to use this facility.
However, it is available should your particular application have some compile-time dependency between PUs.
Rules:
- There can be multiple PUs listed in the 'dependsOn' property.
- The 'dependsOn' can be scattered around PUs; CloudTran resolves the list into a single order.
- Circularities in the 'dependsOn' (PUa dependsOn PUb, which dependsOn PUc, which dependsOn Pua)
are not allowed. They are detected during generation and cause the build to fail.
- This feature only applies to PUs, not Jars.
You can specifically define one or more Jars in the model,
although the way CloudTran builds the CtFramework should remove the need for this in most cases.
Jars are built in the order of declaration (there is no dependsOn there). This means that the complete build order is:
- The CtFramework Jar.
- Modelled jars, in the order of appearance in the model.
- The PUs, observing the 'dependsOn' property.
What to Do
For most applications, the CloudTran solves the issue of compile order - you should not have to do anything.
6.3.3 Run-time Dependencies - Spaces and Services
|
Services and PU maintenance activities need to access remote spaces and other services.
It is perfectly possible and reasonable for two PUs to have a runtime dependency on each other,
either by accessing each other's spaces or services.
For example, in the CloudSave product which is built using CloudTran,
there are PUs to hold entities and PUs to interface to a persistence store.
In one direction, the persistence PUs write into the entity spaces;
in the other direction, the entities will use services in the persistence PUs.
In version 1 of CloudTran, cross references like this were made by adding a reference in the pu.xml
to the other PU's space and a remoting reference call.
The programmer made references to other spaces and services using a proxy object inserted by Spring.
This is not possible in version 2, because it leads to a circular reference.
In our example, when the persistence PUs try to start, they cannot find the entity space because they have not yet been started;
and with the other order, the same problem arises of course.
Therefore, starting with version 2 of CloudTran,
remote service and space proxies are not defined in the pu.xml.
Instead two helper classes are generated.
The first is the CtSpaces class, which provides methods which return a GigaSpace object of the required space.
For example, to access the "CustomerEntity" space, you would write CtSpaces.getCustomerEntity(),
which returns a 'GigaSpace' proxy to the space.
This is lazy-loaded, and will not be accessed before all the spaces are started.
The second is CtProxies, which is a similar idea, but for services.
If there is service called 'MyService', then the static method CtProxies.getMyServiceProxy() gets a synchronous proxy to the service
and CtProxies.getMyServiceAsyncProxy() gets an asynchronous proxy to the service.
The proxies implement the 'IMyService' interface, which has the service methods on it.
Both the CtProxies and the CtSpaces use the XAP API to get the proxies.
What to Do
Use CtSpaces.get<<spaceName>>() to access spaces.
Use CtProxies.get<<serviceName>>[Async]Proxy() to access services.
6.3.4 Distributed Application Initialisation
|
In large applications with multiple PUs, partitions and backups running on different machines,
there will be many PU instances to manage. The features described here help with this because you can
- know that all the PUs specified in the application are up and running before starting the application initialisation sequence
- have central coordination of the startup sequence.
Previous versions of CloudTran used a local file and PU startup ordering to do this.
However, in a large deployment with an unknown local filesystem (like a cloud),
this scheme does not work ... and it is slow when it does.
So now CloudTran manages the start-up sequence including your own workflow actions defined in an XML file.
The steps are described in the following sections.
What to Do
Do local (within-PU) initialisation using the init-methods on <class> objects.
Do cross-grid initialisation using services defined on spaces, using the appInit.xml file to define the workflow.
6.3.4.1 Init Methods
|
The first step in the initialisation sequence is calling initMethod's.
You can set the "initMethod" property on a method (on a class or JavaBean) to true, indicating the this method is to be called
at this point - the first step in initialisation.
These methods can only act locally (on this PU instance - within the JVM),
because no other PUs are reachable when this is called (and the initialisation sequence will lock up!).
6.3.4.2 Registering PUs
|
CloudTran creates a PU Registration service in the Utility PU so all the PUs can register their existence,
which they do once all the initMethod's have completed.
The code to do this and its invocation is created automatically by CloudTran.
The application programmer does not have to do anything to make this happen.
This allows the application initialisation manager described next to wait for the complete set of PUs (including backups) to be present
before starting the final stage of initialisation.
This feature means that all the PUs can be started in parallel (speeding up system start-up and development testing).
6.3.4.3 Non-client Initialisation
|
Once the complete set of PUs is ready, application-specific initialisation of the non-client PUs can start.
This is run by the Application Initialisation Manager (AIM), which is located in the Utility PU
and automatically generated by CloudTran.
This is a flexible, user-defined mechanism, based on the "appInit.xml" file,
which is generated once-only, in the Coordinator PU's src/META-INF directory:
The initial appInit.xml file is is a skeleton (do-nothing) file.
If you want to start up different activities on different PUs to initialise your application before it is "open for business",
then add entries in here.
We noted above that this file is "generated once-only".
This means you can edit this after it is created: CloudTran won't change it in subsequent generations when the file already exists.
The root of the XML document in appInit.xml is one of
- a SequenceProcessor (class="com.cloudtran.core.AIMSequenceProcessor"), which executes its children synchronously one after the other
- a ParallelProcessor (class="com.cloudtran.core.AIMParallelProcessor"), which executes its children in parallel and then waits for them all to complete before returning.
The processor elements then have a single <property>, which has a single nested <list> element.
The contents of the list element can be another '...Processor' bean or a <value>.
The <value> defines a service method to be called, in the form 'serviceName.methodName'.
<bean class="com.cloudtran.core.AIMSequenceProcessor">
<property name="processList">
<list>
<value>serviceA.method1</value>
<bean class="com.cloudtran.core.AIMParallelProcessor">
<property name="processList">
<list>
<value>serviceB.method2</value>
<value>serviceC.method3</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
|
This says:
- First, call serviceA, method1(). Wait for it to finish.
- Then, call, in parallel: serviceB, method2(); and serviceC, method3().
- Wait for the parallel calls to both complete, then finish the initialisation sequence.
At the end of the initialisation sequence, CloudTran puts an 'OpenForBusiness' entry
into the UtilitySpace. This serves as a permanent marker, which is available even if the transaction has failed.
Client applications can wait for that to appear and then start using services, as described below.
The benefit of this approach is that it can coordinate distributed application initialisation
and also do it as fast as possible by doing unrelated tasks in parallel.
6.3.4.4 Identifying Service Methods in 'appinit.xml'
|
Normally, you identify service methods in 'appinit.xml' in 'serviceName.methodName' format
(e.g. 'serviceB.method2' in the example above).
In the unlikely event that this is not unique - the same service/method combination exists in two PUs -
you can disambiguate by prefixing the name with 'puName.'. So the fully-qualified name is
'puName.serviceName.methodName'.
6.3.4.5 Controlling Data Load
|
The loading of data, from persistent stores like databases into the grid, is handled by a no-name service.
This service can be called in one of two ways:
- You can call it explicitly by referencing it in
appInit.xml.
To do this, the usual name used is
For disambiguation (in the unlikely event that your application has another PU with a 'TxBAdmin' service, with a 'loadAllData' businessMethod),
you can use the full name of
CoordinatorPU.TxBAdmin.loadAllData
|
- If you do not reference TxBAdmin.loadAllData in appInit.xml,
CloudTran calls the TxBAdmin.loadAllData operation once appInit.xml processing is complete.
The bottom line is that, either way, the loadAllData operation will be called.
Normally, CloudTran then goes ahead and loads the data.
However, you can prevent the loadAllData method from doing the actual loading by setting the
ct.persist.loadAtStartOfDay property to false.
If you do this, CloudTran will try to initialise any automatically-generated primary keys.
6.3.5 Client Initialisation
|
Once the non-client parts have initialised, clients can start using spaces and services.
The architected way of doing this is to define a 'startClient' model object in the PU.
This turns into an implementation method, which is called when the 'OpenForBusiness' appears in the space.
The programmer is responsible for writing the implementation of the startClient method.
ClientInit methods are similar to 'pulse' methods. They are both called after the initialisation sequence is complete.
The implementation for both lands up in the [[pu name]]PU_Actions class, and they use the same namespace -
a 'pulse' must not have the same name as an 'startClient'. In fact, a 'startClient' method is exactly the
same as a 'pulse' that has 0 initialDelay, and is not repeated - it's a oneshot.
This facility is implemented using a read from the space rather than using an event,
so that if the client is started after all the other application is complete - and the OpenForBusiness is already present in the space -
the condition for starting will still be met.
|