diff --git a/main/apiplugins/io.sarl.core/src/io/sarl/core/bootstrap.sarl b/main/apiplugins/io.sarl.core/src/io/sarl/core/bootstrap.sarl new file mode 100644 index 0000000000..d915353283 --- /dev/null +++ b/main/apiplugins/io.sarl.core/src/io/sarl/core/bootstrap.sarl @@ -0,0 +1,206 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2017 the original authors or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.sarl.core + +import java.lang.ref.SoftReference +import java.util.ServiceLoader +import java.util.UUID +import io.sarl.lang.core.Agent + +/** + * Represents an access point to the SARL run-time environment (SRE). + * This access point may be used for accessing the underlying SRE independently of its implementation. + * + *

Depending on the implementation of the SRE, an instance of this SRE access point could be injected. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6 + */ +interface SREBootstrap { + + /** + * Launch the SRE and the first agent in the kernel. + * + *

The function {@link #getBootAgentIdentifier()} permits to retrieve the identifier of the launched agent. + * + * @param agentCls - type of the first agent to launch. + * @param params - parameters to pass to the agent as its initialization parameters. + * @return the identifier of the created agent. + * @throws Exception - if it is impossible to start the platform. + * @see #getBootAgentIdentifier() + */ + def startAgent(agentCls : Class, params : Object*) : UUID throws Exception + + /** + * Launch the SRE and the first agent in the kernel. + * + *

The function {@link #getBootAgentIdentifier()} permits to retrieve the identifier of the launched agent. + * + * @param nbAgents - the number of agents to be launched. + * @param agentCls - type of the first agent to launch. + * @param params - parameters to pass to the agent as its initialization parameters. + * @return the identifiers of the created agents. + * @throws Exception - if it is impossible to start the platform. + * @see #getBootAgentIdentifier() + */ + def startAgent(nbAgents : int, agentCls : Class, params : Object*) : Iterable throws Exception + + /** + * Replies the identifier of the boot agent from the system's properties. The boot agent is launched with + * {@link #startAgent(int, Class, Object...)}. + * + * @return the identifier of the boot agent, or null if it is unknown. + * @see #startAgent(int, Class, Object...) + */ + def getBootAgentIdentifier : UUID + + /** Replies if the bootstrap could be used. + * + *

If the bootstrap cannot be used, it cannot launch agent. + * + * @return {@code true} if the bootstrap could be used. {@code false} if it cannot be used. + */ + def isActive : boolean { true } +} + +/** + * Represents an access point to the SARL run-time environment (SRE). + * This access point may be used for accessing the underlying SRE independently of its implementation. + * + *

Depending on the implementation of the SRE, an instance of this SRE access point could be injected. + * + *

For declaring a SRE bootstrap, the library that contains the contributing SRE must declared + * a specific service implementation of {@link SREBootstrap}. The declaration of this service must be + * done by creating a file into the folder {@code META-INF/services/io.sarl.lang.core.SREBootstrap}. + * This file contains a single line that is the fully qualified name of the {@link SREBootstrap}'s implementation. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6 + * @see ServiceLoader + */ +final class SRE { + + static var currentSRE : SREBootstrap + + static var loader : SoftReference> + + private new { + } + + /** Reset the service loader for the SRE bootstrap. + */ + static def resetServiceLoader() : void { + synchronized (typeof(SRE)) { + loader = null + } + } + + /** Replies all the installed SRE into the classpath. + * + * @param onlyInstalledInJRE indicates if the services will be considered only into the libraries that are + * installed into the JRE. If {@code true}, only the libraries into the JRE will be considered and + * the application libraries will be ignored. If {@code false}, the application libraries will be + * considered as well. + * @return the installed SRE. + */ + static def getServiceLoader(onlyInstalledInJRE : boolean = false) : ServiceLoader { + synchronized (typeof(SRE)) { + var sl = if (loader === null) null else loader.get + if (sl === null) { + if (onlyInstalledInJRE) { + sl = ServiceLoader::loadInstalled(typeof(SREBootstrap)) + } else { + sl = ServiceLoader::load(typeof(SREBootstrap)) + } + loader = new SoftReference(sl) + } + return sl + } + } + + /** Change the current SRE. + * + * @param sre the current SRE. + */ + static def setBootstrap(sre : SREBootstrap) { + synchronized (typeof(SRE)) { + currentSRE = sre + } + } + + /** Find and reply the current SRE. + * + * @return the current SRE, never {@code null}. + * @throws IllegalStateException if a SRE cannot be found. + */ + static def getBootstrap : SREBootstrap { + synchronized (typeof(SRE)) { + if (currentSRE === null) { + val iterator = getServiceLoader.iterator + if (iterator.hasNext) { + currentSRE = iterator.next + } else { + currentSRE = new VoidSREBootstrap + } + } + return currentSRE + } + } + + /** + * Private API: not documented. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6 + */ + private static class VoidSREBootstrap implements SREBootstrap { + + package new { + } + + override startAgent(agentCls : Class, params : Object*) : UUID { + throw new UnsupportedOperationException + } + + override startAgent(nbAgents : int, agentCls : Class, params : Object*) : Iterable { + throw new UnsupportedOperationException + } + + override getBootAgentIdentifier : UUID { + throw new UnsupportedOperationException + } + + override isActive : boolean { + false + } + + } + +} diff --git a/tests/io.sarl.core.tests/src/test/java/io/sarl/core/tests/SREBootstrapMock.java b/tests/io.sarl.core.tests/src/test/java/io/sarl/core/tests/SREBootstrapMock.java new file mode 100644 index 0000000000..04ee542bad --- /dev/null +++ b/tests/io.sarl.core.tests/src/test/java/io/sarl/core/tests/SREBootstrapMock.java @@ -0,0 +1,51 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2017 the original authors or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.sarl.core.tests; + +import java.util.UUID; + +import io.sarl.core.SREBootstrap; +import io.sarl.lang.core.Agent; + +/** + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +public class SREBootstrapMock implements SREBootstrap { + + @Override + public UUID startAgent(Class agentCls, Object... params) throws Exception { + throw new IllegalStateException(); + } + + @Override + public Iterable startAgent(int nbAgents, Class agentCls, Object... params) throws Exception { + throw new IllegalStateException(); + } + + @Override + public UUID getBootAgentIdentifier() { + throw new IllegalStateException(); + } + +} diff --git a/tests/io.sarl.core.tests/src/test/java/io/sarl/core/tests/SRETest.java b/tests/io.sarl.core.tests/src/test/java/io/sarl/core/tests/SRETest.java new file mode 100644 index 0000000000..1953bdbeca --- /dev/null +++ b/tests/io.sarl.core.tests/src/test/java/io/sarl/core/tests/SRETest.java @@ -0,0 +1,123 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2017 the original authors or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.sarl.core.tests; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.UUID; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import io.sarl.core.SRE; +import io.sarl.core.SREBootstrap; +import io.sarl.lang.core.Agent; +import io.sarl.tests.api.AbstractSarlTest; + +/** + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("all") +public class SRETest extends AbstractSarlTest { + + @Before + public void setUp() { + SRE.resetServiceLoader(); + SRE.setBootstrap(null); + } + + @After + public void tearDown() { + SRE.resetServiceLoader(); + SRE.setBootstrap(null); + } + + private void installNoService() { + SRE.getServiceLoader(true); + } + + private void installService() { + SRE.getServiceLoader(false); + } + + @Test + public void getServiceLoader_noService() { + installNoService(); + ServiceLoader bootstraps = SRE.getServiceLoader(); + assertContains(bootstraps); + } + + @Test + public void getServiceLoader_mockedService() { + installService(); + ServiceLoader bootstraps = SRE.getServiceLoader(); + Iterator iterator = bootstraps.iterator(); + SREBootstrap bs = iterator.next(); + assertNotNull(bs); + assertInstanceOf(SREBootstrapMock.class, bs); + assertFalse(iterator.hasNext()); + } + + @Test + public void getBootstrap_noService() throws Exception { + installNoService(); + SREBootstrap bs = SRE.getBootstrap(); + assertNotNull(bs); + assertFalse(bs.isActive()); + try { + bs.startAgent(MyAgent.class); + fail("Expecting exception"); + } catch (IllegalStateException | UnsupportedOperationException ex) { + } + try { + bs.startAgent(1, MyAgent.class); + fail("Expecting exception"); + } catch (IllegalStateException | UnsupportedOperationException ex) { + } + } + + @Test + public void getBootstrap_mockedService() { + installService(); + SREBootstrap bs = SRE.getBootstrap(); + assertNotNull(bs); + assertTrue(bs.isActive()); + assertInstanceOf(SREBootstrapMock.class, bs); + } + + private static class MyAgent extends Agent { + + public MyAgent(UUID parentID, UUID agentID) { + super(parentID, agentID); + } + + } + +} diff --git a/tests/io.sarl.core.tests/src/test/resources/META-INF/services/io.sarl.core.SREBootstrap b/tests/io.sarl.core.tests/src/test/resources/META-INF/services/io.sarl.core.SREBootstrap new file mode 100644 index 0000000000..65dcee6d69 --- /dev/null +++ b/tests/io.sarl.core.tests/src/test/resources/META-INF/services/io.sarl.core.SREBootstrap @@ -0,0 +1 @@ +io.sarl.core.tests.SREBootstrapMock