Knowledge Base

This page gives coding examples and explanations for certain procedures. If you have particular requests for any examples, then please email admin on this site with the request.

 

  1. Some help tutorials and videos are available.
  2. The Javadoc for the main system is provided.
  3. Programming examples are also provided for certain basic operations.
  4. The final section describes the reasoning behind features that might cause confusion.



Help

I would be grateful for suggestions on how the system is being used or what features you would like. It might be the case that they would not get implemented, but it helps to determine what the system is being used for. So please send an email with your request.



Tutorials and Videos

A tutorial describing a trick to protect global methods in the remote API is available here.

A tutorial about the REST interface has been written and is available here.


Some help videos for the free GUI are listed here. Of course, you can write your own, or add modules to this one.


Setup the GUI after installing [YouTube] Start a Service Running [YouTube]
Disable the Jar Factory [YouTube] Password Protect a Service [YouTube]
Register a Remote Server [YouTube] Service Address Books [YouTube]



Javadoc

The javadoc for the main source package and the main problem solver package can be viewed online or downloaded. This can be more up to date than the current source code release. Reading the 'org.licas' package classes from the 'licas' jar first is the best place to start. There are also other packages that are not open source, but are part of the licas system. You would be expected to use them through the open source licas or problem solver classes, but it might be helpful to know what they are. The javadoc for two of these jars is also provided.



Open Source

View the main problem solver javadoc here, or download here. (solver/licas_solver.jar includes licas.jar)
View the main licas javadoc here, or download here. (server/licas.jar includes other packages)


Other Packages

View the licas soap javadoc here, or download here. (Uses licas.jar)
View the licas script javadoc here, or download here. (included in above packages)
View the licas text javadoc here, or download here. (included in above packages)
View the ai heuristic javadoc here, or download here. (included in above packages)
View the licas internet javadoc here, or download here. (included in above packages)
View the licas xml javadoc here, or download here. (included in above packages)
View the jlog2 javadoc here, or download here. (included in above packages)


A short description of the software hierarchy is as follows:
  1. Problem Solver: this extends the licas classes with more centralised and AI-related search and evaluation classes. It provides the new hyper heuristic framework, genetic algorithms and distributed linking options, but can be extended to import other evaluation modules.
  2. Licas: this is the main licas project. It contains all of the basic components - server, communication, services, linking, web services, etc. This package is more distributed in nature, with autonomic components able to communicate with each other through the default framework.
  3. Licas Script: BPEL-style autonomic script. This is not open source but contains the auto engine that can read a BPEL-style script and execute it. So you can configure a service behaviour externally or even dynamically.
  4. Licas Text: text based processing. This is not open source but contains the query engine that licas uses to process text-based or XML-based documents.
  5. AI Heuristic: at the bottom of this stack, this contains more general AI-related classes. It includes some standard implementations of known evaluation metrics and also some text based structures, such as a bag-of-words. The other packages use these classes as part of their AI-evaluation processes. Some algorithms here have been updated to be generic. However, for a distributed environment where you are testing some form of information processing, they offer a wide variety of options.

Third Party Packages

The packages use a number of third party open source or free code, so many thanks to those projects.




Code Examples


Some basic coding examples are listed here.



1. Server Examples

How to run the server that is provided or create your own server, or access a server from a client. Also how to add a service to a server.


Create Your Own Server

If you want to run a sever locally from your own project, you can do this by copying one file from the licas package into your own project and then running that file. The licas package also needs to be included as an import.


The following code example shows how this can be done.



Run a Server Batch File

This is an example of a batch file that can be used to start a server running. Convert to '.bat' before running.



Add Server Details to a Client

To invoke methods on services running on a server, you firstly need to make sure that you are able to access the server itself.


The following code examples describe how you can check that the server can be accessed.



Add a Service to a Server

After you have retrieved the server details, you can then add services to it and start them running.


The following code example describes how this can be done.




2. Script Examples

How to use the classes to initialise a service, or create a BPEL-style Auto script, for the AutoEngine to execute.


Initialisation through the Admin Script

This can now be a detailed process, where the more advanced features are typically not required. An admin script itself is usually not required and is only if you want to dyanmically change the service config, outside of what the static code offers.


setInstanceValues Method

It is possible to declare actual values for variables in a class and load this in through the admin script that is used to initialise a service when it gets created. Reflection allows access to local variables in a class and the value can be declared in a script, for example. This is an advanced procedure however and so you typically mght not use it. If you do use it however, Reflection can only access the class variables from inside the classitself and so you need to add the method to each class that uses it. The default implementation of the method is as follows. You can copy/modify and paste this into your own derived class if you need to use it.


The following code example describes how this can be done.



AutoEngine Scripts

Some details about creating the AutoEngine scripts. This is an advanced and autonomous feature that might not typically be used.
 

AiConst.THIS Declaration

This relates to the actual service that the AutoEngine is running in. When you declare it in the script, you should add the type of 'Const.OBJECT', as a licas Service is a complex object and the 'THIS' reference is a reserved word. When the AutoEngine is created, the constructor is passed the parent Service as a parameter, so that it's methods can be invoked through this description. Other objects can also be added through the variables or sources sections.


Default Metafactory Scripts

This class contains two scripts for executing on the AutoEngine. One creates a full admin script for initialising and executing an email service upon request. The other executes a single method on a web service. See the 'licasAuto' user guide for more details.


The following code examples describe how this can be done.



3. Communication Examples

How to use the communication mechanism for remotely calling a service.


Default Communication Mechanism

This describes how to invoke a method on a remote object using the default XML-RPC mechanism. Parser requirements are also briefly described.

The following code examples describe how this can be done.



Invoking a Web Service

This describes how to invoke a Web Service. The default communication inside of licas is XML-RPC, but web services can be invoked dynamically by parsing WSDL documents into licas Java objects. It would probably be possible to integrate web services into a complete system, as the invocation process is the same.

The following code examples describe how this can be done.



Using the REST Interface

Very basic full code example of invoking a web service through the REST interface.

The following code examples describe how this can be done.



Aysnchronous Call with Autonomous Communication ID

This describes how an autonomous process might make asynchronous calls and include a communication ID to tell the service what stage the process is at.

The following code examples describe how this can be done.



4. Linking Examples

How to use the linking mechanism to create permanent or dynamic links.


Create a Permanent Link

Permanent links are created between two associated services and define the permanent network structure.


The following code examples describe how this can be done.



Add a Linking Service

Before you can use dynamic links, you need to add a linking service to the service that you want to add links to. The linking service stores the link values for the parent service and controls when they are created or destroyed, etc. The linking service then also needs to be initialised with appropriate values.


The following code examples describe how this can be done.



Create a Dynamic Link

Dynamic links can be used to describe the curent system use through dynamic associations. They are updated and changed through time to define what the current relevant associations between network components are.


The following code examples describe how this can be done.




Other Problem Solutions


The system is not fully commercial and can throw exceptions or output that would look unusual in a commercial product. This can, for the most part, be ignored. Also, due to the lightweight nature of the system, combined with its flexibility and functionality, some operations require a slightly innovative approach that might look unusual. The reasons behind some of these are also described.



Safe Exceptions that are Thrown

The system will automatically throw exceptions when processing certain messages. This will not cause the system to crash or affect how it operates and so these exceptions can be ignored. This is because the system checks in more than one place for methods and automatically throws an exception when it cannot match a method request to a method on the object being invoked.



Passing Strings in Remote Method Calls

The system automatically parses any message to be passed remotely into XML and then a String. You can also pass String-based content inside of any message. In that case, there can be problems depending on the format of the String. If it is an XML-based format, then parsing it inside of other XML elements can lose the formatting tags, etc. These can be replaced by special characters during XML parsing or serialization. If this is a problem, then if the String that you want to pass cannot be interpreted as XML, it will not be parsed that way. So if you want to pass an XML structure as a String and not worry about any reformatting, you can add the following command to the String just before sending it as part of a message:


sourceString = XmlHandler.addStringSpec(sourceString)

This adds some characters to the start of the String and means that it cannot then be interpreted as an XML element. You would typically add this method call at the end of the method that returns the information. For example, the InformationService's getSourceInfo() method adds this method call just before returning its information. The parser then automatically checks if these characters are present and removes them if they are. The receiving client therefore does not need to worry about this and will receive the String in its original format. The parser uses the complementary:

sourceString = XmlHandler.removeStringSpec(sourceString)

As part of its conversion process before the client or calling service would receive the information.


The Correct Order for Adding Services

There is a certain order required when adding services to the network. If you are doing this locally, that is through a direct reference, then the following order is required: To add a service through direct reference calls and then add a child service to that, the following will not work:
Service service = new Service(password, serviceKey);
...
service.addChildService(password, childService);

HttpServer.getHttpServer(serverPassword).getAutoServer( serverPassword).addService(password, service.getUUID(), service.getServiceType(), service);


This is because the call to add the child service is performed before the metadata of the first service is added to the server object. If however, you add the first service to the server and then add the second service to the first one, it will work. The code should be:

Service service = new Service(password, serviceKey);
...

HttpServer.getHttpServer(serverPassword).getAutoServer( serverPassword).addService(password, service.getUUID(), service.getServiceType(), service);

service.addChildService(password, childService);


Additional Required Fields for Communication IDs

If you are adding a communication ID to your MethodInfo object for asynchronous message calling, you need to make sure that you also add the client service address, that is, the uri of the service making the call. This can be done, either by using the MethodInfo(service_making_call) constructor, or setting it manually. If this is not set, then the called service will now know who to reply to. This address is then stored automatically in the receiving Auto-derived class, with the related communication ID.


Parsing Complex Objects with Serialized Objects

At least one serious parsing problem has been found that can be relatively easily fixed. This problem occurs when you are trying to parse more complex objects and use them in remote method calls. Consider the following example: We have a service with a method that accepts complex objects as parameters. One of these complex objects stores other complex objects as variables inside of it. For example, we could have:

class TestParameter implements Serializable;
class TestParameter2;


TestParameter2 testParameter2 = new TestParameter2();
testParameter2.listOfTestParameter.put(“key1”, new TestParameter());
testParameter2.listOfTestParameter.put(“key2”, new TestParameter());


Where 'TestParameter' uses the Java 'Serializable' interface to be deconstructed and does not have a user-written parser, but 'TestParameter2' does have a parser. The following methods might then be implemented for parsing 'TestParameter2'.

public Element serialize(Object toSerialize) throws Exception
{
    Element rootElem;
    Element nextElem;

    ...

    nextElem = XMLFactory.getInstance().createElement("TP2_List");

    //this uses the default Hashtable parser to add the complex object list
    //the object is automatically recognised as a Hashtable and its contents as
    //serializable
    nextElem.setText((String)Parsers.serialize( testParameter2.listOfTestParameter));

    rootElem.addContent(nextElem);
    ...

    return rootElem;
}

When the TestParameter2 object is serialized, the list (Hashtable) variable section might look like:

<TP2_List>
<Parameters Conversion_Type="java.util.Hashtable">
<Key Name="key1"><Parameter><Type>java.lang.String </Type>
<Value>Serialized_Objectqjh-rO0ABXNyACZvcmcubGljYXNfdGVzdC5zZXJ2aWNl
cy5UZXN0UGFyYW1ldGVyMp8ZVNLvhyfVAgACTA
AGdmFsdWUxdAASTGphdmEvbGFuZy9TdHJpbmc7TA
AGdmFsdWUydAASTGphdmEvdXRpbC9WZWN0b3I7
eHB0AA5UaGlzIGlzIHZhbHVlMXNyABBqYXZhLnV0
aWwuVmVjdG9y2Zd9W4A7rwEDAANJABFjYXBhY2l0
eUluY3JlbWVudEkADGVsZW1lbnRDb3VudFsAC2VsZ
W1lbnREYXRhdAATW0xqYXZhL2xhbmcvT2JqZWN0
O3hwAAAAAAAAAAJ1cgATW0xqYXZhLmxhbmcuT2J
qZWN0O5DOWJ8QcylsAgAAeHAAAAAKdAAWVGhp
cyBpcyBhIHZhbHVlMiB2YWx1ZXQAHFRoaXMgaXM
gYW5vdGhlciB2YWx1ZTIgdmFsdWVwcHBwcHBwcHg=

</Value></Parameter></Key>
<Key Name="key2"><Parameter><Type>java.lang.String </Type>
<Value>Serialized_Objectqjh-rO0ABXNyACZvcmcubGljYXNfdGVzdC5zZXJ2aWNlc
y5UZXN0UGFyYW1ldGVyMp8ZVNLvhyfVAgACTAAG
dmFsdWUxdAASTGphdmEvbGFuZy9TdHJpbmc7TAAGd
mFsdWUydAASTGphdmEvdXRpbC9WZWN0b3I7eHB0A
A5UaGlzIGlzIHZhbHVlMXNyABBqYXZhLnV0aWwuVmV
jdG9y2Zd9W4A7rwEDAANJABFjYXBhY2l0eUluY3JlbW
VudEkADGVsZW1lbnRDb3VudFsAC2VsZW1lbnREYXRh
dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAAAAAA
AJ1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8Qcyl
sAgAAeHAAAAAKdAAWVGhpcyBpcyBhIHZhbHVlMiB2Y
Wx1ZXQAHFRoaXMgaXMgYW5vdGhlciB2YWx1ZTIgdm
FsdWVwcHBwcHBwcHg=
</Value></Parameter></Key></Parameters>
</TP2_List>


This structure is created automatically by the licas parsing mechanism. As you can see, the 'TestParameter' objects have been serialized into Base64 code and added to the XML description, along with the hashtable key values, etc. This has been added to the complete XML description as a 'String' in the serialize() method just shown. If you then go to parse this back again, a problem can occur where the parser reads this description and converts it into XML Elements instead of the original 'String'. So the 'String' section that was added through the setText() method in the serialize method is converted back into an XML Element and not a String. This means that if you try to retrieve the text value of the element you are trying to parse, it could be null and instead you need to retrieve the child element with the name of Parameters. So to convert back into Java Objects, your parser might look like:



public Object parse(Element toParse) throws Exception
{
    Element nextElem;

    ...


    if (nextElem.getName().equals("TP2_List"))
    {
        //if this does not work
        testParameter2.listOfTestParameter = (Hashtable)Parsers.parse(nextElem.getValue());

        //then try this
        testParameter2.listOfTestParameter =
            (Hashtable)Parsers.parse(nextElem. getChild(Const.PARAMETERS));
    }
}


The text value for the element might be null if the XML-based description has been parsed back directly into XML, but you can then try to retrieve the child element instead and parse that.



The Correct Format for a Complex Object

The Parser Panel of the GUI can be used to load in parsers from separate jar files and use them to pass complex objects to a service running on a server. On the Parser Panel, from the selected jar file you can select the parser class type and the class type of the object to be parsed. This can then be added locally or to the network servers. After the parser is added, you can pass complex objects to services as parameters, because a parser for parsing them will exist. If you do this through the GUI, you can only load in a complex object as an XML file. This file however currently needs to be saved in the exact format that the parsers would parse it into. In the following example, a test service called TestService has a method that requires a parameter of type TestParameter. This is a complex object and so to test this through the GUI, the parameter needs to be retrieved from an XML file. This file can be created using something like the following code:

TestParameter testParameter = new TestParameter();
testParameter.value1 = "value_a";
testParameter.value2 = "value_b";
testParameter.value3 = "value_c";
Parsers.addParser(TestParameter.class.getName(), new TestParameterParser());
System.out.println((String)Parsers.serialize( testParameter));


This parameter serialized would then look like the following:

org.licas_xml.model.ElementImplqjh-<TestParameter Conversion_Type="org.licas_test.services.TestParameter"> <Value1>value_a</Value1><Value2>value_b</Value2>
<Value3>value_c</Value3>
</TestParameter>

Alternatively, you can use the 'Create Template' option on the Parser Panel, with the class to be parsed selected. The resulting text replaces part of the parsing process exactly and so should be added to the file in that exact format with nothing else (no XML header, etc). You can see however that once the template has been produced, you can change the values to be passed - value_a, value_b or value_c can be set to something else, for example. This has then been tested through the GUI, with the method being passed this parameter and the correct reply received. So the only really big problem is retrieving the complex object value - by loading a file, and writing the parser to parse it.