FELIX-4405 Easy half of prototype scope, expose service as PrototypeServiceFactory

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1604454 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurableComponentHolder.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurableComponentHolder.java
index 7579812..c87f146 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurableComponentHolder.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurableComponentHolder.java
@@ -36,6 +36,7 @@
 import org.apache.felix.scr.impl.helper.SimpleLogger;
 import org.apache.felix.scr.impl.manager.AbstractComponentManager;
 import org.apache.felix.scr.impl.manager.ComponentFactoryImpl;
+import org.apache.felix.scr.impl.manager.PrototypeServiceFactoryComponentManager;
 import org.apache.felix.scr.impl.manager.SingleComponentManager;
 import org.apache.felix.scr.impl.manager.ServiceFactoryComponentManager;
 import org.apache.felix.scr.impl.metadata.ComponentMetadata;
@@ -180,8 +181,7 @@
 
         else if ( m_componentMetadata.getServiceScope() == Scope.prototype )
         {
-            manager = null;// NYI
-            //            manager = new ServiceFactoryComponentManager<S>( m_activator, this, m_componentMetadata, m_componentMethods );
+            manager = PSFLoader.newPSFComponentManager(this, m_componentMethods);
         }
 
         else
@@ -192,6 +192,14 @@
 
         return manager;
     }
+    
+    private static class PSFLoader 
+    {
+        static <S> AbstractComponentManager<S> newPSFComponentManager(ConfigurableComponentHolder<S> holder, ComponentMethods methods) 
+        {
+            return new PrototypeServiceFactoryComponentManager<S>( holder, methods );
+        }
+    }
 
 
     public final BundleComponentActivator getActivator()
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/PrototypeServiceFactoryComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/PrototypeServiceFactoryComponentManager.java
new file mode 100644
index 0000000..60102b6
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/PrototypeServiceFactoryComponentManager.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.felix.scr.impl.manager;
+
+import org.apache.felix.scr.impl.config.ComponentContainer;
+import org.apache.felix.scr.impl.helper.ComponentMethods;
+import org.osgi.framework.PrototypeServiceFactory;
+
+public class PrototypeServiceFactoryComponentManager<S> extends ServiceFactoryComponentManager<S> implements PrototypeServiceFactory<S>
+{
+
+    public PrototypeServiceFactoryComponentManager(ComponentContainer<S> container, ComponentMethods componentMethods)
+    {
+        super(container, componentMethods);
+    }
+
+}
diff --git a/scr/src/main/java/org/osgi/framework/PrototypeServiceFactory.java b/scr/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
new file mode 100644
index 0000000..0561ccc
--- /dev/null
+++ b/scr/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) OSGi Alliance (2012, 2014). All Rights Reserved.
+ * 
+ * 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 org.osgi.framework;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A factory for {@link Constants#SCOPE_PROTOTYPE prototype scope} services. The
+ * factory can provide multiple, customized service objects in the OSGi
+ * environment.
+ * 
+ * <p>
+ * When registering a service, a {@code PrototypeServiceFactory} object can be
+ * used instead of a service object, so that the bundle developer can create a
+ * customized service object for each caller that is using the service.
+ * 
+ * <p>
+ * When a caller uses a {@link ServiceObjects} to
+ * {@link ServiceObjects#getService() request} a service object, the framework
+ * calls the {@link #getService(Bundle, ServiceRegistration) getService} method
+ * to return a service object customized for the requesting caller. The caller
+ * can {@link ServiceObjects#ungetService(Object) release} the returned service
+ * object and the framework will call the
+ * {@link #ungetService(Bundle, ServiceRegistration, Object) ungetService}
+ * method with the service object.
+ * 
+ * <p>
+ * When a bundle uses the {@link BundleContext#getService(ServiceReference)}
+ * method to obtain a service object, the framework must act as if the service
+ * has {@link Constants#SCOPE_BUNDLE bundle scope}. That is, the framework will
+ * call the {@link #getService(Bundle, ServiceRegistration) getService} method
+ * to obtain a bundle-scoped service object which will be cached and have a use
+ * count. See {@link ServiceFactory}.
+ * 
+ * <p>
+ * A bundle can use both {@link ServiceObjects} and
+ * {@link BundleContext#getService(ServiceReference)} to obtain a service object
+ * for a service. {@link ServiceObjects#getService()} will always return a
+ * service object provided by a call to
+ * {@link #getService(Bundle, ServiceRegistration)} and
+ * {@link BundleContext#getService(ServiceReference)} will always return the
+ * bundle-scoped service object.
+ * 
+ * <p>
+ * {@code PrototypeServiceFactory} objects are only used by the Framework and
+ * are not made available to other bundles in the OSGi environment. The
+ * Framework may concurrently call a {@code PrototypeServiceFactory}.
+ * 
+ * @param <S> Type of Service
+ * @see BundleContext#getServiceObjects(ServiceReference)
+ * @see ServiceObjects
+ * @ThreadSafe
+ * @since 1.8
+ * @author $Id: 12129fe6e66b1c9488f3cca84ac228f9baaea083 $
+ */
+@ConsumerType
+public interface PrototypeServiceFactory<S> extends ServiceFactory<S> {
+	/**
+	 * Returns a service object for a caller.
+	 * 
+	 * <p>
+	 * The Framework invokes this method for each caller requesting a service
+	 * object using {@link ServiceObjects#getService()}. The factory can then
+	 * return a customized service object for the caller.
+	 * 
+	 * <p>
+	 * The Framework must check that the returned service object is valid. If
+	 * the returned service object is {@code null} or is not an
+	 * {@code instanceof} all the classes named when the service was registered,
+	 * a framework event of type {@link FrameworkEvent#ERROR} is fired
+	 * containing a service exception of type
+	 * {@link ServiceException#FACTORY_ERROR} and {@code null} is returned to
+	 * the caller. If this method throws an exception, a framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing a service exception of
+	 * type {@link ServiceException#FACTORY_EXCEPTION} with the thrown exception
+	 * as the cause and {@code null} is returned to the caller.
+	 * 
+	 * @param bundle The bundle requesting the service.
+	 * @param registration The {@code ServiceRegistration} object for the
+	 *        requested service.
+	 * @return A service object that <strong>must</strong> be an instance of all
+	 *         the classes named when the service was registered.
+	 * @see ServiceObjects#getService()
+	 */
+	public S getService(Bundle bundle, ServiceRegistration<S> registration);
+
+	/**
+	 * Releases a service object customized for a caller.
+	 * 
+	 * <p>
+	 * The Framework invokes this method when a service has been released by a
+	 * bundle such as by calling {@link ServiceObjects#ungetService(Object)}.
+	 * The service object may then be destroyed.
+	 * 
+	 * <p>
+	 * If this method throws an exception, a framework event of type
+	 * {@link FrameworkEvent#ERROR} is fired containing a service exception of
+	 * type {@link ServiceException#FACTORY_EXCEPTION} with the thrown exception
+	 * as the cause.
+	 * 
+	 * @param bundle The bundle releasing the service.
+	 * @param registration The {@code ServiceRegistration} object for the
+	 *        service being released.
+	 * @param service The service object returned by a previous call to the
+	 *        {@link #getService(Bundle, ServiceRegistration) getService}
+	 *        method.
+	 * @see ServiceObjects#ungetService(Object)
+	 */
+	public void ungetService(Bundle bundle, ServiceRegistration<S> registration, S service);
+}
diff --git a/scr/src/main/java/org/osgi/framework/ServiceObjects.java b/scr/src/main/java/org/osgi/framework/ServiceObjects.java
new file mode 100644
index 0000000..b47e5cc
--- /dev/null
+++ b/scr/src/main/java/org/osgi/framework/ServiceObjects.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) OSGi Alliance (2012, 2014). All Rights Reserved.
+ * 
+ * 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 org.osgi.framework;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Allows multiple service objects for a service to be obtained.
+ * 
+ * <p>
+ * For services with {@link Constants#SCOPE_PROTOTYPE prototype} scope, multiple
+ * service objects for the service can be obtained. For services with
+ * {@link Constants#SCOPE_SINGLETON singleton} or {@link Constants#SCOPE_BUNDLE
+ * bundle} scope, only one, use-counted service object is available to a
+ * requesting bundle.
+ * 
+ * <p>
+ * Any unreleased service objects obtained from this {@code ServiceObjects}
+ * object are automatically released by the framework when the bundle associated
+ * with the BundleContext used to create this {@code ServiceObjects} object is
+ * stopped.
+ * 
+ * @param <S> Type of Service
+ * @see BundleContext#getServiceObjects(ServiceReference)
+ * @see PrototypeServiceFactory
+ * @ThreadSafe
+ * @since 1.8
+ * @author $Id: 99314fe285a227cd63a21814a2300b109845125f $
+ */
+@ProviderType
+public interface ServiceObjects<S> {
+	/**
+	 * Returns a service object for the {@link #getServiceReference()
+	 * associated} service.
+	 * 
+	 * <p>
+	 * This {@code ServiceObjects} object can be used to obtain multiple service
+	 * objects for the associated service if the service has
+	 * {@link Constants#SCOPE_PROTOTYPE prototype} scope.
+	 * 
+	 * <p>
+	 * If the associated service has {@link Constants#SCOPE_SINGLETON singleton}
+	 * or {@link Constants#SCOPE_BUNDLE bundle} scope, this method behaves the
+	 * same as calling the {@link BundleContext#getService(ServiceReference)}
+	 * method for the associated service. That is, only one, use-counted service
+	 * object is available from this {@link ServiceObjects} object.
+	 * 
+	 * <p>
+	 * This method will always return {@code null} when the associated service
+	 * has been unregistered.
+	 * 
+	 * <p>
+	 * For a prototype scope service, the following steps are required to obtain
+	 * a service object:
+	 * <ol>
+	 * <li>If the associated service has been unregistered, {@code null} is
+	 * returned.</li>
+	 * <li>The
+	 * {@link PrototypeServiceFactory#getService(Bundle, ServiceRegistration)}
+	 * method is called to supply a customized service object for the caller.</li>
+	 * <li>If the service object returned by the {@code PrototypeServiceFactory}
+	 * object is {@code null}, not an {@code instanceof} all the classes named
+	 * when the service was registered or the {@code PrototypeServiceFactory}
+	 * object throws an exception, {@code null} is returned and a Framework
+	 * event of type {@link FrameworkEvent#ERROR} containing a
+	 * {@link ServiceException} describing the error is fired.</li>
+	 * <li>The customized service object is returned.</li>
+	 * </ol>
+	 * 
+	 * @return A service object for the associated service or {@code null} if
+	 *         the service is not registered, the customized service object
+	 *         returned by a {@code ServiceFactory} does not implement the
+	 *         classes under which it was registered or the
+	 *         {@code ServiceFactory} threw an exception.
+	 * @throws IllegalStateException If the BundleContext used to create this
+	 *         {@code ServiceObjects} object is no longer valid.
+	 * @see #ungetService(Object)
+	 */
+	public S getService();
+
+	/**
+	 * Releases a service object for the {@link #getServiceReference()
+	 * associated} service.
+	 * 
+	 * <p>
+	 * This {@code ServiceObjects} object can be used to obtain multiple service
+	 * objects for the associated service if the service has
+	 * {@link Constants#SCOPE_PROTOTYPE prototype} scope. If the associated
+	 * service has {@link Constants#SCOPE_SINGLETON singleton} or
+	 * {@link Constants#SCOPE_BUNDLE bundle} scope, this method behaves the same
+	 * as calling the {@link BundleContext#ungetService(ServiceReference)}
+	 * method for the associated service. That is, only one, use-counted service
+	 * object is available from this {@link ServiceObjects} object.
+	 * 
+	 * <p>
+	 * For a prototype scope service, the following steps are required to
+	 * release a service object:
+	 * <ol>
+	 * <li>If the associated service has been unregistered, this method returns
+	 * without doing anything.</li>
+	 * <li>The
+	 * {@link PrototypeServiceFactory#ungetService(Bundle, ServiceRegistration, Object)}
+	 * method is called to release the specified service object.</li>
+	 * </ol>
+	 * 
+	 * <p>
+	 * The specified service object must no longer be used and all references to
+	 * it should be destroyed after calling this method.
+	 * 
+	 * @param service A service object previously provided by this
+	 *        {@code ServiceObjects} object.
+	 * @throws IllegalStateException If the BundleContext used to create this
+	 *         {@code ServiceObjects} object is no longer valid.
+	 * @throws IllegalArgumentException If the specified service object was not
+	 *         provided by this {@code ServiceObjects} object.
+	 * @see #getService()
+	 */
+	public void ungetService(S service);
+
+	/**
+	 * Returns the {@link ServiceReference} for the service associated with this
+	 * {@code ServiceObjects} object.
+	 * 
+	 * @return The {@link ServiceReference} for the service associated with this
+	 *         {@code ServiceObjects} object.
+	 */
+	public ServiceReference<S> getServiceReference();
+}