/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) 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: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.runtime.model.impl; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.runtime.api.Framework; import org.eclipse.ecr.runtime.model.Adaptable; import org.eclipse.ecr.runtime.model.Component; import org.eclipse.ecr.runtime.model.ComponentContext; import org.eclipse.ecr.runtime.model.ComponentInstance; import org.eclipse.ecr.runtime.model.ComponentName; import org.eclipse.ecr.runtime.model.Extension; import org.eclipse.ecr.runtime.model.ExtensionPoint; import org.eclipse.ecr.runtime.model.Property; import org.eclipse.ecr.runtime.model.RegistrationInfo; import org.eclipse.ecr.runtime.model.ReloadableComponent; import org.eclipse.ecr.runtime.model.RuntimeContext; import org.osgi.framework.Bundle; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceRegistration; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> * */ public class ComponentInstanceImpl implements ComponentInstance { private static final Log log = LogFactory.getLog(ComponentInstanceImpl.class); protected Object instance; protected RegistrationInfoImpl ri; protected List<OSGiServiceFactory> factories; public ComponentInstanceImpl(RegistrationInfoImpl ri) throws Exception { this.ri = ri; if (ri.implementation == null) { //TODO //should be an extension component instance = this; } else { // TODO: load class only once when creating the registration info instance = this.ri.context.loadClass(this.ri.implementation) .newInstance(); } } @Override public Object getInstance() { switch (ri.state) { case RegistrationInfo.RESOLVED: // if not already activated activate it now try { ri.activate(); return instance; } catch (Exception e) { log.error(e); // fatal error if development mode - exit Framework.handleDevError(e); } return null; case RegistrationInfo.ACTIVATED: return instance; default: return null; } } public void create() throws Exception { if (ri.implementation == null) { instance = this; // should be an extension component } else { // TODO: load class only once when creating the reshgitration info instance = ri.context.loadClass(ri.implementation).newInstance(); } } @Override public void destroy() throws Exception { deactivate(); instance = null; ri = null; factories = null; } @Override public RuntimeContext getContext() { return ri.context; } @Override public ComponentName getName() { return ri.name; } // TODO: cache info about implementation to avoid computing it each time @Override public void activate() throws Exception { // activate the implementation instance try { if (instance instanceof Component) { ((Component) instance).activate(this); } else { // try by reflection Method meth = instance.getClass().getDeclaredMethod("activate", ComponentContext.class); meth.setAccessible(true); meth.invoke(instance, this); } registerServices(); } catch (NoSuchMethodException e) { // ignore this exception since the activate method is not mandatory } catch (Exception e) { log.error("Failed to activate component: "+getName(), e); Framework.handleDevError(e); } } // TODO: cache info about implementation to avoid computing it each time @Override public void deactivate() throws Exception { // activate the implementation instance try { unregisterServices(); if (instance instanceof Component) { ((Component) instance).deactivate(this); } else { // try by reflection Method meth = instance.getClass().getDeclaredMethod( "deactivate", ComponentContext.class); meth.setAccessible(true); meth.invoke(instance, this); } } catch (NoSuchMethodException e) { // ignore this exception since the activate method is not mandatory } catch (Exception e) { log.error("Failed to deactivate component: "+getName(), e); Framework.handleDevError(e); } } @Override public void reload() throws Exception { // activate the implementation instance try { if (instance instanceof ReloadableComponent) { ((ReloadableComponent) instance).reload(this); } else { Method meth = instance.getClass().getDeclaredMethod("reload", ComponentContext.class); meth.setAccessible(true); meth.invoke(instance, this); } } catch (NoSuchMethodException e) { // ignore this exception since the reload method is not mandatory } catch (Exception e) { log.error("Failed to reload component: "+getName(), e); Framework.handleDevError(e); } } // TODO: cache info about implementation to avoid computing it each time @Override public void registerExtension(Extension extension) throws Exception { // if this the target extension point is extending another extension point from another component // then delegate the registration to the that component component ExtensionPoint xp = ri.getExtensionPoint(extension.getExtensionPoint()); if (xp != null) { String superCo = xp.getSuperComponent(); if (superCo != null) { ((ExtensionImpl)extension).target = new ComponentName(superCo); ri.manager.registerExtension(extension); return; } } else { log.error("Warning: target extension point '" + extension.getExtensionPoint() + "' of '" + extension.getTargetComponent().getName() + "' is unknown. Check your extension in component " + extension.getComponent().getName()); // fatal error if development mode - exit Framework.handleDevError(null); } // this extension is for us - register it // activate the implementation instance if (instance instanceof Component) { ((Component) instance).registerExtension(extension); } else { // try by reflection try { Method meth = instance.getClass().getDeclaredMethod( "registerExtension", Extension.class); meth.setAccessible(true); meth.invoke(instance, extension); } catch (Exception e) { // no such method Framework.handleDevError(e); } } } // TODO: cache info about implementation to avoid computing it each time @Override public void unregisterExtension(Extension extension) throws Exception { // activate the implementation instance if (instance instanceof Component) { ((Component) instance).unregisterExtension(extension); } else { // try by reflection try { Method meth = instance.getClass().getDeclaredMethod( "unregisterExtension", Extension.class); meth.setAccessible(true); meth.invoke(instance, extension); } catch (Exception e) { // no such method Framework.handleDevError(e); } } } @Override public <T> T getAdapter(Class<T> adapter) { Object object = getInstance(); if (object instanceof Adaptable) { return ((Adaptable) object).getAdapter(adapter); } if (adapter.isAssignableFrom(object.getClass())) { return adapter.cast(object); } return null; } @Override public String[] getPropertyNames() { Set<String> set = ri.getProperties().keySet(); return set.toArray(new String[set.size()]); } @Override public Property getProperty(String property) { return ri.getProperties().get(property); } @Override public RuntimeContext getRuntimeContext() { return ri.getContext(); } @Override public Object getPropertyValue(String property) { return getPropertyValue(property, null); } @Override public Object getPropertyValue(String property, Object defValue) { Property prop = getProperty(property); if (prop != null) { return prop.getValue(); } else { return defValue; } } @Override public String[] getProvidedServiceNames() { return ri.getProvidedServiceNames(); } /** * Register provided services as OSGi services */ public void registerServices() throws Exception { if (!Framework.isOSGiServiceSupported()) { return; } String[] names = getProvidedServiceNames(); if (names != null && names.length > 0) { factories = new ArrayList<ComponentInstanceImpl.OSGiServiceFactory>(); for (String className : names) { OSGiServiceFactory factory = new OSGiServiceFactory(className); factory.register(); factories.add(factory); } } } public void unregisterServices() throws Exception { //TODO the reload method is not reloading services. do we want this? if (factories != null) { for (OSGiServiceFactory factory : factories) { factory.unregister(); } factories = null; } } @Override public String toString() { if (ri == null) { return super.toString(); } return ri.toString(); } protected class OSGiServiceFactory implements ServiceFactory { protected Class<?> clazz; protected ServiceRegistration reg; public OSGiServiceFactory(String className) throws Exception { this (ri.getContext().getBundle(), className); } public OSGiServiceFactory(Bundle bundle, String className) throws Exception { clazz = ri.getContext().getBundle().loadClass(className); } @Override public Object getService(Bundle bundle, ServiceRegistration registration) { return getAdapter(clazz); } @Override public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { // do nothing } public void register() { reg = ri.getContext().getBundle().getBundleContext().registerService(clazz.getName(), this, null); } public void unregister() { if (reg != null) { reg.unregister(); } reg = null; } } }