Assimilator FAQ

The following topics are presented in this FAQ

General

General questions about what Assimilator is and its capabilities.

Operational String

This section provides an overview of some common questions in dealing with the OperationalString:

Developer aides

This section provides an overview of some common questions in developing and debugging services with Assimilator

Runtime Environment

This section provides an overview of some common questions in dealing with the Runtime Environment




Q: What is Assimilator?
A: Assimilator is and open source project providing a distributing computing platform for executing and managing distributed services. It implements capabilities for executing services in a network that need reliable discovery and remote communication and self-management to insure services are meeting define service level agreements.

Q: What are the core components?
  • Service Container (Cybernode) - which hosts service components and executes them and keeps track of available resources e.g. Operating System, hardware platform, CPU, Disk storage, memory, etc.
  • Service Monitor (Provisioner)- keeps track of available Containers, insures deployed services are executing to defined SLAs, deploys services to containers with matching criteria.
  • Code Server (Webster) - serves all code needed to deploy services to a container including service core code, downloadable (stubs) service code for remote access, service GUI code to remotely interface with the service.

Q: How do I use Logging?
A: The Logging element has been provided to be able to declare named Logger attributes (Levels, Handlers, etc...) at runtime without depending on logger.properties attributes and values to be set.
 
Using the Logging element in the OperationalString Document :

    <Logging>
        <Logger Name="net.assimilator.demos.hello" Level="ALL">
            <Handler Class="java.util.logging.ConsoleHandler" Level="ALL"/>
        </Logger>
    </Logging>

 If we were to choose a java.util.FileHandler as the Handler the declaration would be as follows:

    <Logging>
        <Logger Name="net.assimilator.demos.hello" Level="ALL">                   
            <Handler Class="java.util.logging.FileHandler" Level="ALL">
                <Parameters>
                    <Parameter Name="java.lang.String" Value="${ASSIMILATOR_LOG_DIR}${/}hello%u.log"/>
                    <Parameter Name="boolean" Value="true"/>
                </Parameters>
                <Formatter Class="java.util.logging.SimpleFormatter"/>
            </Handler>
        </Logger>
    </Logging>

Note the use of ${ASSIMILATOR_LOG_DIR} and ${/}. These values refer to the values of system properties by using the syntax ${propertyName} within the Value literal. The ${/}refers to the file name separator character. Its use is identical to that found with the Jini Configuration approach.

Information on Logger names for Assimilator components can be found here

Q: What is the DeploymentSchedule?


A: The DeploymentSchedule element allows a deployer to specify a time in the future when an OperationalString will be deployed and how long it will remain deployed (the deployment duration).

Scheduling of an OperationalString is part of the OperationalString itself, the schedule itself is an optional declarative attribute, and if not declared the OperationalString will be deployed immediately.

The OperationalString is scheduled as part of the OperationalString element. An example of a
schedule follows:

<DeploymentSchedule>
    <DeployDate DayOfWeek="Thursday" Hour="8" Minute="30" Format="PM"/>
    <Duration Minutes="30"/>
    <Repeats Count="10">
        <Interval Days="1" Hours="1" Minutes="1"/>
    </Repeats>
</DeploymentSchedule>

This DeploymentSchedule produces the following:
  • Deploy the OperationalString every Thursday at 8:30 PM
  • The OperationalString shall remain deployed for 30 minutes
  • The OperationalString deployment repeats 10 times, waiting 1 day, 1 hour and 1 minute between
  • deployments
Once the DeploymentSchedule element is parsed, a net.assimilator.core.Schedule object is created and
set to the enclosing net.assimilator.core.OperationalString object.

Once the OperationalString is deployed, the Schedule is consulted to determine the semantics of when to optionally schedule the OperationalString deployment.

Q: What is the SystemRequirements element and what does it mean?


A: The SystemRequirements element is a child of the ServiceLevelAgreements element and provides information specific to what system components and measures must be present on a compute resource in order for a ServiceBean to be provisioned. These capabilities allow better control over resources and better provisioning behavior.

Declaring SystemRequirements for a ServiceBean allows specific matching to occur, ensuring the declared properties are matched to advertised Cybernode capabilities. Cybernode provides (and updates) a net.assimilator.qos.ResourceCapability object with it's registration to discovered ProvisionMonitor instances.

The SystemRequirements element allows the declaration of SystemComponent and Utilization elements.Determination of whether a platform capability can be met is decided by the PlatformCapability itself. Declared Utilization requirements are verified against the net.assimilator.core.MeasuredResource objects obtained from the net.assimilator.qos.ResourceCapability object.

If there are no Cybernode instances which support declared SystemRequirements, the service will not be provisioned, and the corresponding log message "No compute resources found that meet requirements" will be logged by the ProvisionMonitor.

Q: How do I use the SLA element?


A: The definition of a SLA typically represents a contractual service commitment, describing the minimum performance criteria to meet while delivering a service. In this context the SLA element provides the means to specify low and high limits (defining an acceptable range) for a SLA with a given identifier. Additionally the SLA provides a mechanism to associate a SLA policy handler, which can be used to process policies specific to SLAs as they occur, taking specific actions appropriate for the ServiceBean.

Assimilator provides 3 SLA policy handlers. Additional policy handlers can be created by extending the policy handler class.


net.assimilator.qos.SLAPolicyHandler If a threshold is crossed (breached or cleared), the SLAPolicyHandler will fire a SLAThresholdEvent
net.assimilator.qos.RelocationPolicyHandler
Extends net.assimilator.qos.SLAThresholdHandler, and if thresholds are crossed, will inform the OperationalStringManager to relocate the ServiceBean
net.assimilator.qos.ScalablePolicyHandler
Extends net.assimilator.qos.SLAThresholdHandler, and will increment and optionally decrement instances of the ServiceBean it is associated to based on limits set for the SLA


When a SeviceBean is instantiated by a Cybernode, the SLA element's ID is used to match a net.assimilator.watch.Watch that has been created by the ServiceBean. If  the SLA ID cannot be matched to a watch, the SLA will not be associated.

Once associated the SLAPolicyHandler will be notified based on the thresholds that have been set.

The SLA element contains a Configuration as a child element. The Configuration element is used to declare an net.assimilator.qos.SLAPolicyHandler instance (the declared instance must extend this class), and used to configure the declared  SLAPolicyHandler instance.

<SLA ID="Hit Counter" Low="2" High="7" >
    <Configuration>
        <Component Name="net.assimilator.qos.SLAPolicyHandler">
            <Parameter Name="slaPolicyHandler"
                       Value="new net.assimilator.qos.ScalingPolicyHandler(
                                           (net.assimilator.core.SLA)$data)" />
        </Component>
        <Component Name="net.assimilator.qos.ScalingPolicyHandler">
            <Parameter Name="MaxServices" Value="2" />
            <Parameter Name="LowerThresholdDampeningFactor" Value="30000" />
            <Parameter Name="ServiceDiscoveryWait" Value="2000" />
        </Component>
    </Configuration>
</SLA>


Q: What is a SharedComponent and how do I use it?


A: A SharedComponent is a class which can be dynamically installed into the shared ComponentLoader and  used by all active services. Access to the ComponentLoader is from the ServiceBeanContext.

The shared component concept was originally created to deal with the issue of loading and sharing native libraries, but other uses can be thought of. Examples:
  • Creating a shared blackboard for local communications
  • Creating transient PlatformCapabilities
  • Creating a common capability to share across service instances like a Jxta peer or a database connection
Declaring a shared component is done as part of the Resources element. Example:

<ImplementationClass>net.assimilator.demos.jni.JNITest
    <Resources>
        <JAR>jni.jar</JAR>
        <JAR>assimilator.jar</JAR>
        <SharedComponent>
            <Class>net.assimilator.demos.jni.component.JNIObject</Class>
            <JAR>jni-component.jar</JAR>
       </SharedComponent>
    </Resources>
</ImplementationClass>
 

Q: What does global configuration mean and how do I use it?


A: Global configuration is simply the capability to declare common attributes of services declared in the OperationalString as direct children of the OperationalString element. This provides ease of use reducing the amount of (potential) repetitive definitions for services in an OperationalString.

The benefit to doing this is you define common attributes once, and all ServiceBean and Service definitions essentially 'inherit' those declarations. If attributes are declared at both levels (OperationalString and ServiceBean) , declared ServiceBean attributes would take precedence. That is if an OperationalString-level Parameter is defined, and the same name is defined at the ServiceBean-level the ServiceBean defined parameter would override the parameter declared at the OperationalString level for that specific Service's parameter configuration.

Elements that can be globally declared are:
  • Cluster
  • Codebase
  • Configuration
  • Groups
  • Locators
  • Logging
  • Organization
  • Parameters
If global elements are declared in both the OperationalString element and either the ServiceBean or Service element, they are replaced with the ServiceBean or Service declared values. If the declared elements contain child elements (Cluster, Groups, Locators, Parameters, Logging, Configuration, Cluster) the element's attribute of IncludeGlobalDecl is used to determine if the globally declared value should be added to the current declaration, or the current declaration should override the global declaration. If IncludeGlobalDecl is set to "yes" then the globally declared value is included. If "no" then the locally (the element in the ServiceBean or Service element) is used.

Example 1

<OperationalString Name="Hello World">

        <Parameters>
            <Parameter Name="param4" Value="baz"/>
        </Parameters>
       
        ...

        <ServiceBean Name="Hello World" MatchOnName="yes" ProvisionType="fixed">
            ...
           
            <Parameters IncludeGlobalDecl="yes">
                <Parameter Name="param1" Value="foo"/>
                <Parameter Name="param2" Value="bar"/>
                <Parameter Name="param3" Value="foobar"/>
            </Parameters>
            ...
        </ServiceBean>
</OperationalString>


In this example the resulting Parameters are:

param1=foo
param2=bar
param3=foobar
param4=baz

Example 2

<OperationalString Name="Hello World">

        <Parameters>
            <Parameter Name="param4" Value="baz"/>
        </Parameters>
       
        ...

        <ServiceBean Name="Hello World" MatchOnName="yes" ProvisionType="fixed">
            ...
           
            <Parameters>
                <Parameter Name="param1" Value="foo"/>
                <Parameter Name="param2" Value="bar"/>
                <Parameter Name="param3" Value="foobar"/>
            </Parameters>
            ...
        </ServiceBean>
</OperationalString>


In this example the resulting Parameters are:

param1=foo
param2=bar
param3=foobar

Example 3:

<OperationalString Name="Hello World">

        <Parameters>
            <Parameter Name="param4" Value="baz"/>
        </Parameters>
       
        ...

        <ServiceBean Name="Hello World" MatchOnName="yes" ProvisionType="fixed">
            ...
           
        </ServiceBean>
</OperationalString>


In this example the resulting Parameters are:

param4=baz

In addition to this processing if a globally declared value contains child elements, and  the child element is  of the form key/value (Parameters,  Configuration, Logging), the declared value at the <ServiceBean> or <Service> element will override the key/value of the global element

Example 4

<OperationalString Name="Hello World">

        <Parameters>
            <Parameter Name="param2" Value="baz"/>
        </Parameters>
       
        ...

        <ServiceBean Name="Hello World" MatchOnName="yes" ProvisionType="fixed">
            ...
           
            <Parameters>
                <Parameter Name="param1" Value="foo"/>
                <Parameter Name="param2" Value="bar"/>
                <Parameter Name="param3" Value="foobar"/>
            </Parameters>
            ...
        </ServiceBean>
</OperationalString>


In this example the resulting Parameters are:

param1=foo
param2=bar
param3=foobar


Q: What is a SoftwareLoad?


A: The SoftwareLoad element is declared by a ServiceBean as an optional parameter, which is used to install required software on a compute resource if the compute resource does not have the requisite software available (as determined by advertised PlatformCapability objects).

A SoftwareLoad can only be provisioned to a Cybernode if and only if the Cybernode supports persistent provisioning (as determined by the net.assimilator.cybernode.provisionEnabled property set by the Cybernode) and there is adequate diskspace on the targeted machine.

If the SoftwareLoad can be provisioned to the Cybernode, the SoftwareLoad element defines an installable software element with a post install helper to configure the downloaded software. The SoftwareLoad additionally provides a RemoveOnDestroy attribute which will cause the SoftwareLoad to be removed from the compute resource when the ServiceBean has terminated. If the RemoveOnDestroy is declared as no, then the SoftwareLoad will remain on the compute resource after the ServiceBean has terminated. Additionally, the compute resource's PlatformCapability collection will be updated to reflect the addition of the SoftwareLoad.

If the SoftwareLoad is installed on the compute resource a net.assimilator.core.provision.SoftwareDownloadRecord is created and used to document disk space use as part of the net.assimilator.core.provision.ServiceRecord and net.assimilator.core.provision.ServiceStatement created documenting service instantiation time and usage.


Q: Whats the difference between ProvisionType of fixed and dynamic?


A: The ProvisionType is an attribute of the ServiceBean element and indicates how the ServiceBean will be provisioned and allocated through the network.
  • The dynamic type indicates that the service will be provisioned to available compute resources that meet declared SystemRequirements up to the amount specified by the Maintain element (the number of planned instances). This is the default ProvisionType

  • The fixed type indicates that the service will be provisioned to to available compute resources that meet declared SystemRequirements. The number of services provisioned to each compute resource is the number specified in the Maintain element
So when to use dynamic and when to use fixed?

Use dynamic when you want to have a certain amount of services running on machines which meet the declared SystemRequirements for the service. For example if you need 10 services running across a collection of compute resources at all times, then use dynamic.

If however you want to make sure that every compute resource that advertises a certain capability has a service running on it use fixed. If you have 100 compute resources online, you'll have 100 services. If there are 5 online, you'll have 5 services.

Q: Whats the difference between Parameters and Configuration and when should I use which one?

A: Right off the bat the Configuration element produces net.jini.config.Configuration object and the Parameters element creates a java.util.Map with name/value keys populated from the element. The Configuration is obtained by using the net.assimilator.core.jsb.ServiceBeanContext.getConfiguration() invocation. The name/value pairs obtained from the Parameters element are accessible using the  net.assimilator.core.jsb.ServiceBeanContext.getInitParameterNames()  and  net.assimilator.core.jsb.ServiceBeanContext.getInitParameter(String name) methods.

The Parameters element initially creates String name/value pairs. You can add to the parameters as well during service execution, creating name/value pairs where the value  can be something other then a String.

Use the Configuration if you need to construct complicated Object types at initialization, otherwise the Parameters element serves the purpose for setting runtime properties.

Q: What is the reason for having a Group in the opstring?  Don't all services use the same group as the ProvisionMonitor?

A: Groups are specified in the OperationalString to indicate the set of groups the Service will use for discovery. These groups do not need to be the same as the set of groups configured for the ProvisionMonitor. The ProvisionMonitor uses the Groups declared by the service to discover instances of the service in order to monitor, and when needed allocate instances of the service to compute resources which meet declared requirements of the service.

The groups a ProvisionMonitor uses for discovery do not need to match the groups a service uses. If this were the case then you would need to modify the set of groups the ProvisionMonitor has been set with to match the set of all groups for all services the ProvisionMonitor is monitoring. Alternatively you could deploy a ProvisionMonitor each time a service were added to the network which had a different set of groups then the groups the ProvisionMonitor knows about. Neither of these cases are preferred.

Q: How do I declare a ServiceUI in the OperationalString?

A Service UI can be declared in the OperationalString using <Configuration> approach.

The net.assimilator.jsb.ServiceBeanAdapter will use the net.jini.config.Configuration object returned from net.assimilator.core.jsb.ServiceBeanContext.getConfiguration() to get a configuration property named <package-name-of-the-service>.serviceUIs. In this example the configuration entry is net.assimilator.examples.demos.hello.serviceUIs

This approach was chosen instead of a more general "get initial attributes" approach because additional data is being provided to resolve the host address and port where the service UI can be located on the network.

An example of the declaration of a serviceUI for the Hello service:

<Component Name="net.assimilator.demos.hello">
    <Parameter
        Name="serviceUIs"
        Value="new net.jini.core.entry.Entry[]{
               net.assimilator.entry.UIDescriptorFactory.getUIDescriptor(
                   net.jini.lookup.ui.MainUI.ROLE,
                   net.jini.lookup.ui.factory.JComponentFactory.TYPE_NAME,
                   (String)$data,
                   &quot;hello-ui.jar&quot;,
                   &quot;net.assimilator.examples.demos.hello.HelloUI&quot;,
                )}
    "/>
</Component>

To declare the same service UI which additionally computes a message digest using the codebase :

<Component Name="net.assimilator.demos.hello">
    <Parameter
        Name="serviceUIs"
        Value="new net.jini.core.entry.Entry[]{
               net.assimilator.entry.UIDescriptorFactory.getUIDescriptor(
                   net.jini.lookup.ui.MainUI.ROLE,
                   net.jini.lookup.ui.factory.JComponentFactory.TYPE_NAME,
                   (String)$data,
                   &quot;hello-ui.jar&quot;,
                   &quot;net.assimilator.examples.demos.hello.HelloUI&quot;,
                   true,
                   &quot;sha&quot;
                )}
    "/>
</Component>




Q: How can I debug a service?

A: To debug a service, you need to do the following:

  • Make sure Java compiler generates debug info when compiling the source code of the service. If you use command line javac compiler, specify "-g" option. If you use Ant, specify "debug" and, optionally, "debuglevel" parameters for the <javac> task. If you use an IDE, make sure the compiler generates debug info.
  • Run JVM that hosts Cybernode with the options that enable remote debugging:
    -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=8888,suspend=y
If you use the cybernode.bat (Windows) or cybernode.sh (Unix) script to start Cybernode, make a copy of the script (name it e.g. cybernoded.bat or cybernoded.sh) and modify it to pass the debugging options to JVM:

On Windows:
set DEBUG_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=8888,suspend=y

%JAVA_HOME%\bin\java -server %classpath% -Djava.security.policy=%ASSIMILATOR_CORE_HOME%\policy\policy.all -Djava.protocol.handler.pkgs=net.jini.url -DASSIMILATOR_CORE_HOME=%ASSIMILATOR_CORE_HOME% -DJINI_HOME=%JINI_HOME% -DASSIMILATOR_LOG_DIR=%ASSIMILATOR_LOG_DIR% %DEBUG_OPTS% %launchTarget% %ASSIMILATOR_CORE_HOME%\configs\start-cybernode.config

On Unix (bash):
DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=8888,suspend=y"

$JAVA_HOME/bin/java -server $classpath -Djava.security.policy=$ASSIMILATOR_CORE_HOME/policy/policy.all -Djava.protocol.handler.pkgs=net.jini.url -DASSIMILATOR_CORE_HOME=$ASSIMILATOR_CORE_HOME -DJINI_HOME=$JINI_HOME -DASSIMILATOR_LOG_DIR=$ASSIMILATOR_LOG_DIR $DEBUG_OPTS $launchTarget $ASSIMILATOR_CORE_HOME/configs/start-cybernode.config

Now use the modified script to start Cybernode. Note that "suspend=y" means that JVM will suspend until you connect to it with a debugger. For more details see e.g. http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html. If you use the startall script to start Assimilator infrastructure, create and use the debug version of this script in the same way.
  • Attach your Java debugger to the JVM. For instance, if you use NetBeans 4.0, click the "Attach Debugger..." toolbar button, make sure "dt_socket" transport is in use and "SocketAttach ..." is selected in the "Connector" field, type 8888 in the "Port" field, and click "OK". Once the debugger is attached, JVM usually resumes automatically; otherwise you should do this manually. Now you can set breakpoints in your code (in most of the IDEs you can also set breakpoints before you attach the debugger). To test that debugging works, set a breakpoint in the service's initialize() method. Now deploy the opstring using OpString Monitor. This should instantiate the service, so if you have a breakpoint in the initialize() method, the debugger should stop on it. Now you are able to debug the code. Note that the debugger should know where the source code of the service is located. In NetBeans it is enough to have the corresponding project opened.
Note that you can also debug Assimilator code itself. Assimilator is built with debug info turned on by default, so all you need is to tell the debugger where Assimilator sources are located. For example, In NetBeans you can add lib/assimilator.jar to the collection of libraries and indicate that the sources are located in the modules/core/src directory of the Assimilator distribution.

Of course, this is not the only way to debug Assimilator services, but, perhaps, a simple one. This approach has been verified on Solaris, not on Windows or OS X.

Q: How do I know whether a service has been provisioned for the first time or whether the service is being provisioned as a result of failure?

A: The easiest way is to check the Host History for the service. Every service will have a history of the hosts it has been instantiated on. If the service has never been instantiated on a host, it's host history will have a length of zero. Example :

java.util.List hostList =
            (java.util.List)context.getServiceBeanConfig().
                                    getConfigurationParamaters().
                                    get(ServiceBeanConfig.HOST_HISTORY);
if(hostList.size()==0) {
    System.out.println("First time");
} else {
    for(java.util.Iterator it=hostList.iterator(); it.hasNext();)
        System.out.println("\t"+it.next());
}

Another approach is to add a service-defined initialization parameter. this can be done very easily in the ServiceBean initialize() method as follows:

public void initialize(ServiceBeanContext context) throw Exception {
    super.initialize(context);
    String cookie = (String)context.getInitParameter("service-cookie");
    if(cookie!=null) {
        System.out.println("cookie="+cookie);
    } else {
        System.out.println("First time, create service cookie");
        context.getServiceBeanConfig().addInitParameter("service-cookie", "some-value");
    }  
}

It should be noted that the ServiceBeanConfig survives service crashes, and can be used to store service specific attributes.



Q: On a Linux platform when I execute the shell scripts why does it say: bad interpreter: No such file or directory

The shell script interpreter being used may not be found or there may be extra control characters at the end of each line.

Check to make sure the other scripts can be run using /bin/bash. Check to make sure the first line of the Assimilator script includes the correct interpreter line. For example: #!/bin/bash

If there are \r characters for carriage returns at the end of each line, they need to be removed for Linux. These are ok in Windows. The control characters can be removed using the following command:
dostounix filename.sh
SourceForge.net Logo