As promised in the
second part, this article is devoted exclusively to practical examples demonstrating the features of the interaction of Caché + Java + Flex. Let's start with data type conversion throughout the bundle. We did not focus on conversions of such data types as Integer, Float, String, Boolean, since these types are identical in Caché and Java (and in ActionScript all numeric types are converted to Number). Another thing is the collection of objects. Their transformation takes place rather nontrivially, therefore we will dwell on them in more detail.
Collection conversion

"
Consider the transformation of collections throughout the entire bunch of Caché + Java + Flex on the example of a list of disciplines in a particular cycle. As an example of a collection, consider relations — a type of relationship between Caché classes, implemented as properties of the Relationship type, with a certain kind of integrity support.
Read more about relationships in Caché .
Class IKIT.cCicl Extends %Persistent { … Relationship ListOfDisc As IKIT.cDiscipline [Cardinality = many, Inverse = Cicl]; … }
In the generated java projection (package “Java c-classes”) this property is represented by an object of the class com.intersys.classes.RelationshipObject from the library cachedb.jar.
public com.intersys.classes.RelationshipObject getListOfDisc() throws com.intersys.objects.CacheException { com.intersys.cache.Dataholder dh = mInternal.getProperty("ListOfDisc", true); com.intersys.cache.CacheObject cobj = dh.getCacheObject(); if (cobj == null) return null; return (com.intersys.classes.RelationshipObject)(cobj.newJavaInstance()); }
This class has a standard asList method that accepts RelationshipObject and returns a List, which makes it much easier to work with the list in the future.
Sheets are used in m-classes (List listOfDisc), so you must convert each c-classes object from RelationshipObject to its corresponding m-classes object. This action takes place in the CacheTransform class.
for (int i=0;i<cur.getListOfDisc().size();i++) { res.getListOfDisc(). add(TranformDi((cDiscipline)cur.getListOfDisc().asList().get(i))); }
Next, the m-classes are projected into the flex module according to the parameters specified in the POM file for GraniteDS. GraniteDS, by default, uses the corresponding ListCollectionView type (in ActionScript) for the List type projection (in Java). For example, the projection of the list in the generated mCiclBase AS class would look like this.
[Bindable] public class mCiclBase implements IExternalizable { … protected var _listOfDisc:ListCollectionView; … public function set listOfDisc (value: ListCollectionView):void { _listOfDisc = value; } public function get listOfDisc():ListCollectionView { return _listOfDisc; } … }
To further simplify work directly in ActionScript, we used the ArrayCollection class, which is a class derived from ListCollectionView and implements the standard Ilist flex interface (cur.listOfDisc = new ArrayCollection ();), which if necessary can be easily converted as ListCollectionView.
The inverse transform follows the same pattern. The changes that occur in the AS-projection through GraniteDS are also performed in the corresponding classes of the “Java m-classes” package. The conversion of collection objects from java m- to java c- classes takes place in the CacheTransform class in functions with the Rev prefix. In our case, in the function RevTranformCi (mCicl cur, Integer act), which takes an object of the m-class and an action (rewriting or creating), writes the object to the database and returns the object of the c-class. Sheet conversion occurs in this function as follows.
for (int i=0;i<cur.getListOfDisc().size();i++) { if(cur.getListOfDisc().get(i).getId()!=null) { res.getListOfDisc().add(RevTranformDi(cur.getListOfDisc().get(i),1)); } if(cur.getListOfDisc().get(i).getId()==null) { res.getListOfDisc().add(RevTranformDi(cur.getListOfDisc().get(i),2)); } }
After that, the standard _save method of the c-classes object is called, which saves the data to the Caché database, while casting the List type to the appropriate type in Caché com.intersys.classes.RelationshipObject.
Now that everything is clear with the collections, let's move on to an example that demonstrates adding and deleting objects.
An example of adding and deleting an object
Consider working with the services interface on the example of adding and deleting a curriculum. The first is the “curriculum” object in the AS-projection of the mCurriculum class. The creation of this object is performed on the client side, then it is transferred to the server and stored in the database. To do this, call the addOneCurr method through the object that implements the server services interface.
userService.addOneCurr(cur, k);
where ur is an object of the mCurriculum projection class created in flex, and k is a variable responsible for the choice of action (adding or editing).
')
Here it must be remembered that GraniteDS sends requests asynchronously and, if it is necessary to perform any action strictly after the function completes, it must be placed directly into the result handler function. For example: userService.addOneCurr ( cur, k, function(e:TideResultEvent):void { Alert.show(" "); updateOneCur(curCurriculum.id); }, function (e:TideFaultEvent):void { Alert.show(e.fault.faultDetail); } );
This function will display the “Add completed” message strictly after the successful completion of the addOneCurr function.Below is the code for the addOneCurr function in the projection class on the ActionScript interface IUserService generated by GraniteDS.
public function addOneCurr(arg0:mCurriculum, arg1:Number, resultHandler:Object = null, faultHandler:Function = null):void { if (faultHandler != null) callProperty("addOneCurr", arg0, arg1, resultHandler, faultHandler); else if (resultHandler is Function || resultHandler is ITideResponder) callProperty("addOneCurr", arg0, arg1, resultHandler); else if (resultHandler == null) callProperty("addOneCurr", arg0, arg1); else throw new Error("Illegal argument to remote call (last argument should be Function or ITideResponder): " + resultHandler); }
The same function in the original java class UserService, which implements the IUserService interface.
@Override public Boolean addOneCurr(mCurriculum cur,Integer k){ objT.RevTranformCu(cur,k); return true; }
This function calls the RevTranformCu (mCurriculum cur, Integer act) method of the CacheTransform class to convert an object of the mCurriculum class to an object of the cCurriculum class and write it to the database. In addition, the function addOneCurr "cascade" causes the preservation of objects included in the collection, processed curriculum.
As you can see from the example, the method consistently saves all the objects from the lists of semesters, cycles and logs using the corresponding methods RevTranformSe, RevTranformCi, RevTranformLo. In this case, the principle remains the same: if id is missing, then a new object is created, otherwise an existing one is edited.
Similarly proceeds and the removal of the curriculum. To delete, it is enough to receive the object id of the projection class mCurriculum and transfer it to the server The delOneCurr function in the AS is responsible for this by the projection of the IUserService interface function.
userService.delOneCurr ( curId, function (e:TideResultEvent):void { loadCur(0); }, function (e:TideFaultEvent):void { Alert.show(e.fault.faultDetail); } );
The same function in the original java class UserService, which implements the IUserService interface.
@Override public void delOneCurr(Integer i) { objT.deleteOneCurr(i); }
The delOneCurr function calls the deleteOneCurr (Integer dd) method of the CacheTransform class, designed to remove the cCurriculum object from the database. In addition, this function cascadingly causes deletion of objects included in the lists of the curriculum to be deleted.
The deleteOneCurr method. public void deleteOneCurr(Integer dd) { try { System.out.println("//MAS: TRY DELETE Curriculum"); cCurriculum cur; cur=(cCurriculum) cCurriculum._open(dbconnection, new Id(dd)); if(cur.getListOfCicl()!=null){ for(int i=0;i<cur.getListOfCicl().size();i++) { cCicl k=(cCicl)cur.getListOfCicl().asList().get(i); deleteOneCicl(Integer.parseInt(k.getId().toString())); } } if(cur.getListOfSemestr()!=null) { for(int i=0;i<cur.getListOfSemestr().size();i++) { cSemestr k = (cSemestr)cur.getListOfSemestr().asList().get(i); deleteOneSeme(Integer.parseInt(k.getId().toString())); } } if(cur.getListOfLogs()!=null) { for(int i=0;i<cur.getListOfLogs().size();i++) { cLogs k=(cLogs)cur.getListOfLogs().asList().get(i); deleteOneLog(Integer.parseInt(k.getId().toString())); } } cur._close(); cCurriculum._deleteId(dbconnection, new Id(dd)); System.out.println("//MAS: DELETE Complite"); } catch (CacheException e) { e.printStackTrace(); } }
As can be seen from the example, the method sequentially performs the removal of all objects from the lists of semesters, cycles and logs by the corresponding methods deleteOneSeme, deleteOneCicl, deleteOneLog.
A few words about the "production"
Since Cache-projections fully support the principles of OOP, adding a field or changing a type in the Cache-class is equivalent to the corresponding operation directly in Java.
More specifically, the use of projections and duplication of classes makes it relatively easy to make changes to a project when adding a new field or changing the data type in Cache.
Such a solution is easy to maintain, the main requirement that needs to be fulfilled is to follow the notation when naming classes, properties and methods.
With an increase in the number of classes, the code grows in direct proportion, and if you try to unify the Tranform functions, it is many times smaller.
At this stage, when making changes or adding a new class, you must:
- re-generate projection classes and replace existing ones (or add new ones);
- make appropriate changes to the m-classes (since the principles of the PLO are implemented, such changes will not be difficult);
- make changes to the controller (unification can be avoided).
Conclusion
Considering all the pros and cons of the
presented combination and the proposed approach , it is worth considering the further development of the system in the direction of agent technologies.
Since
JADE was chosen as the platform, it is natural that it imposes a number of restrictions on the design of the architecture of the entire system. Since JADE itself is written in Java and is a cross-platform system running under a virtual machine, the logic of the developed MAS is also built in this environment. The main unit (element) of JADE is the agent, which, in essence, is an object of the java-class. All the logic of the functioning of this agent is also implemented in this class. It is convenient to implement long-term storage of data for each agent in an object-oriented database, since the agent himself assumes the presence of an intellectual component in it, which can be based on semantic networks and ontologies. Ontologies, in turn, are stored data and inherit (so to speak) the object-oriented approach. Thus, the most convenient DBMS for the implementation of this system is object-oriented and provides the ability to work with it through java-classes. DBMS Caché, in our opinion, is a good candidate for this task.
At this stage, it is difficult to assess the effectiveness of using Flex technology as a web interface, since the Caché-Java bundle provides a fairly wide choice for further development.
The disadvantages of the proposed architecture include duplication of java-projections in the “Java m-classes” package and implementation of additional functions in the CacheTransform class, which provide synchronization of objects of java-projection classes and m-classes. In the future, this problem can be solved by refining the mechanism for generating java-projections in Caché, which can take into account the additional specifics determined by external applications used by java-projections. For example, additional requirements for the generation of projections can be described in a separate xml file, using which the generation mechanism will create the correct java projections. Then the need for duplicate classes of java-projections will disappear.
From the authors
We hope that you were interested in reading this article, and you were able to find something useful for yourself. We will certainly continue to work on the project (including work on its optimization), therefore it is possible that this series of articles will be expanded in the future.
Now we have considered only one of the modules of the developed multi-agent system, and our main goal was to acquaint the reader with a bunch of Caché + Java + Flex. At the same time, we left unsolved the question of the use of agents. Therefore, for those who are interested in this topic, we suggest to get acquainted with some explanations under the spoiler.
Agent and Micro AgentThese project components are necessary for further expansion of the functionality in accordance with the specific requirements of the system. Currently, only a web application is connected with an agent on the server and calls a number of its functions. This will further help in the development of a system built on the interaction of agents in the JADE environment, where the presented project is only one of the visual modules of the agent. The microagent, in fact, is an auxiliary component to facilitate the work with the multiagent system of users. Each user is provided with his own agent through which he interacts with other agents of the system. To ensure constant communication of agents with the user, a micro agent is used that is installed on the user's computer. The microagent informs the user about the events and received messages transmitted by the main agent. It also provides the launch of the application on the client (a browser that displays web pages), allowing the user to implement a dialogue with his agent.
We demonstrate the function that launches a new container and an agent on the JADE platform when the web server is started.
public boolean StartAgent() { String aName = "ZavKaf Agent - "; String aClass = "agents.ZavCafAgent"; rt= Runtime.instance(); p=new ProfileImpl(); p.setParameter("container-name","ZavKaf_Agent"); mainCont=rt.createAgentContainer(p); try { Object[] temp=new Object[1]; temp[0]=testSoul; ac=mainCont.createNewAgent(aName, aClass, temp); ac.start(); agSt=true; System.out.println("Agent Start"); return true; } catch (Exception ex) { testSoul.setA(null); System.out.println("Agents ERROR: " + ex); return false; } }
Below is a call to one of the modules of the agent responsible for the functional design of the curriculum.
public mAlgRes doAlgorithm(List<mDiscipline> curL, mCurriculum curriclum, List<mControlForm> cf) { return Agents.getA().doAlg(curL,curriclum,cf); }
Here Agents is a class object that stores data about active agents and agents associated with the current web application.