Spis treści:

Przykłady adaptacji komponentów zewnętrznych.

Zamieszczone tutaj dwa przykłady (propertiesxml i propertiesxml2) prezentują w jaki sposób można wykorzystać adaptowanie zewnętrznych komponentów na obecnym etapie. Przykłady dostępne są w pod adresem https://herman.iisg.agh.edu.pl/svn/jage/trunk/examples/.Uruchamia się je analogicznie do aplikacji Hello World wykorzystując pliki uruchomieniowe Eclipse'a propertiesxml.launch i propertiesxml2.launch dostępne w projekcie applications w katalogu src/main/config.

Niestety nie jest możliwe stworzenie pseudo-agenta niezalężnego od jAgE'a, którego można by zaadoptować.
Dlatego w przygotowanych przykładach stworzeni zostali agenci, którzy spełniają warunki narzucone przez platformę, ale korzystają z adaptowanych zewnętrznych komponentów.

Przykład 1

W tym przykładzie tworzone jest środowisko obliczeniowe z trzema agentami Scully, Mulder i Skinner.
Wszyscy są zagregowani w TheXFiles.
Do każdego z agentów przypisany jest obiekt komponentu SimpleFunctionCounter potrafiący obliczać sumę kwadratów dwóch liczb.
Obiekty te są przy starcie inicjalizowane wartościami (x i y) podanymi w pliku konfiguracyjnym środowiska obliczeniowego.
Przy każdym wywołaniem metody liczącej obie zmienne są inkrementowane.

W każdej turze agent przy pomocy swojego obiektu komponentu oblicza wartość funkcji, a wynik obliczeń jest logowany.
Dodatkowo co 3 tury agent "rozgląda" się w swoim środowisku i informuje jakich agentów "widzi".

Wynik działania

W wyniku uruchomienia przykładu na konsoli powinno się pojawić coś podobnego do:

[main                                              ][INFO ][PropertiesSimpleAgent                             ] Constructing
[main                                              ][INFO ][PropertiesSimpleAgent                             ] Constructing
[main                                              ][INFO ][PropertiesSimpleAgent                             ] Constructing
[main                                              ][INFO ][SimpleAggregate                                   ] Agent Scully@ added to the aggregate TheXFiles@
[main                                              ][INFO ][SimpleAggregate                                   ] Agent Mulder@ added to the aggregate TheXFiles@
[main                                              ][INFO ][SimpleAggregate                                   ] Agent Skinner@ added to the aggregate TheXFiles@
[main                                              ][INFO ][SimpleAggregate                                   ] Agents added.
[main                                              ][INFO ][PropertiesSimpleAgent                             ] Initializing agent: Skinner@
[main                                              ][INFO ][PropertiesSimpleAgent                             ] Initializing agent: Mulder@
[main                                              ][INFO ][PropertiesSimpleAgent                             ] Initializing agent: Scully@
[main                                              ][INFO ][PicoWorkplaceManager                              ] Workplace TheXFiles@ initialized
[main                                              ][INFO ][SimpleWorkplace                                   ] TheXFiles@ is starting...
[main                                              ][INFO ][PicoWorkplaceManager                              ] Workplace TheXFiles@ started
[TheXFiles@                                        ][INFO ][IsolatedSimpleWorkplace                           ] TheXFiles@ has been started. Step to go: 10
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent Skinner@: step 1
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Skinner@ counted function. The result for 100^2 + 200^2 is: 50000
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent Mulder@: step 1
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Mulder@ counted function. The result for 10^2 + 20^2 is: 500
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Mulder@ can see in its environment: TheXFiles@ following agents:
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]    agent: Skinner@ with properties:
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       address: Skinner@
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       functionCounter: 101^2 + 201^2
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       actor: Mitch Pileggi
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]    agent: Scully@ with properties:
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       address: Scully@
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       functionCounter: 1^2 + 2^2
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       actor: Gillian Anderson
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent Scully@: step 1
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Scully@ counted function. The result for 1^2 + 2^2 is: 5
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Scully@ can see in its environment: TheXFiles@ following agents:
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]    agent: Skinner@ with properties:
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       address: Skinner@
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       functionCounter: 101^2 + 201^2
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       actor: Mitch Pileggi
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]    agent: Mulder@ with properties:
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       address: Mulder@
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       functionCounter: 11^2 + 21^2
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ]       actor: David Duchovny
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent Skinner@: step 2
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Skinner@ counted function. The result for 101^2 + 201^2 is: 50602
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent Mulder@: step 2
[TheXFiles@                                        ][INFO ][PropertiesSimpleAgent                             ] Agent: Mulder@ counted function. The result for 11^2 + 21^2 is: 562

Konfiguracja środowiska

Konfiguracja aplikacji znajduje się w projekcie applications-examples w pliku: src/main/resources/examples/properties/xml/workplace.xml. Plik ten definiuje strukturę lokalnego środowiska obliczeniowego. Poniżej przedstawiono jego zawartość:

<configuration>
    <object name="workplace" class="org.jage.workplace.IsolatedSimpleWorkplace">
        <property name="address">
            <value class="AgentAddress" value="TheXFiles" />
        </property>
        <property name="endConditionStepsCnt">
            <value class="Integer" value="10" />
        </property>
        <object name="functionCounter1" class="org.jage.examples.properties.xml.SimpleFunctionCounter">
            <property name="xParameter">
                <value class="Integer" value="1" />
            </property>
            <property name="yParameter">
                <value class="Integer" value="2" />
            </property>
        </object>
        <object name="functionCounter2" class="org.jage.examples.properties.xml.SimpleFunctionCounter">
            <property name="xParameter">
                <value class="Integer" value="10" />
            </property>
            <property name="yParameter">
                <value class="Integer" value="20" />
            </property>
        </object>
        <object name="functionCounter3" class="org.jage.examples.properties.xml.SimpleFunctionCounter">
            <property name="xParameter">
                <value class="Integer" value="100" />
            </property>
            <property name="yParameter">
                <value class="Integer" value="200" />
            </property>
        </object>
        <list name="agents">
            <object name="scully" class="org.jage.examples.properties.xml.PropertiesSimpleAgent">
                <property name="address">
                    <value class="AgentAddress" value="Scully" />
                </property>
                <property name="actor">
                    <value class="String" value="Gillian Anderson" />
                </property>
                <property name="functionCounter">
                    <reference target="functionCounter1" />
                </property>
            </object>
            <reference target="scully" />
            <object name="mulder" class="org.jage.examples.properties.xml.PropertiesSimpleAgent">
                <property name="address">
                    <value class="AgentAddress" value="Mulder" />
                </property>
                <property name="actor">
                    <value class="String" value="David Duchovny" />
                </property>
                <property name="functionCounter">
                    <reference target="functionCounter2" />
                </property>
            </object>
            <reference target="mulder" />
            <object name="skinner" class="org.jage.examples.properties.xml.PropertiesSimpleAgent">
                <property name="address">
                    <value class="AgentAddress" value="Skinner" />
                </property>
                <property name="actor">
                    <value class="String" value="Mitch Pileggi" />
                </property>
                <property name="functionCounter">
                    <reference target="functionCounter3" />
                </property>
            </object>
            <reference target="skinner" />
        </list>
        <property name="agents">
            <reference target="agents" />
        </property>
    </object>
</configuration>

Należy zwrócić uwagę, że w konfiguracji zawarta jest również konfiguracja komponentów obliczających funkcję.

Implementacja agenta

Implementacja agenta jest właściwie standardową implementacją, dlatego część kodu została pominięta poniżej.
Ważne są elementy związane z SimpleFunctionCounter. Plik źródłowy znajduje się w projekcie solutions-example w katalogu src/main/java/org/jage/examples/properties/xml.

public class PropertiesSimpleAgent extends SimpleAgent {
	public PropertiesSimpleAgent() {}

	public PropertiesSimpleAgent(IAgentAddress address) {}

	public void init() {}

	@PropertyField(propertyName = "actor")
	private String actor = null;

	private transient int counter = 0;

	private SimpleFunctionCounter functionCounter;

	public void step() {
		counter++;
		_log.info("Agent " + getAddress() + ": step " + counter);
		_log.info("Agent: " + getAddress() + " counted function. The result for " + functionCounter.toString()
				+ " is: " + functionCounter.countSquareSum());
		if ((counter + this.hashCode()) % 3 == 0) {
			watch();
		}

		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			_log.error("Interrupted", e);
		}
	}

	private void watch() {
		final IQueryResult answer;
		try {
			answer = this.queryEnvironment(Query.EMPTY);
			_log.info("Agent: " + getAddress() + " can see in its environment: " + getParentAddress()
					+ " following agents:");
			for (IQueryResult.Entry entry : answer.entries()) {
				IAgentAddress address = entry.getAddress();
				if (address != getAddress()) {
					_log.info("   agent: " + address + " with properties:");
					IPropertiesSet properties = entry.getProperties();
					for (Property property : properties) {
						_log.info("      " + property.getMetaProperty().getName() + ": " + property.getValue());
					}
				}
			}
		} catch (AgentException ae) {
			_log.error("Can't query environment.", ae);
		}
	}

	public void finish() {}

	@PropertyGetter(propertyName = "functionCounter", isMonitorable = true)
	public SimpleFunctionCounter getFunctionCounter() {
		return functionCounter;
	}

	@PropertySetter(propertyName = "functionCounter", type = SimpleFunctionCounter.class)
	public void setFunctionCounter(SimpleFunctionCounter functionCounter) throws Exception {
		this.functionCounter = functionCounter;
	}
}

Implementacja SimpleFunctionCounter

Poniżej prezentowana jest implementacja komponentu zewnętrznego obliczającego wartość funkcji oraz XMLowy plik kontraktu dla tegoż komponentu.
Należy pamiętać, że plik kontraktu musi znajdować się w tym samym pakiecie co komponent.
W tym przykładzie plik źródłowy SimpleFunctionCounter.java znajduje się w projekcie solutions-example w katalogu src/main/java/org/jage/examples/properties/xml.
Plik kontraktu SimpleFunctionCounter.contract.xml znajduje się w projekcie solutions-example w katalogu src/main/resources/org/jage/examples/properties/xml.

SimpleFunctionCounter.java

public class SimpleFunctionCounter {

	private final String version = "1.0.0";
	private int x;
	private int y;

	public SimpleFunctionCounter() {
		this.x = 0;
		this.y = 0;
	}

	public SimpleFunctionCounter(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int countSquareSum() {
		int result = x * x + y * y;
		x++;
		y++;
		return result;
	}

	public int getY() {
		return y;
	}

	public String toString() {
		return x + "^2 + " + y + "^2";
	}
}

SimpleFunctionCounter.contract.xml

<?xml version="1.0" encoding="UTF-8"?>
<component
  class="org.jage.examples.properties.xml.SimpleFunctionCounter"
  version="" xmlns="org.jage"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="org.jage ageDependency.xsd">
  <declaration>
    <attribute access="setter" modifier="2" name="xParameter" required="true" setter="setX" getter="getX">
      <type>
        <primitive name="int"/>
      </type>
    </attribute>
    <attribute access="direct" modifier="2" name="yParameter" required="true" fieldName="y">
      <type>
        <primitive name="int"/>
      </type>
    </attribute>
  </declaration>
</component>

Należy zwrócić uwagę na zdefiniowane propery i sposób ich obsługi.
W tym przykładzie mamy dwa property:

  • xParameter - zdefiniowany w pierwszym attribute poprzez parametr name. Wartość parametru access ustawiono na setter, a więc zapis i odczyt wartości będzie obsługiwany przez setter (setX) i getter (getX).
  • yParameter - zdefiniowany w drugim attribute poprzez parametr name. Wartość parametru access ustawiono na direct, a więc zapis i odczyt wartości będzie obsługiwany przez refleksję na atrybucie y (jak zdefinowano za pomocą fieldName).

Przykład 2

W tym przykładzie tworzone jest środowisko obliczeniowe z dwoma agentami Scully i Mulder.
Oboje są zagregowani w TheXFiles.
Do każdego z agentów przypisany jest obiekt komponentu ExampleComponent, w którym są przechowywane informacje o danym agencie.
Obiekty te są przy starcie inicjalizowane wartościami podanymi w pliku konfiguracyjnym środowiska obliczeniowego.

W każdej turze agent przedstawia się przy pomocy swojego obiektu komponentu.

Wynik działania

W wyniku uruchomienia przykładu na konsoli powinno się pojawić coś podobnego do:

[main                                              ][INFO ][SimpleAggregate                                   ] Agent Scully@ added to the aggregate TheXFiles@
[main                                              ][INFO ][SimpleAggregate                                   ] Agent Mulder@ added to the aggregate TheXFiles@
[main                                              ][INFO ][SimpleAggregate                                   ] Agents added.
[main                                              ][INFO ][XMLContractHelloWorldSimpleAgent                  ] Initializing Hello World Simple Agent with XMLContracts.
[main                                              ][INFO ][XMLContractHelloWorldSimpleAgent                  ] Initializing Hello World Simple Agent with XMLContracts.
[main                                              ][INFO ][PicoWorkplaceManager                              ] Workplace TheXFiles@ initialized
[main                                              ][INFO ][SimpleWorkplace                                   ] TheXFiles@ is starting...
[main                                              ][INFO ][PicoWorkplaceManager                              ] Workplace TheXFiles@ started
[TheXFiles@                                        ][INFO ][IsolatedSimpleWorkplace                           ] TheXFiles@ has been started. Step to go: 10
[TheXFiles@                                        ][INFO ][XMLContractHelloWorldSimpleAgent                  ] Hello world! An example not-ClassPropertyContainer object will introduce himself:
My name is Mulder's component
My version is 777
I'm holding reference to java.lang.Object@bb7465
[TheXFiles@                                        ][INFO ][XMLContractHelloWorldSimpleAgent                  ] Hello world! An example not-ClassPropertyContainer object will introduce himself:
My name is Scully's component
My version is 1
I'm holding reference to java.lang.Object@d6c16c
[TheXFiles@                                        ][INFO ][XMLContractHelloWorldSimpleAgent                  ] Hello world! An example not-ClassPropertyContainer object will introduce himself:
My name is Mulder's component
My version is 777
I'm holding reference to java.lang.Object@bb7465
[TheXFiles@                                        ][INFO ][XMLContractHelloWorldSimpleAgent                  ] Hello world! An example not-ClassPropertyContainer object will introduce himself:
My name is Scully's component
My version is 1
I'm holding reference to java.lang.Object@d6c16c

Konfiguracja środowiska

Konfiguracja aplikacji znajduje się w projekcie applications-examples w pliku: src/main/resources/examples/properties/xml/workplace2.xml. Plik ten definiuje strukturę lokalnego środowiska obliczeniowego. Poniżej przedstawiono jego zawartość:

<configuration>
	<object name="workplace" class="org.jage.workplace.IsolatedSimpleWorkplace">
		<property name="address">
			<value class="AgentAddress" value="TheXFiles" />
		</property>
		<property name="endConditionStepsCnt">
			<value class="Integer" value="10" />
		</property>

		<list name="agents">
			<object name="exampleComponentForScully" class="org.jage.examples.properties.xml.ExampleComponent">
				<property name="name">
					<value value="Scully's component" class="String"/>
				</property>
				<property name="version">
					<value value="1" class="Integer"/>
				</property>
				<object name="sampleObject" class="java.lang.Object"/>
				<property name="holder">
					<reference target="sampleObject"/>
				</property>
			</object>

			<object name="exampleComponentForMulder" class="org.jage.examples.properties.xml.ExampleComponent">
				<property name="name">
					<value value="Mulder's component" class="String"/>
				</property>
				<property name="version">
					<value value="777" class="Integer"/>
				</property>
				<object name="sampleObject" class="java.lang.Object"/>
				<property name="holder">
					<reference target="sampleObject"/>
				</property>
			</object>

			<object name="scully" class="org.jage.examples.properties.xml.XMLContractHelloWorldSimpleAgent">
				<property name="address">
					<value value="Scully" class="AgentAddress" />
				</property>
				<property name="exampleComponent">
					<reference target="exampleComponentForScully"></reference>
				</property>
			</object>
			<reference target="scully"/>

			<object name="mulder" class="org.jage.examples.properties.xml.XMLContractHelloWorldSimpleAgent">
				<property name="address">
					<value value="Mulder" class="AgentAddress" />
				</property>
				<property name="exampleComponent">
					<reference target="exampleComponentForMulder"></reference>
				</property>
			</object>
			<reference target="mulder"/>

		</list>
		<property name="agents">
			<reference target="agents"/>
		</property>
	</object>
</configuration>

Należy zwrócić uwagę, że w konfiguracji zawarta jest również konfiguracja komponentów zewnętrznych.

Implementacja agenta

Implementacja agenta jest właściwie standardową implementacją, dlatego część kodu została pominięta poniżej.
Ważne są elementy związane z ExampleComponent. Plik źródłowy znajduje się w projekcie solutions-example w katalogu src/main/java/org/jage/examples/properties/xml.

public class XMLContractHelloWorldSimpleAgent extends SimpleAgent {

	@PropertyField(propertyName = "exampleComponent", type = ExampleComponent.class)
	private ExampleComponent _component;

	public void step() {
		_log.info("Hello world! An example not-ClassPropertyContainer object will introduce himself: ");
		_component.printComponentInfo();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException ex) {
			_log.error("Interrupted", ex);
		}
	}
}

Implementacja ExampleComponent

Poniżej prezentowana jest implementacja komponentu zewnętrznego przechowującego dane o agencie oraz XMLowy plik kontraktu dla tegoż komponentu.
Należy pamiętać, że plik kontraktu musi znajdować się w tym samym pakiecie co komponent.
W tym przykładzie plik źródłowy ExampleComponent.java znajduje się w projekcie solutions-example w katalogu src/main/java/org/jage/examples/properties/xml.
Plik kontraktu ExampleComponent.contract.xml znajduje się w projekcie solutions-example w katalogu src/main/resources/org/jage/examples/properties/xml.

ExampleComponent.java

public class ExampleComponent {
	@SuppressWarnings("unused")
	private static final String version = "1.0.0";

	private String _name = "";
	private int _versionNo = 0;
	private Object _holder = null;

	public void setName(String name) {
		this._name = name;
	}

	public String getName() {
		return _name;
	}

	public int getVersionNo() {
		return _versionNo;
	}

	public Object getHoldedObject() {
		return _holder;
	}

	public void printComponentInfo() {
		System.out.println("My name is " + _name);
		System.out.println("My version is " + _versionNo);
		System.out.println("I'm holding reference to " + _holder);
	}

ExampleComponent.contract.xml

<?xml version="1.0" encoding="UTF-8"?>
<component class="org.jage.examples.properties.xml.ExampleComponent"
  version="1.0.0" xmlns="org.jage"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="org.jage ageDependency.xsd">
  <declaration>
    <attribute access="setter" name="name" required="true" setter="setName" getter="getName">
      <type>
        <simpleClass class="java.lang.String" interface="false"/>
      </type>
    </attribute>
    <attribute access="direct" name="version"
    	required="true" fieldName="_versionNo">
    	<type>
    		<primitive name="int" />
    	</type>
    </attribute>
    <attribute access="direct" name="holder"
    	required="true" fieldName="_holder">
    	<type>
    		<simpleClass class="java.lang.Object" interface="false" />
    	</type>
    </attribute>
  </declaration>
</component>

Należy zwrócić uwagę na zdefiniowane propery i sposób ich obsługi.
W tym przykładzie mamy trzy property:

  • name - zdefiniowany w pierwszym attribute poprzez parametr name. Wartość parametru access ustawiono na setter, a więc zapis i odczyt wartości będzie obsługiwany przez setter (setName) i getter (getName).
  • version - zdefiniowany w drugim attribute poprzez parametr name. Wartość parametru access ustawiono na direct, a więc zapis i odczyt wartości będzie obsługiwany przez refleksję na atrybucie _versionNo (jak zdefinowano za pomocą fieldName).
  • holder - zdefiniowany w drugim attribute poprzez parametr name. Wartość parametru access ustawiono na direct, a więc zapis i odczyt wartości będzie obsługiwany przez refleksję na atrybucie _holder (jak zdefinowano za pomocą fieldName).