Visualizzazione post con etichetta aglets. Mostra tutti i post
Visualizzazione post con etichetta aglets. Mostra tutti i post

venerdì 16 novembre 2012

Some work on Aglets after all!

I don't remember the last time I did some work on the Aglets platform, but in the last days something have moved. Thanks to the great work by Thomas, the platform is moving to a much more easy deployment. I took the chance to do a code clean up and to fix some annoying problems with the terrible AGLETS_HOME variable and file paths that were referring to it.

mercoledì 10 agosto 2011

FLAP is now available as Open Source

A few days ago I created a repository on GitHub to host a few bits of source code: Ferrari Luca's Agent Platform (FLAP). This tiny project is a Java agent platform inspired by Aglets that I used during a course I did at the University of Modena and Reggio Emilia in late 2006. My idea was to present students with a simple agent platform, easy to understand and to debug, in order to make they understand the main concept behind concurrency, thread management and agent messaging a platform must accomplish. The platform represents also a good starting point to do experiments against agent algorithms and theories, and it is for this reason I'm releasing this as Open Source. You are free to experiment and improve the platform as much as you want, but please take into account that the platform is intended to be a didactic workbench, not a professional or complex product. The code compiles with Apache Maven, and if you run test you will actually prompted with a micro interactive shell.

The code is released under the BSD license, so you are really free to do whatever you want with it. But if you find it useful in your courses, studies, free time, please let me know.


lunedì 26 luglio 2010

Aglets 2.5

It took very much time to get to the new  alpha release of the Aglets Mobile Agent Platform. The version is the 2.5, that creates an hole between the previous one, 2.0.2, but since there are so many improvements the I decided to make it clear that this is a version that has touched almost every part of the platform.
There are important changes, the much visible of which is the new GUI that is based on Swing and no more on AWT. The GUI has not been simply converted from AWT to Swing, but it has also been restructured in order to be simpler and more friendly to use, as well as of course include the new features.
Behind the hood, the platform now supports the sleeping of an agent in a manner similar to Thread.currentThread().sleep(), and a lot of classes have been adjusted to be type safe and better organized. This means that Aglets now requires a newer version of Java, in particular at least J2SE 6. The reason for that is the adoption of a fully integrated localization system that is based on J2SE 6 Resource Bundle, wrapped in an AgletTranslator object that allows the user, as well as agents, to get localized texts and icons.

Other improvements are related to the general management of the internal data structures, that has changed from slightly to deeply depending on the case.

You can download a zip archive containing the alpha release from here.

I will thank everyone that assisted me in this long process, and I will apologize for this late releasing. 

This release makes also the old web site to become unavailable, relying instead on the default Sourceforge web content (as shown below). The reason for this is that the old site was still linking the AgentGroup @ Unimore, that didn't help me within Aglets and, in fact, copied and paster part of my work within this project releasing it as their own! It is evident if you see the papers related to Aglets: the first in the list ((Leveraging strong agent mobility for Aglets with the Mobile JikesRVM framework. Scalable Computing: Practice and Experience) contains parts of the second article (Strong Agent Mobility for Aglets based on the IBM JikesRVM) that was written before with my contribution. So to get it short, I removed the web site with all references to Unimore. The following is how the site was appearing and how it appears now.



Here I post some screenshots from the new alpha release.


lunedì 12 luglio 2010

Aglets History

Today I made a really mess with the Aglets Git repository! It happened that I pushed a not yet merged branch, because I was in a rush and my brain was outside somewhere walking...
What I was doing was converting the whole Aglets history from the old CVS repository to the Git one I created one year ago. 
Now the repository is fine again, and I've also done a few changes in some examples and classes that did not compile before.

mercoledì 14 ottobre 2009

Fixed a bug in the array class loading

Thanks to the effort of a friend of mine, Fernando, involved in the Aglets development, a bug about the class loading of an array of objects have been fixed. Today I commited the new code to the git repository.
The bug caused an agent, using an array of objects, to be not loaded well after a migration; the problem was that the Aglets class loader was unable to load the class of the array and instantiate, consequently, the array itself. This happened with a recent JVM (e.g., 1.6) and not with older ones, due to some minor changes in the new JVM array handling.

mercoledì 29 luglio 2009

New GIT repository for Aglets

After a quite long suffering with the CVS repository provided by SourceForge, I moved the Aglets code base to a GIT repository, still hosted at SourceForge (see the official news).
I haven't moved the official CVS module aglets, but instead I've integrated my own branch which includes the new threading scheme, the new GUI (based on Swing), the new message queueing and the full localization of all messages.
I hope this change will ease the development and, in the meantime, attract new developers to work on the code base. Quite surprisingly the number of project members is growing, even if the commits are not, but of course there must be time for new members to learn the code before they can work on it!
At the moment the documentation is still on the CVS repository, but I guess it will be migrated sooner or later. The CVS repository is still active, in order to allow GIT-newbies to take a look at the code without having to use GIT, but CVS will be disabled soon so I recommend using the GIT repository instead.

lunedì 23 marzo 2009

A new version of the Manual is ready for download!

After four years a new version of the Aglets User Manual is available for downloading at SourceForge.net.
Please note that we have always worked to the manual, improving and fixing the first version I wrote once I was in charge of the project itself, but before now no downloadable versions were officially available (even if the CVS repository allows public checkout).
Now, thanks to the efforts of some developers, the manual has been improved and is now available for users. Please refer to this last version of the manual.

Work on the manual has not yet finished, and we are continuously improving it.

giovedì 2 ottobre 2008

WhiteCat: injecting roles into Java agents/proxies

In the past few months I developed the prototype of a role system for Java agents that I called WhiteCat, in contrast to RoleX (also known as BlackCat).
The prototype is developed in 100% Java and is based, as its precedessor, on the Javassist bytecode manipulation library. The idea is the same as in RoleX: Java agents should be enhanced thru roles (i.e., Java third party libraries) dynamically at run-time, and must be merged with the role they assume, becoming a single entity with their roles. The aim is thus to support role external visibility, that is the capability to recognize a role played by an agent simply looking at it. Since in Java "looking" at an object and retrieving its class information, the WhiteCat approach must manipulate the agent class structure in order to make the role visible as a part of the agent itself.
For a more detailed explaination of the inspiration behind this role approach please see this article on the IBM Systems Journal.

There are a few drawbacks in the RoleX design that lead me to the design of the WhiteCat approach:
  • RoleX merges the role in the agent changing the whole agent class structure. This means that not only the bottom of the agent class inheritance chain is modified, but that each level of the agent inheritance is changed depending on the role class structure. This is a time consuming activity.
  • RoleX requires a different class loader for each role assumption/release. This means that if the same agent assumes different roles at different times, each assumption requires a different class loader (called role loader). This is resource consuming.
  • RoleX requires developers to deal with reflection and invocation translators to make injected features (i.e., role features) available. This is complex to understand and to develop/mantain.
  • RoleX provides only one kind of role, that is a publicly visible role. Each assumed role is automatically visible to external entities, and this could lead to security and privacy problems.
  • RoleX injects the role into the agent class, without taking into account that the agent could be hidden behind a proxy (see for example Aglets), and thus the role external visibility is not exploitable.
  • RoleX requires the role to be not instantiated and used by anything that is not the role loader. In other words you are not free to create a role object, since the role engine will workonly with the role classes, and thus having a role object will not provide you any meaningful capability.
  • RoleX creates different class definitions at run-time, so you could have two classes with the same name and different implementation. It can thus be difficult to deal with objects, since you could not knowor recognize which one is the original (i.e., never manipulated) one. Moreover, this way of working requires that developers discard the old (i.e., not manipulated) agent reference and use the new one provided by the role engine. Of course, there is no way to enforce this practice, so a malicious developer could keep a reference to both.

Starting from the above drawbacks I decide to design a new approach with the same base concepts, but faster, simpler and with more role visibility options. The result is the WhiteCat prototype that provides three level of role visibility:

  • invisible: the role is not visible at all from an external point of view. This means that an agent has assumed and uses a role without letting any other agent to be able to autonomously knowing it;
  • visible: the role is visible to any external entity;
  • public: the role is visible and can even be used from external entities.



Role visibility is defined thru interfaces and annotations, so that the role loader can recognize how and where to apply changes to the agent (or its proxy).

Moreover WhiteCat allows the manipulation of either the agent class structure or its proxy one. In the case the agent is hidden behind a proxy, and the role is visible or public, the proxy class structure is changed in order to make the role perceivable; in the case the role is directly accessible (i.e., there's no proxy) its class structure is directly changed in order to apply changes.



As an example consider the following application snippet:

 // role class
Class roleClass = root.loadClass("whitecat.example.DatabaseAdministrator");
DatabaseAdministrator dbaRole = (DatabaseAdministrator) roleClass.newInstance();
AgentProxy proxy = .... // get the proxy in the way your platform allows you

// get a new role loader engine
RoleEngine engine = new RoleEngine();
// add the dbarole to the current agent
AgentProxy newproxy = engine.injectPublicRole(proxy, dbaRole);

if( newproxy instanceof IDatabaseAdministrator ) {
// the proxy can work as a IDatabaseAdministrator object from here!
}




As you can see the agent proxy is simply injected with the role. Please note that a role instance is used, not a role class. This means that the role can be instantiated and initialized as you like, and can injected to the agent or its proxy when you want, and of course the role will keep its state.
Just after the injectPublicRole() call, the agent proxy is perceivable (thru instanceof) as implementing the public role interface, that in the above example is the IDatabaseAdministrator one. In order to achieve this, the agent proxy class structure is extended by the role one, meaning that it is like the agent proxy has new subclasses with the role properties. Since the agent proxy class structure is extended (i.e., all the classes up to the agent proxy are not touched), there's no need to discard the "old" object reference. If the developer does not use the new proxy reference (i.e., newproxy in the code above), it will simply not be able to exploit role capabilities, but he will be able to exploit all
original proxy capabilities.
The RoleEngine is a special class loader that performs bytecode manipulation in order to extend the agent proxy class chain. The RoleEngine exploits the Javassist library in order to manipulate the class structure, and appends
to each new class a unique number, so that the same class can be manipulated different times by the same RoleLoader as the result will have a different name and so will not clashes with other already manipulated classes. Moreover the role adoption model is simpler than in RoleX, since the developers will handle roles in straight way: once the injectPublicRole() is called, the developer could
test the agent proxy against the injected role and cast the agent proxy to such role. Last but not least, since WhiteCat does not manipulate a whole inheritance chain, rather it appends a chain to another, the approach is much faster than the RoleX one.

Of course, as shown in the figure, the manipulation process can be repeated as many times as needed (i.e., one for every role assumption).




In my opinion WhiteCat represents a very enhanced role approach than RoleX, and even if still a prototype, it is enough stable to be used in role based applications.
I'm still working on WhiteCat, and I will release it under the GNU GPLv3 license. If you are interested in WhiteCat and want to know more details or discuss with me, please e-mail me (fluca1978 at gmail.com) and I will be happy to provide more information.
I'd like to thanks Mr. Shigeru Chiba and the JBoss team and community for their work and support on Javassist.

martedì 18 marzo 2008

The Aglets GUI

At the end of the last year I finished committing a set of changes to the Aglets GUI (to be precise the Tahiti GUI) so that it is now more reach, supports Swing (the original 2.0.2 was written in AWT). The followings are a few screenshots I took on my laptop.

The dialog for the login is now more funny!


The main window provides now more information to the user, like the number of active threads in the pool and the memory situation. The list of the agent now appears different depending on the state of the agent (running, active, suspended, ...).



Just a refactoring of the create dialog and of the security one....


Other windows and widget have been adapted similarly. Some of them have been enhanced, while others have been simplified removing no more necessary options (for instance the settings of the preferences dialog). I hope the new GUI will be merged in the trunk as soon as possible.

The Aglets MessageQueue

Each agent stores incoming messages into a message queue (com.ibm.aglets.MessageQueue); the message insertion into the message queue is transparently done by the delivering of a message.

The idea is that a message manager creates its own message queue and uses it to store message before it has activate a thread to process it. The use of a message queue allows the uncoupling of agent threads. In fact when an agent sends a message to another agent, the sending thread thru several method calls inserts the message in the message queue, and thus the sender thread returns. Thus the sender thread is involved until the message has been inserted in the message queue. Once this is done, it is the addressee thread in charge of processing the message itself, and this is done by the message manager that gets a thread and pops a message from the message queue.

The following is the implementation of the MessageQueue in the current development tree. As readers can see, the queue exploits a simple List to store messages that must be processed. All the methods implements the appropriate logic (e.g., priority) working around such list. Please note that the message queue is parametrizable thru Java Generics.

public class MessageQueue {


/**
* The messages will be stored into a linked list.
*/
private List messages = new LinkedList();



/**
* Appends a message, that is places the message at the end of the queue.
* @param msg the message to append
*/
public synchronized void append(MSG msg){
// check params
if( msg == null )
return;

// append the message at the tail
this.messages.add(msg);
}

/**
* Inserts a new message in the queue. The alghoritm is the following:
* each message is extracted from the queue and analyzed; at the first message with
* a priority less than the one we want to insert, the message is inserted. If no one
* message is found, than the message is placed at the tail of the queue.
* @param msg the message to insert
*/
public synchronized void insert(MSG msg){
// check params
if( msg == null )
return;


Iterator iter = this.messages.iterator();

while (iter != null && iter.hasNext()) {
MSG currentMessage = (MSG) iter.next();
if( currentMessage.getPriority() < msg.getPriority() ){
int index = this.messages.indexOf(currentMessage);
this.messages.add(index, msg);
return;
}
}
// if here the message must be placed at the tail of the queue
this.messages.add(msg);
}

/**
* Inserts the message at the top of the queue. Please note that this method will
* overtake the priority mechanism, thus it is possible to insert at the top a message
* with a low priority.
* @param top the message to place at the head of the list
*/
public synchronized void insertAtTop(MSG top){
if( top == null ) return;
// place the message at the head of the queue
this.messages.add(0, top);
}

public synchronized void insertAtTop(MessageQueue queue) {
if( queue == null || queue.isEmpty() )
return;
else{
this.messages.addAll(0, queue.messages);
}
}


public int size(){
return this.messages.size();
}

public boolean isEmpty(){
return this.messages.isEmpty();
}

/**
* Gets (but don't remove) the message at the top of the queue.
* @return the message at the top.
*/
public synchronized MSG peek(){
if( ! this.messages.isEmpty() )
return this.messages.get(0);
else
return null;
}


/**
* Extract (i.e., remove) the message at the top of the queue.
* @return the message at the top or null
*/
public synchronized MSG pop(){
if( this.messages.isEmpty() )
return null;
else
return this.messages.remove(0);
}



/**
* Removes a message from the queue.
* @param msg the message to remove
* @return true if the message has been removed, false otherwise
*/
public synchronized boolean remove(MSG msg){
if( this.messages.contains(msg) ){
this.messages.remove(msg);
return true;
}
else
return false;
}

/**
* Removes all the messages from the queue, that is after this the queue will be
* empty.
*
*/
public synchronized void removeAll(){
if( ! this.messages.isEmpty() )
this.messages.clear();
}


public String toString() {
StringBuffer buffer = new StringBuffer(50 * this.messages.size() );

buffer.append("The message queue contains " + this.messages.size() + " messages");
for(int i=0; i< this.messages.size(); i++){
buffer.append("\n");
for(int j=i; j>0; j--) // make a few indentation spaces
buffer.append(" ");

buffer.append("message " + i +")" + this.messages.get(i));
}

return buffer.toString();
}
}

The Aglets threading scheme

The Aglets platform is a multi-threaded platform where a thread can serve one agent depending on the actions it must perform during its life. The thread used by Aglets is implemented in the AgletThread class of the com.ibm.aglets.thread package.

Please note that each thread is activated depending on the delivery of one (or more) messages to an agent. Once a message is delivered, the addressee agent must process it and so a thread is woken up and used to handle the message thru the addressee agent.


Once an agent must receive/process a message, a thread is activated and used to process such message. In other words, the handleMessage() method of an aglet is run on top of one AgletThread.
Please note that the thread management involves the message manager and the message itself, since the steps are the following:
1) a message is delivered from a sender to the destination agent. The addressee agent stores the message into a message queue, hold from the message manager.
2) the message manager, once one or more thread have been stored in the queue, pops a thread and starts processing such threads one by one. In particular:
a) the message is passed to the thread;
b) the thread invokes the handle() method on the message itself
c) the message invokes the handleMessage() on the aglet, passing itself as argument
d) the thread exits the monitor, that is informs the message manager that the above message has been delivered. The message manager processes another message or leaves the thread. In the first case there is a chain that produces that until the message queue is empty the thread is hold to process messages. In the second case the thread is free, and the message manager waits until a new message comes.

In the 2.0.2 schema each agent has its own threadSpool, that is a stack of threads used to manage only messages related to the owner agent. Once the thread has delivered the message, it is pushed back into such stack (that is contained in the message manager).

In the 2.1.0 under development the schema is different: there is a thread pool that, globally, provides threads for the whole messaging system. Thus the message manager does not handle any more a private stack of threads but requires them to the pool. Once the thread has delivered the message and no more messages must be processed for this agent, the message manager pushes it back in the thread pool This allows a thread to be used for different agents at different times. Please note that this implies that a thread must know not only the message it is going to process, but also the message manager that oredered that, for coherence.
The thread has also two ways of locking depending on its state:
_ processing = it is processing a message, thus it cannot receive changes about the message itself or the message manager;
_ changing = it is changing either the message manager or the message to process and thus cannot process it.

Please note that the message manager will push and pop the thread again when it process a next message, this can bring to situations where the next message is processed by a different thread and, in general, wastes a little resources. Maybe this will be fixed in the future.

It is important to note that when a thread is woken up to handle a message, it is assigned to a specific MessageManager, that is an handler that owns messages for a specific agent (thru a message queue), as well as an agent reference. So when the thread re-start its execution, it knows exactly the message manager from which it can obtain the message and the agent. The message is already directly available to the agent, so that the thread can directly process the message. Processing the message means that the handle(..) method of the MessageImpl object is invoked, that will call consequently the handleMessage(..) method on the agent itself.

More in detail with regard to the MessageManager: the message manager is the decoupling point between a sender thread and a receiver one. In fact, when an agent sends a message to another agent, it comes up to the message manager of the addressee agent and enters the postMessage method. Such method is quite complex, but briefly stores the message into the addressee message queue and then notifies the message manager itself that are at least one new pending message. The addressee message manager then pops a thread from the thread pool and then processes the message at the top of the queue (and all the following ones) until the queue is empty. After that the message managers waits for other messages to come.

Once the thread has processed the message, it searches to process a new message (pushThreadAndExitMonitorIfOwner(..)). Please note that the MessageManager implementation (MessageManagerImpl) has the concept of owner: a message that has just been picked from the message queue or that has been just processed by a thread. So, if the thread was processing the message owner (i.e., the message for which the thread has been waked up) a new message to process is searched. If no new (or remaining) messages are present in the message queue, the thread is forced to suspend itself (and to return to the thread pool).
In the special case of re-entrant message (a message that issued a new message to process, as for instance in a request-response protocol) a new message is popped from the message queue and processed; in the case no remaining messages are available (this should not be the case of a re-entrant message) the thread suspends itself as above.
public void run() {
// if the loop of handing messages is already started return, so thus
// no more than one run call can be done.
if (loop_started) {
// to assure that aglet cannot call run on this thread.
return;
}

// set this thread as "started to handle messages"
loop_started = true;
start = false;

// get the reference of the agent behind the message manager
if( this.messageManager == null )
return; // the message manager is not valid!

try {
while (valid) {
try {

logger.debug("AgletThread is starting processing");
this.setReentrant(false); // if the process is here and is re-entrant now I'm processing
// a re-entrant message, thus after this I have to suspend myself.
this.setProcessing(true);
// get the right reference to the aglet behind the current
// message manager. This must be done each time in the cycle because
// the thread could be suspended or the message manager could be changed
// if the thread has passed thru the pool.
MessageManagerImpl manager = this.getMessageManager();
logger.debug("The message manager is " + manager + ", the message is " + message);
LocalAgletRef ref = manager.getAgletRef();
message.handle(ref); // handle the message
this.messageHandled++; // increment the number of messages handled by this thread

synchronized(this){
if( ! this.isReentrant() ){
message = null; // invalidate the message so to not repeat the handling
logger.debug("AgletThread has invalidate the message just processed (no reentrant find!)");
}
}

this.setProcessing(false);
logger.debug("AgletThread finished processing a message");

} catch (RuntimeException ex) {
logger.error("Exception caught while processing a message", ex);
valid = false;
throw ex;
} catch (Error ex) {
logger.error("Error caught while processing a message");
valid = false;
throw ex;
} catch (InvalidAgletException ex) {
logger.error("Exception caught while processing a message", ex);
valid = false;
start = true;
} finally {

// if the thread is valid, i.e., it has not been stopped
// then invoke special methods on the message manager to process
// another message (thus once the thread has been activated all messages are processed)
// or to process another message (if present) and to push back the thread in the pool.
if (valid && (! this.isReentrant())) {
// push the thread back into the pool...
logger.debug("The thread is going to be pushed back in the pool...");
messageManager.pushThreadAndExitMonitorIfOwner(this);
} else {
// process one more message...
messageManager.exitMonitorIfOwner();
}
}

// here the message has been processed, thus I can suspend myself
// waiting for a new message to process
synchronized (this) {

while (valid && this.message == null && (! this.isReentrant())) {
try {
logger.debug("Thread suspending waiting for a next message...");
this.wait();
} catch (InterruptedException ex) {
logger.error("Exception caught while waiting for an incoming message", ex);
}
}

}

}
}
finally {
message = null;
}
}
}


The AgletsTranslator: considerations about the adoption of a ResourceBundle

The Java library provides the java.util.ResourceBundle class that can be used to localize within a Java run-time. Localizing means that each kind of resource can be adapted to the execution environment, in order to present to the users a more comprehensive resource. The simplest example is that of strings embedded in a program, that can be translated into the native language of the final users.

Since mobile agents can run on very different hosts, it is important that they can provide to the final users a localized version of messages and other resources (e.g., icons, images). In this brief post I present my implementation of the org.aglets.util.AgletTranslator class, that can be used both from agents and the platform to translate resources (mainly string messages).

The AgletsTranslator is a wrapper around the ResourceBundle that provides simplified services for the localization. Let's start considering the class implementation base:

public class AgletsTranslator implements Cloneable {

/**
* The resource bundle used to handle locale content.
*/
private transient ResourceBundle bundle = null;
public String translate(String text){
// be sure there is something to translate and I have a bundle to
// ask for translation
if( text != null && text.length() > 0
&& this.bundle != null
&& this.bundle.containsKey(text)){
String translated = null;
translated = this.bundle.getString(text);
logger.translation(text, translated);
return translated;
}
else
// nothing to do, return the string passed as argument
return text;
}

...

}
As readers can see, the class wraps an instance of a ResourceBundle, and then provides the translate(String) method that translates the text. The latter method works as follows: if the resource bundle contains the key (i.e., an identifier of the text that must be translated), then the returned text is provided by the resource bundle (and thus is a translated text). If the bundle does not contain the key (i.e., cannot provide a translation for the specified text), then the untraslated text is provided. This means that, if the key passed as a string has not been included in the translation configuration, then this text will be returned unmodified. This allows, in any moment, an agent or a developer can try to translate a string without worring too much about its availability in the translation system. This also means that a developer can use always the translate(..) method without worrying about the translation dictionary, that can become available even later the end of the development. So, the adoption of the translate(..) method for issuing strings is a good practice.

The AgletsTranslator is constructed knowing a Locale (i.e., an object that represents the configuration of the hosting platform) and a basename. A basename represent a configuration point for a resource bundle. The basename can be either a class name (fully qualified name) that provides a translation by its own, or a file name (e.g., a property file) that is within the classpath. In the case of the AgletsTranslator, the platform uses a property file called tahiti.properties and placed within the lib directory. The file contains a dictionary with entries like the followings:

com.ibm.aglets.tahiti.LoginDialog.usernameLabel = Username:
com.ibm.aglets.tahiti.LoginDialog.passwordLabel = Password:
com.ibm.aglets.tahiti.LoginDialog.okButton = Log in
com.ibm.aglets.tahiti.LoginDialog.okButton.tooltip = Performs the log-in into Aglets
com.ibm.aglets.tahiti.LoginDialog.okButton.icon = img/ok.png


It is possible to see the key (e.g., com.ibm.aglets.tahiti.LoginDialog.usernameLabel) and the appropriate translation (e.g., Username:). Please note that a resource bundle can be used also for icons and images, not only for text, as already stated before.
The convention of using the fully qualified class name as part of the key is just a good practice that allows a quick individuation of where a key is used in the application itself.

Having obtained an AgletsTranslator its use is quite simple:

AgletsTranslator translator = ...;
String translated = translator.translate("com.ibm.aglets.tahiti.LoginDialog.usernameLabel");
Icon icon = new ImageIcon( translator.translate("com.ibm.aglets.tahiti.LoginDialog.okButton.icon") );




How can a translator be obtained? The AgletsTranslator class provides a factory method to obtain a new instance:

public static AgletsTranslator getInstance(String localeBaseName, Locale currentLocale) {
String key = localeBaseName + currentLocale.toString();

// search first in the cache
if( translators.containsKey(key) )
return translators.get(key);
else{
AgletsTranslator translator = new AgletsTranslator(localeBaseName, currentLocale);
translators.put(key, translator);
return translator;
}

}

private static HashMap translators = new HashMap();




As readers can see the factory method searches first for an already constructed translated for the specified basename and locale. The adoption of such caching technique provides the same translator for the same code base, keeping the creation of translators under control and a lower memory footprint. In case the translator has not been created, it is created and a reference to it is stored in the translator map for caching.