Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2016-03-07 16:53:05 +0000
committerChristian W. Damus2016-03-07 22:54:00 +0000
commitb3abad7ab47a4414939e41665d5d0d4c6bd8bcc7 (patch)
tree727f52ab4beee7a1ed974ba5d9645670e337d646
parent85ac78aed7e2a0b17920adf21f4d905b53159081 (diff)
downloadorg.eclipse.papyrus-b3abad7ab47a4414939e41665d5d0d4c6bd8bcc7.tar.gz
org.eclipse.papyrus-b3abad7ab47a4414939e41665d5d0d4c6bd8bcc7.tar.xz
org.eclipse.papyrus-b3abad7ab47a4414939e41665d5d0d4c6bd8bcc7.zip
Bug 488558: [Releng] Projects not included in the Main release
https://bugs.eclipse.org/bugs/show_bug.cgi?id=488558 Implement a factory for shared service instances in the Service Registry. Use this new facility for the stateless object localizer service. Change-Id: I467c2d9c59243658b467697b099afa46af480fb2
-rw-r--r--extraplugins/cdo/org.eclipse.papyrus.cdo.core/.classpath2
-rw-r--r--extraplugins/cdo/org.eclipse.papyrus.cdo.core/.settings/org.eclipse.jdt.core.prefs6
-rw-r--r--extraplugins/cdo/org.eclipse.papyrus.cdo.core/META-INF/MANIFEST.MF2
-rw-r--r--extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/services/localizer/CDOAwareObjectLocalizer.java33
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/SharedServiceFactory.java283
-rw-r--r--plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.classpath2
-rw-r--r--plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.settings/org.eclipse.jdt.core.prefs6
-rw-r--r--plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/META-INF/MANIFEST.MF2
-rw-r--r--plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/src/org/eclipse/papyrus/infra/services/localizer/util/DefaultObjectLocalizerFactory.java32
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/ServicesRegistryTest.java4
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/SharedServiceFactoryTest.java399
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java5
12 files changed, 708 insertions, 68 deletions
diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.classpath b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.classpath
index cc4f891eda6..dbc46ec74ec 100644
--- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.classpath
+++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src-gen"/>
<classpathentry kind="src" path="src/"/>
diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.settings/org.eclipse.jdt.core.prefs b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.settings/org.eclipse.jdt.core.prefs
index 94d61f00da6..b3aa6d60f94 100644
--- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.settings/org.eclipse.jdt.core.prefs
+++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/.settings/org.eclipse.jdt.core.prefs
@@ -1,10 +1,10 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/META-INF/MANIFEST.MF b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/META-INF/MANIFEST.MF
index fb514cc358c..80641f36eb4 100644
--- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/META-INF/MANIFEST.MF
+++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/META-INF/MANIFEST.MF
@@ -67,4 +67,4 @@ Bundle-Activator: org.eclipse.papyrus.cdo.internal.core.Activator
Bundle-ManifestVersion: 2
Bundle-Description: %pluginDescription
Bundle-SymbolicName: org.eclipse.papyrus.cdo.core;singleton:=true
-Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/services/localizer/CDOAwareObjectLocalizer.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/services/localizer/CDOAwareObjectLocalizer.java
index cd6753439c5..d0a5e5fd501 100644
--- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/services/localizer/CDOAwareObjectLocalizer.java
+++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/services/localizer/CDOAwareObjectLocalizer.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2016 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
*
* Contributors:
* CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 488558
*****************************************************************************/
package org.eclipse.papyrus.cdo.internal.core.services.localizer;
@@ -18,9 +19,7 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.papyrus.cdo.internal.core.CDOUtils;
-import org.eclipse.papyrus.infra.core.services.IServiceFactory;
-import org.eclipse.papyrus.infra.core.services.ServiceException;
-import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.core.services.SharedServiceFactory;
import org.eclipse.papyrus.infra.services.localizer.DefaultObjectLocalizer;
import org.eclipse.papyrus.infra.services.localizer.IObjectLocalizer;
@@ -87,32 +86,10 @@ public class CDOAwareObjectLocalizer extends DefaultObjectLocalizer {
// Nested types
//
- public static class Factory implements IServiceFactory {
-
- private static final IObjectLocalizer LOCALIZER = new CDOAwareObjectLocalizer();
+ public static class Factory extends SharedServiceFactory<IObjectLocalizer> {
public Factory() {
- super();
- }
-
- @Override
- public void init(ServicesRegistry servicesRegistry) throws ServiceException {
- // pass. The localizer is stateless and requires no initialization
- }
-
- @Override
- public void startService() throws ServiceException {
- // pass. The localizer is stateless and requires no starting
- }
-
- @Override
- public void disposeService() throws ServiceException {
- // pass. The localizer is stateless and requires no disposal
- }
-
- @Override
- public Object createServiceInstance() throws ServiceException {
- return LOCALIZER;
+ super(IObjectLocalizer.class, CDOAwareObjectLocalizer::new);
}
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/SharedServiceFactory.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/SharedServiceFactory.java
new file mode 100644
index 00000000000..7066747473d
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/SharedServiceFactory.java
@@ -0,0 +1,283 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.services;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.papyrus.infra.core.Activator;
+import org.eclipse.papyrus.infra.tools.util.ReferenceCounted;
+
+/**
+ * A service factory that creates a reference-counted service that is shared
+ * amongst all {@linkplain ServicesRegistry service registries}. If the
+ * service interface {@code S} does not conform to the {@link IService} protocol
+ * then it is recommended to override the following operations:
+ * <ul>
+ * <li>{@link #start(Object)}</li>
+ * <li>{@link #dispose(Object)}</li>
+ * </ul>
+ * In any case, if the service is an {@link IService}, it will never be
+ * {@linkplain IService#init(ServicesRegistry) initialized} with a service
+ * registry because it is shared by all service registries.
+ *
+ * @param <S>
+ * the shared service interface
+ *
+ * @since 2.0
+ */
+public class SharedServiceFactory<S> implements IServiceFactory {
+
+ private final Class<? extends S> serviceInterface;
+
+ private final Supplier<? extends S> serviceCreator;
+
+ private final Function<? super ServicesRegistry, Scope> scopeProvider;
+
+ private Scope scope;
+
+ private S serviceInstance;
+
+ /**
+ * Creates a new shared service factory for services of the given type. Subclasses that
+ * use this constructor must override the {@link #createSharedInstance()} method to
+ * create the shared service instance. The {@link Scope} of the shared service instance
+ * is the {@linkplain Scope#GLOBAL_SCOPE global scope}.
+ *
+ * @param serviceInterface
+ * the service type that I create
+ */
+ protected SharedServiceFactory(Class<? extends S> serviceInterface) {
+ this(serviceInterface, null, null);
+ }
+
+ /**
+ * Creates a new shared service factory for services of the given type with a supplier
+ * that provides the shared service instance on demand. Subclasses that
+ * use this constructor need not override the {@link #createSharedInstance()} method to
+ * create the shared service instance, unless the {@code serviceCreator} is {@code null}.
+ * The {@link Scope} of the shared service instance is the
+ * {@linkplain Scope#GLOBAL_SCOPE global scope}.
+ *
+ * @param serviceInterface
+ * the service type that I create
+ * @param serviceCreator
+ * a supplier that creates a new service instance on demand. This commonly
+ * would be a zero-argument constructor of the service implementation class
+ */
+ protected SharedServiceFactory(Class<? extends S> serviceInterface, Supplier<? extends S> serviceCreator) {
+ this(serviceInterface, serviceCreator, null);
+ }
+
+ /**
+ * Creates a new shared service factory for services of the given type in a defined
+ * scope, with a supplier that provides the shared service instance on demand.
+ * Subclasses that use this constructor need not override the {@link #createSharedInstance()}
+ * method to create the shared service instance, unless the {@code serviceCreator} is
+ * {@code null}.
+ *
+ * @param serviceInterface
+ * the service type that I create
+ * @param serviceCreator
+ * a supplier that creates a new service instance on demand. This commonly
+ * would be a zero-argument constructor of the service implementation class
+ * @param scopeProvider
+ * a function that computes the appropriate {@link Scope} in which
+ * a registry should find the shared instance of my service. A {@code null} scope
+ * is equivalent to the {@linkplain Scope#GLOBAL_SCOPE global scope}
+ */
+ protected SharedServiceFactory(Class<? extends S> serviceInterface, Supplier<? extends S> serviceCreator, Function<? super ServicesRegistry, Scope> scopeProvider) {
+ super();
+
+ this.serviceInterface = serviceInterface;
+ this.serviceCreator = (serviceCreator != null) ? serviceCreator : this::createSharedInstance;
+ this.scopeProvider = (scopeProvider != null) ? scopeProvider : __ -> Scope.GLOBAL_SCOPE;
+ }
+
+ @Override
+ public final void init(ServicesRegistry servicesRegistry) throws ServiceException {
+ // A shared service doesn't need to know any particular registry, but I
+ // need to create my scope
+ scope = scopeProvider.apply(servicesRegistry);
+ }
+
+ @Override
+ public final void startService() throws ServiceException {
+ // It was already started on creation
+ }
+
+ /**
+ * Releases my service instance, thereby reducing its reference count and potentially
+ * actually disposing of it (if there are no other service registries using it).
+ */
+ @Override
+ public final void disposeService() {
+ if (serviceInstance != null) {
+ serviceInstance = null;
+ scope.maybeGetService(serviceInterface).ifPresent(ReferenceCounted::release);
+ scope = null;
+ }
+ }
+
+ @Override
+ public final Object createServiceInstance() throws ServiceException {
+ if (serviceInstance == null) {
+ try {
+ serviceInstance = serviceInterface.cast(getCountedService().retain());
+ } catch (ClassCastException e) {
+ getCountedService().release(); // It's of no use to us
+ throw new ServiceException("Incompatible service type", e); //$NON-NLS-1$
+ }
+ }
+
+ return serviceInstance;
+ }
+
+ private ReferenceCounted<?> getCountedService() throws ServiceException {
+ return scope.getService(serviceInterface, this);
+ }
+
+ /**
+ * If the factory is not {@linkplain #SharedServiceFactory(Class, Supplier) initialized}
+ * with a service creator, then this method must be overridden to create the shared
+ * service instance. The default implementation throws {@link UnsupportedOperationException}..
+ *
+ * @return a new instance of my service, to be shared by all registries
+ */
+ protected S createSharedInstance() {
+ throw new UnsupportedOperationException("createSharedInstance"); //$NON-NLS-1$
+ }
+
+ /**
+ * Starts the shared instance of my service. This is only called once in the lifetime
+ * of the instance. The default implementation forwards to the {@link IService} protocol
+ * if the {@code sharedInstance} conforms to it. Otherwise, subclasses should override
+ * to start the service instance if necessary.
+ *
+ * @param sharedInstance
+ * the shared instance of my service
+ *
+ * @throws ServiceException
+ * on failure to start the instance
+ *
+ * @see IService#startService()
+ */
+ protected void start(S sharedInstance) throws ServiceException {
+ if (sharedInstance instanceof IService) {
+ ((IService) sharedInstance).startService();
+ }
+ }
+
+ /**
+ * Disposes of the shared instance of my service. This is only called once in the lifetime
+ * of the instance. The default implementation forwards to the {@link IService} protocol
+ * if the {@code sharedInstance} conforms to it. Otherwise, subclasses should override
+ * to dispose of the service instance if necessary.
+ *
+ * @param sharedInstance
+ * the shared instance of my service
+ *
+ * @throws ServiceException
+ * on failure to dispose of the instance
+ *
+ * @see IService#disposeService()
+ */
+ protected void dispose(S sharedInstance) throws ServiceException {
+ if (sharedInstance instanceof IService) {
+ ((IService) sharedInstance).disposeService();
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A scope in which a single instance of my service is to be shared. Any number of
+ * service registries can share the same service within a scope if they have factories
+ * that provide that scope. It is up to each factory to determine the scope appropriate
+ * for its registry. The default scope is the {@link #GLOBAL_SCOPE}. Clients may
+ * create any scopes that they may need, in addition to using the
+ * {@linkplain #GLOBAL_SCOPE global scope}.
+ *
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+ public static class Scope {
+ /**
+ * The default scope in which registries share service instances, being a global scope
+ * available to all registries.
+ */
+ public static final Scope GLOBAL_SCOPE = new Scope();
+
+ /**
+ * Initialize me.
+ */
+ public Scope() {
+ super();
+ }
+
+ private final Map<Class<?>, ReferenceCounted<?>> serviceInstances = new ConcurrentHashMap<>();
+
+ /**
+ * Obtains the shared instance of the specified service interface if it currently
+ * exists in the scope.
+ *
+ * @param serviceInterface
+ * a service interface
+ *
+ * @return the current shared instance, or {@code null} if it does not exist
+ */
+ public <S> S getService(Class<S> serviceInterface) {
+ return maybeGetService(serviceInterface)
+ .map(serviceInterface::cast)
+ .orElse(null);
+ }
+
+ Optional<ReferenceCounted<?>> maybeGetService(Class<?> serviceInterface) {
+ return Optional.ofNullable(serviceInstances.get(serviceInterface));
+ }
+
+ <S> ReferenceCounted<?> getService(Class<? extends S> serviceInterface, SharedServiceFactory<S> factory) throws ServiceException {
+ try {
+ return serviceInstances.computeIfAbsent(serviceInterface, type -> {
+ S sharedInstance = factory.serviceCreator.get();
+
+ try {
+ factory.start(sharedInstance);
+ } catch (ServiceException e) {
+ throw new WrappedException(e);
+ }
+
+ return new ReferenceCounted<>(sharedInstance, () -> {
+ // Remove the mapping for this shared instance
+ serviceInstances.remove(type);
+
+ // And dispose of it
+ try {
+ factory.dispose(sharedInstance);
+ } catch (Exception e) {
+ Activator.log.error("Shared service instance not successfully disposed", e); //$NON-NLS-1$
+ }
+ });
+ });
+ } catch (WrappedException e) {
+ throw (ServiceException) e.exception();
+ }
+ }
+ }
+}
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.classpath b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.classpath
index ad32c83a788..eca7bdba8f0 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.classpath
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.settings/org.eclipse.jdt.core.prefs b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.settings/org.eclipse.jdt.core.prefs
index 94d61f00da6..b3aa6d60f94 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.settings/org.eclipse.jdt.core.prefs
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/.settings/org.eclipse.jdt.core.prefs
@@ -1,10 +1,10 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/META-INF/MANIFEST.MF b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/META-INF/MANIFEST.MF
index e13640ff285..db1a0451351 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/META-INF/MANIFEST.MF
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/META-INF/MANIFEST.MF
@@ -12,4 +12,4 @@ Bundle-Localization: plugin
Bundle-ManifestVersion: 2
Bundle-Activator: org.eclipse.papyrus.infra.services.localizer.internal.Activator
Bundle-SymbolicName: org.eclipse.papyrus.infra.services.localizer;singleton:=true
-Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/src/org/eclipse/papyrus/infra/services/localizer/util/DefaultObjectLocalizerFactory.java b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/src/org/eclipse/papyrus/infra/services/localizer/util/DefaultObjectLocalizerFactory.java
index 79ffad08e9f..35ca3a6ecf6 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/src/org/eclipse/papyrus/infra/services/localizer/util/DefaultObjectLocalizerFactory.java
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.localizer/src/org/eclipse/papyrus/infra/services/localizer/util/DefaultObjectLocalizerFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST and others.
+ * Copyright (c) 2013, 2016 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,42 +8,22 @@
*
* Contributors:
* CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 488558
*****************************************************************************/
package org.eclipse.papyrus.infra.services.localizer.util;
-import org.eclipse.papyrus.infra.core.services.IServiceFactory;
-import org.eclipse.papyrus.infra.core.services.ServiceException;
-import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.core.services.SharedServiceFactory;
import org.eclipse.papyrus.infra.services.localizer.DefaultObjectLocalizer;
+import org.eclipse.papyrus.infra.services.localizer.IObjectLocalizer;
/**
* Service factory for the default object localizer.
*/
-public class DefaultObjectLocalizerFactory implements IServiceFactory {
+public class DefaultObjectLocalizerFactory extends SharedServiceFactory<IObjectLocalizer> {
public DefaultObjectLocalizerFactory() {
- super();
- }
-
- @Override
- public void init(ServicesRegistry servicesRegistry) throws ServiceException {
- // pass. The localizer is stateless and requires no initialization
- }
-
- @Override
- public void startService() throws ServiceException {
- // pass. The localizer is stateless and requires no starting
- }
-
- @Override
- public void disposeService() throws ServiceException {
- // pass. The localizer is stateless and requires no disposal
- }
-
- @Override
- public Object createServiceInstance() throws ServiceException {
- return DefaultObjectLocalizer.INSTANCE;
+ super(IObjectLocalizer.class, DefaultObjectLocalizer::new);
}
}
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/ServicesRegistryTest.java b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/ServicesRegistryTest.java
index 25c620d9ed8..0b8e1ce035e 100644
--- a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/ServicesRegistryTest.java
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/ServicesRegistryTest.java
@@ -8,7 +8,7 @@
*
* Contributors:
* LIFL - Initial API and implementation
- * Christian W. Damus - bug 488791
+ * Christian W. Damus - bugs 488791, 488558
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services;
@@ -739,7 +739,7 @@ public class ServicesRegistryTest extends AbstractPapyrusTest {
/**
* General purpose descriptor.
*/
- public class ServiceFactoryDesc extends TestServiceDescriptor {
+ public static class ServiceFactoryDesc extends TestServiceDescriptor {
public ServiceFactoryDesc(String key, ServiceStartKind startKind) {
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/SharedServiceFactoryTest.java b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/SharedServiceFactoryTest.java
new file mode 100644
index 00000000000..78424bb56b4
--- /dev/null
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/services/SharedServiceFactoryTest.java
@@ -0,0 +1,399 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.services;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.eclipse.papyrus.junit.utils.rules.AnnotationRule;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test cases for the {@link SharedServiceFactory} class. Tests annotated with
+ * {@link Parallel @Parallel} operate on multiple service registries in parallel
+ * and the initialization and finalization of services in parallel tests have
+ * random short delays to try to mix things up for each execution.
+ */
+public class SharedServiceFactoryTest {
+ private static final int DEFAULT_REGISTRY_COUNT = 3;
+
+ private static Synchronizer sync;
+
+ @Rule
+ public final AnnotationRule<Integer> registryCount = AnnotationRule.create(Registries.class, DEFAULT_REGISTRY_COUNT);
+
+ @Rule
+ public final AnnotationRule<Boolean> parallelRegistries = AnnotationRule.createExists(Parallel.class);
+
+ private List<ServicesRegistry> registries;
+
+ public SharedServiceFactoryTest() {
+ super();
+ }
+
+ @Test
+ public void uniqueServiceInstances() {
+ registries().forEach(this::start);
+
+ assertUniqueServiceInstance(Service1.class, Service1Impl.class);
+ assertUniqueServiceInstance(Service2.class, Service2Impl.class);
+ }
+
+ @Test
+ public void serviceNotInitialized() {
+ registries().forEach(this::start);
+
+ Service1Impl s1 = assumeUniqueServiceInstance(Service1.class, Service1Impl.class);
+ assertThat(s1.initializedCount.get(), is(0));
+ }
+
+ @Test
+ public void serviceStartedOnce() {
+ registries().forEach(this::start);
+
+ Service1Impl s1 = assumeUniqueServiceInstance(Service1.class, Service1Impl.class);
+ assertThat(s1.startedCount.get(), is(1));
+
+ Service2Impl s2 = assumeUniqueServiceInstance(Service2.class, Service2Impl.class);
+ assertThat(s2.startedCount.get(), is(1));
+ }
+
+ @Test
+ public void serviceDisposedOnce() {
+ registries().forEach(this::start);
+
+ Service1Impl s1 = assumeUniqueServiceInstance(Service1.class, Service1Impl.class);
+ Service2Impl s2 = assumeUniqueServiceInstance(Service2.class, Service2Impl.class);
+
+ destroyFixture();
+
+ assertThat(s1.disposedCount.get(), is(1));
+ assertThat(s2.disposedCount.get(), is(1));
+ }
+
+ @Test
+ public void servicesRecreated() {
+ registries().forEach(this::start);
+
+ Service1Impl s1 = assumeUniqueServiceInstance(Service1.class, Service1Impl.class);
+ Service2Impl s2 = assumeUniqueServiceInstance(Service2.class, Service2Impl.class);
+
+ destroyFixture();
+ createFixture();
+ registries().forEach(this::start);
+
+ Service1Impl s1_ = assumeUniqueServiceInstance(Service1.class, Service1Impl.class);
+ Service2Impl s2_ = assumeUniqueServiceInstance(Service2.class, Service2Impl.class);
+
+ assertThat(s1, not(sameInstance(s1_)));
+ assertThat(s2, not(sameInstance(s2_)));
+ }
+
+ @Registries(64)
+ @Parallel
+ @Test
+ public void parallelAccess() {
+ // Do a few iterations of this test
+ for (int i = 0; i < 5; i++) {
+ if (i > 0) {
+ // We destroyed the fixture the last time around
+ createFixture();
+ }
+
+ registries().forEach(this::start);
+
+ Service1Impl s1 = assumeUniqueServiceInstance(Service1.class, Service1Impl.class);
+ Service2Impl s2 = assumeUniqueServiceInstance(Service2.class, Service2Impl.class);
+ Service3Impl s3 = assumeUniqueServiceInstance(Service3.class, Service3Impl.class);
+
+ destroyFixture();
+
+ assertThat(s1.startedCount.get(), is(1));
+ assertThat(s1.disposedCount.get(), is(1));
+
+ assertThat(s2.startedCount.get(), is(1));
+ assertThat(s2.disposedCount.get(), is(1));
+
+ assertThat(s3.startedCount.get(), is(1));
+ assertThat(s3.disposedCount.get(), is(1));
+ }
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createFixture() {
+ if (parallelRegistries.get()) {
+ sync = new ParallelSynchronizer();
+ } else {
+ // No-op synchronizer
+ sync = () -> {
+ };
+ }
+
+ registries = createServiceRegistries(registryCount.get());
+ }
+
+ @After
+ public void destroyFixture() {
+ if (registries != null) {
+ try {
+ registries().forEach(this::dispose);
+ } finally {
+ registries = null;
+ }
+ }
+
+ sync = null;
+ }
+
+ Stream<ServicesRegistry> registries() {
+ return parallelRegistries.get() ? registries.parallelStream() : registries.stream();
+ }
+
+ <S, T extends S> T assertUniqueServiceInstance(Class<S> serviceInterface, Class<T> serviceImpl) {
+ List<S> result = registries()
+ .map(r -> getService(r, serviceInterface))
+ .distinct()
+ .collect(Collectors.toList());
+ assertThat(result.size(), is(1));
+ return serviceImpl.cast(result.get(0));
+ }
+
+ <S, T extends S> T assumeUniqueServiceInstance(Class<S> serviceInterface, Class<T> serviceImpl) {
+ List<S> result = registries()
+ .map(r -> getService(r, serviceInterface))
+ .distinct()
+ .collect(Collectors.toList());
+ Assume.assumeThat(result.size(), is(1));
+ return serviceImpl.cast(result.get(0));
+ }
+
+ <S> S getService(ServicesRegistry registry, Class<S> serviceInterface) {
+ try {
+ return registry.getService(serviceInterface);
+ } catch (ServiceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ List<ServicesRegistry> createServiceRegistries(int count) {
+ return IntStream.range(0, count).mapToObj(__ -> createServicesRegistry()).collect(Collectors.toList());
+ }
+
+ ServicesRegistry createServicesRegistry() {
+ ServicesRegistry result = new ServicesRegistry();
+ result.add(new ServicesRegistryTest.ServiceFactoryDesc(Service1.class, Service1Factory.class.getName(), ServiceStartKind.LAZY));
+ result.add(new ServicesRegistryTest.ServiceFactoryDesc(Service2.class, Service2Factory.class.getName(), ServiceStartKind.LAZY));
+ result.add(new ServicesRegistryTest.ServiceFactoryDesc(Service3.class, Service3Factory.class.getName(), ServiceStartKind.LAZY));
+ return result;
+ }
+
+ void start(ServicesRegistry registry) {
+ try {
+ registry.startRegistry();
+ } catch (ServiceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void dispose(ServicesRegistry registry) {
+ try {
+ registry.disposeRegistry();
+ } catch (ServiceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public interface Service1 extends IService {
+ // Nothing more
+ }
+
+ static class Service1Impl implements Service1 {
+ AtomicInteger initializedCount = new AtomicInteger();
+ AtomicInteger startedCount = new AtomicInteger();
+ AtomicInteger disposedCount = new AtomicInteger();
+
+ {
+ sync.sync();
+ }
+
+ @Override
+ public void init(ServicesRegistry servicesRegistry) throws ServiceException {
+ initializedCount.incrementAndGet();
+ }
+
+ @Override
+ public void startService() throws ServiceException {
+ sync.sync();
+ startedCount.incrementAndGet();
+ }
+
+ @Override
+ public void disposeService() throws ServiceException {
+ sync.sync();
+ disposedCount.incrementAndGet();
+ }
+
+ }
+
+ public static class Service1Factory extends SharedServiceFactory<Service1> {
+
+ public Service1Factory() {
+ super(Service1.class, Service1Impl::new);
+ }
+
+ }
+
+ public interface Service2 {
+ // Empty
+ }
+
+ static class Service2Impl implements Service2 {
+ AtomicInteger startedCount = new AtomicInteger();
+ AtomicInteger disposedCount = new AtomicInteger();
+
+ {
+ sync.sync();
+ }
+
+ void start() throws ServiceException {
+ sync.sync();
+ startedCount.incrementAndGet();
+ }
+
+ void dispose() throws ServiceException {
+ sync.sync();
+ disposedCount.incrementAndGet();
+ }
+
+ }
+
+ public static class Service2Factory extends SharedServiceFactory<Service2> {
+
+ public Service2Factory() {
+ super(Service2.class);
+ }
+
+ @Override
+ protected Service2 createSharedInstance() {
+ return new Service2Impl();
+ }
+
+ @Override
+ protected void start(Service2 sharedInstance) throws ServiceException {
+ ((Service2Impl) sharedInstance).start();
+ }
+
+ @Override
+ protected void dispose(Service2 sharedInstance) throws ServiceException {
+ ((Service2Impl) sharedInstance).dispose();
+ }
+ }
+
+ public interface Service3 {
+ // Empty
+ }
+
+ static class Service3Impl implements Service3 {
+ AtomicInteger startedCount = new AtomicInteger();
+ AtomicInteger disposedCount = new AtomicInteger();
+
+ {
+ sync.sync();
+ }
+
+ void start() throws ServiceException {
+ sync.sync();
+ startedCount.incrementAndGet();
+ }
+
+ void dispose() throws ServiceException {
+ sync.sync();
+ disposedCount.incrementAndGet();
+ }
+
+ }
+
+ public static class Service3Factory extends SharedServiceFactory<Service3> {
+
+ public Service3Factory() {
+ super(Service3.class, Service3Impl::new);
+ }
+
+ @Override
+ protected void start(Service3 sharedInstance) throws ServiceException {
+ ((Service3Impl) sharedInstance).start();
+ }
+
+ @Override
+ protected void dispose(Service3 sharedInstance) throws ServiceException {
+ ((Service3Impl) sharedInstance).dispose();
+ }
+ }
+
+ @Target({ ElementType.TYPE, ElementType.METHOD })
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Registries {
+ int value() default DEFAULT_REGISTRY_COUNT;
+ }
+
+ @Target({ ElementType.TYPE, ElementType.METHOD })
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Parallel {
+ // Empty
+ }
+
+ @FunctionalInterface
+ interface Synchronizer {
+ void sync();
+ }
+
+ /**
+ * A synchronizer that introduces short random delays into the
+ * execution of a parallel test case.
+ */
+ static final class ParallelSynchronizer implements Synchronizer {
+ final Random random = new Random(System.currentTimeMillis());
+
+ @Override
+ public void sync() {
+ try {
+ Thread.sleep(random.nextInt(100) + 20L);
+ } catch (Exception e) {
+ e.printStackTrace();
+ // Never mind. Just proceed
+ }
+ }
+ }
+}
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java
index c8e600342f9..063762cff96 100644
--- a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/tests/AllTests.java
@@ -10,7 +10,7 @@
* Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation
* Christian W. Damus (CEA LIST) - add test for AdapterUtils
* Christian W. Damus (CEA) - bugs 402525, 422257, 399859
- * Christian W. Damus - bugs 456934, 468030, 482949, 485220
+ * Christian W. Damus - bugs 456934, 468030, 482949, 485220, 488558
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.tests;
@@ -23,6 +23,7 @@ import org.eclipse.papyrus.infra.core.resource.ResourceAdapterTest;
import org.eclipse.papyrus.infra.core.resource.ResourceAdapterTransactionalTest;
import org.eclipse.papyrus.infra.core.services.ComposedServiceTest;
import org.eclipse.papyrus.infra.core.services.ServicesRegistryTest;
+import org.eclipse.papyrus.infra.core.services.SharedServiceFactoryTest;
import org.eclipse.papyrus.infra.core.utils.AdapterUtilsTest;
import org.eclipse.papyrus.infra.core.utils.JobBasedFutureTest;
import org.eclipse.papyrus.infra.core.utils.JobExecutorServiceTest;
@@ -42,7 +43,7 @@ import org.junit.runners.Suite.SuiteClasses;
// {oep}.core.language
LanguageServiceTest.class,
// {oep}.core.services
- ComposedServiceTest.class, ServicesRegistryTest.class,
+ ComposedServiceTest.class, ServicesRegistryTest.class, SharedServiceFactoryTest.class,
// {oep}.core.utils
AdapterUtilsTest.class, JobBasedFutureTest.class, JobExecutorServiceTest.class
})

Back to the top