blob: 393be7b62d1b4fd63007ba038c7bb39e24b554e9 [file] [log] [blame]
/*
* 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.ipojo.extender.internal.linker;
import static java.lang.String.format;
import org.apache.felix.ipojo.*;
import org.apache.felix.ipojo.extender.ExtensionDeclaration;
import org.apache.felix.ipojo.extender.InstanceDeclaration;
import org.apache.felix.ipojo.extender.TypeDeclaration;
import org.apache.felix.ipojo.extender.builder.FactoryBuilderException;
import org.apache.felix.ipojo.extender.internal.DefaultJob;
import org.apache.felix.ipojo.extender.internal.Lifecycle;
import org.apache.felix.ipojo.extender.queue.QueueService;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* This class is responsible to create the factory for a given type declaration. It also instructs the factories to
* create
* the instance for each instance declaration targeting the managed factory.
*/
public class ManagedType implements FactoryStateListener, Lifecycle {
/**
* Identify the factory creation job submitted to the QueueService.
*/
public static final String FACTORY_CREATION_JOB_TYPE = "factory.creation";
/**
* Identify the instance startup job submitted to the QueueService.
*/
public static final String INSTANCE_STARTUP_JOB_TYPE = "instance.startup";
/**
* The bundle context
*/
private final BundleContext m_bundleContext;
/**
* The queue service used for the creation.
*/
private final QueueService m_queueService;
/**
* The type declaration that we have to handle.
*/
private final TypeDeclaration m_declaration;
/**
* The service tracker tracking {@link ExtensionDeclaration} services.
*/
private ServiceTracker m_extensionTracker;
/**
* The service tracker tracking the {@link InstanceDeclaration} services.
*/
private ServiceTracker m_instanceTracker;
/**
* The job used to instantiate the factory.
*/
private Future<IPojoFactory> m_future;
/**
* If the Managed Type cannot be initializes, sets this flag to true and no links will be created.
*/
private boolean m_frozen;
/**
* Constructs a Managed Type object for the given type declaration.
*
* @param bundleContext the bundle context
* @param queueService the queue service
* @param declaration the declaration
*/
public ManagedType(BundleContext bundleContext, QueueService queueService, TypeDeclaration declaration) {
m_bundleContext = bundleContext;
m_queueService = queueService;
m_declaration = declaration;
try {
initExtensionTracker();
initInstanceTracker();
} catch (InvalidSyntaxException e) {
// Error during filter creation, freeze the declaration and add a meaningful message
m_frozen = true;
m_declaration.unbind("Filter creation error", e);
}
}
/**
* Initializes the extension declaration tracker.
*
* @throws InvalidSyntaxException cannot happen
*/
private void initExtensionTracker() throws InvalidSyntaxException {
String filter = format(
"(&(objectclass=%s)(%s=%s))",
ExtensionDeclaration.class.getName(),
ExtensionDeclaration.EXTENSION_NAME_PROPERTY,
m_declaration.getExtension()
);
m_extensionTracker = new ServiceTracker(m_bundleContext, m_bundleContext.createFilter(filter), new ExtensionSupport());
}
/**
* Initializes the instance declaration tracker.
*
* @throws InvalidSyntaxException cannot happen
*/
private void initInstanceTracker() throws InvalidSyntaxException {
String filter;
String version = m_declaration.getComponentVersion();
if (version != null) {
// Track instance for:
// * this component's name OR classname
// AND
// * this component's version OR no version
filter = format(
"(&(objectClass=%s)(|(%s=%s)(%s=%s))(|(%s=%s)(!(%s=*))))",
InstanceDeclaration.class.getName(),
InstanceDeclaration.COMPONENT_NAME_PROPERTY,
m_declaration.getComponentName(),
InstanceDeclaration.COMPONENT_NAME_PROPERTY,
getComponentClassname(),
InstanceDeclaration.COMPONENT_VERSION_PROPERTY,
version,
InstanceDeclaration.COMPONENT_VERSION_PROPERTY
);
} else {
// Track instance for:
// * this component's name OR classname
// AND
// * no version
filter = format(
"(&(objectClass=%s)(|(%s=%s)(%s=%s))(!(%s=*)))",
InstanceDeclaration.class.getName(),
InstanceDeclaration.COMPONENT_NAME_PROPERTY,
m_declaration.getComponentName(),
InstanceDeclaration.COMPONENT_NAME_PROPERTY,
getComponentClassname(),
InstanceDeclaration.COMPONENT_VERSION_PROPERTY
);
}
m_instanceTracker = new ServiceTracker(m_bundleContext, m_bundleContext.createFilter(filter), new InstanceSupport());
}
/**
* Returns the {@literal classname} attribute value.
*/
private String getComponentClassname() {
return m_declaration.getComponentMetadata().getAttribute("classname");
}
/**
* Starting the management.
* We open only the extension tracker.
*/
public void start() {
if (!m_frozen) {
m_extensionTracker.open(true);
}
}
/**
* Stopping the management.
*/
public void stop() {
m_instanceTracker.close();
m_extensionTracker.close();
}
/**
* The factory we have built has a state in his change.
*
* @param factory the changing factory
* @param newState the new factory state
*/
public void stateChanged(Factory factory, int newState) {
if (Factory.VALID == newState) {
// Start tracking instances
m_instanceTracker.open(true);
} else {
// Un-track all instances
m_instanceTracker.close();
}
}
/**
* The service tracker customizer for extension declaration.
* It submits a factory building job when an extension matching our type declaration is found.
*/
private class ExtensionSupport implements ServiceTrackerCustomizer {
public Object addingService(ServiceReference reference) {
final Object service = m_bundleContext.getService(reference);
if (service instanceof ExtensionDeclaration) {
m_future = m_queueService.submit(new DefaultJob<IPojoFactory>(reference.getBundle(), FACTORY_CREATION_JOB_TYPE) {
/**
* The factory creation job.
* @return the IPojoFactory
* @throws Exception the factory cannot be created
*/
public IPojoFactory call() throws Exception {
ExtensionDeclaration declaration = (ExtensionDeclaration) service;
try {
// Build and start the factory instance
IPojoFactory factory = declaration.getFactoryBuilder().build(m_bundleContext, m_declaration.getComponentMetadata());
factory.addFactoryStateListener(ManagedType.this);
factory.start();
// Change the status
m_declaration.bind();
return factory;
} catch (FactoryBuilderException e) {
m_declaration.unbind(format("Cannot build '%s' factory instance", m_declaration.getExtension()), e);
} catch (Throwable t) {
m_declaration.unbind(format("Error during '%s' factory instance creation", m_declaration.getExtension()), t);
}
return null;
}
}, format("Building Factory for type %s", m_declaration.getComponentName()));
// Return something, otherwise, ServiceTracker think that we're not interested
// in this service and never call us back on disposal.
return service;
}
return null;
}
public void modifiedService(ServiceReference reference, Object o) {
}
public void removedService(ServiceReference reference, Object o) {
ExtensionDeclaration extensionDeclaration = (ExtensionDeclaration) o;
// Then stop the factory
try {
IPojoFactory factory = m_future.get();
// It is possible that the factory couldn't be created
if (factory != null) {
factory.removeFactoryStateListener(ManagedType.this);
factory.dispose();
m_declaration.unbind(format("Extension '%s' is missing",
extensionDeclaration.getExtensionName()));
}
} catch (InterruptedException e) {
m_declaration.unbind("Could not create Factory", e);
} catch (ExecutionException e) {
m_declaration.unbind("Factory creation throw an Exception", e);
}
m_future = null;
}
}
private class InstanceSupport implements ServiceTrackerCustomizer {
public Object addingService(final ServiceReference reference) {
Object service = m_bundleContext.getService(reference);
if (service instanceof InstanceDeclaration) {
final InstanceDeclaration instanceDeclaration = (InstanceDeclaration) service;
// Check that instance is not already bound
if (instanceDeclaration.getStatus().isBound()) {
return null;
}
// Handle visibility (private/public factories)
if (!m_declaration.isPublic()) {
if (!reference.getBundle().equals(m_bundleContext.getBundle())) {
Bundle origin = m_bundleContext.getBundle();
instanceDeclaration.unbind(
format("Component '%s/%s' is private. It only accept instances " +
"from bundle %s/%s [%d] (instance bundle origin: %d)",
m_declaration.getComponentName(),
m_declaration.getComponentVersion(),
origin.getSymbolicName(),
origin.getVersion(),
origin.getBundleId(),
reference.getBundle().getBundleId())
);
return null;
}
}
return m_queueService.submit(new DefaultJob<ComponentInstance>(reference.getBundle(), INSTANCE_STARTUP_JOB_TYPE) {
public ComponentInstance call() throws Exception {
try {
// Create the component's instance
// It is automatically started
// Future.get should never be null since this tracker is started when the factory has been created
ComponentInstance instance = m_future.get().createComponentInstance(instanceDeclaration.getConfiguration());
// Notify the declaration that everything is fine
instanceDeclaration.bind();
return instance;
} catch (UnacceptableConfiguration c) {
instanceDeclaration.unbind(format("Instance configuration is invalid (component:%s/%s, bundle:%d)",
m_declaration.getComponentName(),
m_declaration.getComponentVersion(),
reference.getBundle().getBundleId()),
c);
} catch (MissingHandlerException e) {
instanceDeclaration.unbind(
format(
"Component '%s/%s' (required for instance creation) is missing some handlers",
m_declaration.getComponentName(),
m_declaration.getComponentVersion()
),
e);
} catch (ConfigurationException e) {
instanceDeclaration.unbind(
format(
"Instance configuration is incorrect for component '%s/%s'",
m_declaration.getComponentName(),
m_declaration.getComponentVersion()),
e);
}
return null;
}
}, format("Creating component instance of type %s (declaration from bundle %d)",
m_declaration.getComponentName(),
reference.getBundle().getBundleId()));
}
return null;
}
public void modifiedService(ServiceReference reference, Object o) {
}
public void removedService(ServiceReference reference, Object o) {
InstanceDeclaration instanceDeclaration = (InstanceDeclaration) m_bundleContext.getService(reference);
Future<ComponentInstance> future = (Future<ComponentInstance>) o;
ComponentInstance instance;
try {
instance = future.get();
// It is possible that the instance couldn't be created
if (instance != null) {
String message = format("Factory for Component '%s/%s' is missing",
instance.getFactory().getName(),
m_declaration.getComponentVersion());
instanceDeclaration.unbind(message);
instance.stop();
instance.dispose();
}
} catch (InterruptedException e) {
instanceDeclaration.unbind("Could not create ComponentInstance", e);
} catch (ExecutionException e) {
instanceDeclaration.unbind("ComponentInstance creation throw an Exception", e);
}
}
}
}