5.3 Start-of-Day Sequence
This section describes
- the issues surrounding start of day - starting up applications - in the GigaSpaces environment.
- the solutions chosen by GigaSystemBuilder
- what you have to do to use them.
Most of the examples in the GigaSpaces documentation are relatively simple and do not expose the issues encountered here:
they arise when you design for large grids containing many different spaces and PUs that provide interrelated services.
5.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.
5.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) GigaSystemBuilder automatically generates the "GSBFramework.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 GigaSystemBuilder 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 GigaSystemBuilder 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; GigaSystemBuilder 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 GigaSystemBuilder builds the GSBFramework 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 GsbFramework Jar.
- Modelled jars, in the order of appearance in the model.
- The PUs, observing the 'dependsOn' property.
What to Do
For most applications, the GigaSystemBuilder solves the issue of compile order - you should not have to do anything.
5.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 GigaSystemBuilder,
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 GigaSystemBuilder, 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 GigaSystemBuilder,
remote service and space proxies are not defined in the pu.xml.
Instead two helper classes are generated.
The first is the GsbSpaces class, which provides methods which return a GigaSpace object of the required space.
For example, to access the "CustomerEntity" space, you would write GsbSpaces.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 GsbProxies, which is a similar idea, but for services.
If there is service called 'MyService', then the static method GsbProxies.getMyServiceProxy() gets a synchronous proxy to the service
and GsbProxies.getMyServiceAsyncProxy() gets an asynchronous proxy to the service.
The proxies implement the 'IMyService' interface, which has the service methods on it.
Both the GsbProxies and the GsbSpaces use the XAP API to get the proxies.
What to Do
Use GsbSpaces.get<<spaceName>>() to access spaces.
Use GsbProxies.get<<serviceName>>[Async]Proxy() to access services.
5.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.
Version 1 of GigaSystemBuilder 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 starting with Version 2, GigaSystemBuilder manages the start-up sequence.
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.
5.3.4.1 Init Methods
|
The first step in the initialisation sequence is calling initMethod's.
A class, normally a JavaBean, can be marked as having an "initMethod".
This then uses the OpenSpaces facility which allows you to
insert an instance of the class into the PU and call its initMethod.
Our recommendation is that initMethod's act locally only (on this PU), because it is difficult to access
spaces and services on other PUs reliably from an initMethod.
5.3.4.2 Registering PUs
|
GigaSystemBuilder 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 GigaSystemBuilder.
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).
5.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 GigaSystemBuilder.
This is a flexible, user-defined mechanism, based on the file "appInit.xml" in the Eclipse project directory.
A skeleton (do-nothing) appInit.xml file is produced as part of a new Eclipse project;
it needs to be edited by hand in GigaSystemBuilder 2.
The root of the XML document in appInit.xml is one of
- a SequenceProcessor (class="com.gsb.core.AIMSequenceProcessor"), which executes its children synchronously one after the other
- a ParallelProcessor (class="com.gsb.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.gsb.core.AIMSequenceProcessor">
<property name="processList">
<list>
<value>serviceA.method1</value>
<bean class="com.gsb.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, GigaSystemBuilder puts an 'ApplicationInitialisationComplete' entry
into the UtilitySpace.
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.
5.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 use to define 'startClient' object in the PU.
This turns into an implementation method, which is called when the 'ApplicationInitialisationComplete' 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 ApplicationInitialisationComplete is already present in the space -
the condition for starting will still be met.
|