001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Nuxeo - initial API and implementation 011 * 012 * $Id$ 013 */ 014 015package org.nuxeo.runtime.model.impl; 016 017import java.lang.reflect.InvocationTargetException; 018import java.lang.reflect.Method; 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Set; 022 023import org.apache.commons.logging.Log; 024import org.apache.commons.logging.LogFactory; 025import org.nuxeo.common.utils.ExceptionUtils; 026import org.nuxeo.runtime.RuntimeServiceException; 027import org.nuxeo.runtime.api.Framework; 028import org.nuxeo.runtime.model.Adaptable; 029import org.nuxeo.runtime.model.Component; 030import org.nuxeo.runtime.model.ComponentContext; 031import org.nuxeo.runtime.model.ComponentInstance; 032import org.nuxeo.runtime.model.ComponentName; 033import org.nuxeo.runtime.model.Extension; 034import org.nuxeo.runtime.model.ExtensionPoint; 035import org.nuxeo.runtime.model.Property; 036import org.nuxeo.runtime.model.RegistrationInfo; 037import org.nuxeo.runtime.model.ReloadableComponent; 038import org.nuxeo.runtime.model.RuntimeContext; 039import org.nuxeo.runtime.service.TimestampedService; 040import org.osgi.framework.Bundle; 041import org.osgi.framework.ServiceFactory; 042import org.osgi.framework.ServiceRegistration; 043 044/** 045 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 046 */ 047public class ComponentInstanceImpl implements ComponentInstance { 048 049 private static final Log log = LogFactory.getLog(ComponentInstanceImpl.class); 050 051 protected Object instance; 052 053 protected RegistrationInfoImpl ri; 054 055 protected List<OSGiServiceFactory> factories; 056 057 public ComponentInstanceImpl(RegistrationInfoImpl ri) { 058 this.ri = ri; 059 if (ri.implementation == null) { 060 // TODO: should be an extension component 061 instance = this; 062 } else { 063 // TODO: load class only once when creating the registration info 064 instance = createInstance(); 065 } 066 } 067 068 @Override 069 public Object getInstance() { 070 switch (ri.state) { 071 case RegistrationInfo.RESOLVED: 072 // if not already activated activate it now 073 ri.activate(); 074 return instance; 075 case RegistrationInfo.ACTIVATED: 076 return instance; 077 default: 078 return null; 079 } 080 } 081 082 public void create() { 083 if (ri.implementation == null) { 084 instance = this; // should be an extension component 085 } else { 086 // TODO: load class only once when creating the reshgitration info 087 instance = createInstance(); 088 } 089 } 090 091 protected Object createInstance() { 092 try { 093 return ri.context.loadClass(ri.implementation).newInstance(); 094 } catch (ReflectiveOperationException e) { 095 throw new RuntimeServiceException(e); 096 } 097 } 098 099 @Override 100 public void destroy() { 101 deactivate(); 102 instance = null; 103 ri = null; 104 factories = null; 105 } 106 107 @Override 108 public RuntimeContext getContext() { 109 return ri.context; 110 } 111 112 @Override 113 public ComponentName getName() { 114 return ri.name; 115 } 116 117 // TODO: cache info about implementation to avoid computing it each time 118 @Override 119 public void activate() { 120 // activate the implementation instance 121 try { 122 if (instance instanceof Component) { 123 ((Component) instance).activate(this); 124 } else { // try by reflection 125 Method meth = instance.getClass().getDeclaredMethod("activate", ComponentContext.class); 126 meth.setAccessible(true); 127 meth.invoke(instance, this); 128 } 129 registerServices(); 130 } catch (NoSuchMethodException e) { 131 // ignore this exception since the activate method is not mandatory 132 } catch (SecurityException | IllegalAccessException | InvocationTargetException e) { 133 handleError("Failed to activate component: " + getName(), e); 134 } 135 } 136 137 // TODO: cache info about implementation to avoid computing it each time 138 @Override 139 public void deactivate() { 140 // activate the implementation instance 141 try { 142 unregisterServices(); 143 if (instance instanceof Component) { 144 ((Component) instance).deactivate(this); 145 } else { 146 // try by reflection 147 Method meth = instance.getClass().getDeclaredMethod("deactivate", ComponentContext.class); 148 meth.setAccessible(true); 149 meth.invoke(instance, this); 150 } 151 } catch (NoSuchMethodException e) { 152 // ignore this exception since the activate method is not mandatory 153 } catch (SecurityException | IllegalAccessException | InvocationTargetException e) { 154 handleError("Failed to deactivate component: " + getName(), e); 155 } 156 } 157 158 @Override 159 public void reload() { 160 // activate the implementation instance 161 try { 162 if (instance instanceof ReloadableComponent) { 163 ((ReloadableComponent) instance).reload(this); 164 } else { 165 Method meth = instance.getClass().getDeclaredMethod("reload", ComponentContext.class); 166 meth.setAccessible(true); 167 meth.invoke(instance, this); 168 } 169 } catch (NoSuchMethodException e) { 170 // ignore this exception since the reload method is not mandatory 171 } catch (ReflectiveOperationException e) { 172 handleError("Failed to reload component: " + getName(), e); 173 } 174 } 175 176 // TODO: cache info about implementation to avoid computing it each time 177 @Override 178 public void registerExtension(Extension extension) { 179 // if this the target extension point is extending another extension 180 // point from another component 181 // then delegate the registration to the that component component 182 ExtensionPoint xp = ri.getExtensionPoint(extension.getExtensionPoint()); 183 if (xp != null) { 184 String superCo = xp.getSuperComponent(); 185 if (superCo != null) { 186 ((ExtensionImpl) extension).target = new ComponentName(superCo); 187 ri.manager.registerExtension(extension); 188 return; 189 } 190 // this extension is for us - register it 191 // activate the implementation instance 192 if (instance instanceof Component) { 193 ((Component) instance).registerExtension(extension); 194 } else if (instance != this) { 195 // try by reflection, avoiding stack overflow 196 try { 197 Method meth = instance.getClass().getDeclaredMethod("registerExtension", Extension.class); 198 meth.setAccessible(true); 199 meth.invoke(instance, extension); 200 } catch (ReflectiveOperationException e) { 201 handleError("Error registering " + extension.getComponent().getName(), e); 202 } 203 } 204 } else { 205 String message = "Warning: target extension point '" + extension.getExtensionPoint() + "' of '" 206 + extension.getTargetComponent().getName() + "' is unknown. Check your extension in component " 207 + extension.getComponent().getName(); 208 handleError(message, null); 209 } 210 } 211 212 // TODO: cache info about implementation to avoid computing it each time 213 @Override 214 public void unregisterExtension(Extension extension) { 215 // activate the implementation instance 216 if (instance instanceof Component) { 217 ((Component) instance).unregisterExtension(extension); 218 } else if (instance != this) { 219 // try by reflection, avoiding stack overflow 220 try { 221 Method meth = instance.getClass().getDeclaredMethod("unregisterExtension", Extension.class); 222 meth.setAccessible(true); 223 meth.invoke(instance, extension); 224 } catch (ReflectiveOperationException e) { 225 handleError("Error unregistering " + extension.getComponent().getName(), e); 226 } 227 } 228 } 229 230 protected void handleError(String message, Exception e) { 231 Exception ee = e; 232 if (e != null) { 233 ee = ExceptionUtils.unwrapInvoke(e); 234 } 235 log.error(message, ee); 236 Framework.getRuntime().getWarnings().add(message); 237 Framework.handleDevError(ee); 238 } 239 240 @Override 241 public <T> T getAdapter(Class<T> adapter) { 242 T res = null; 243 Object object = getInstance(); 244 if (object instanceof Adaptable) { 245 res = ((Adaptable) object).getAdapter(adapter); 246 } else if (adapter.isAssignableFrom(object.getClass())) { 247 res = adapter.cast(object); 248 } 249 // to handle hot reload 250 if (res instanceof TimestampedService && object instanceof TimestampedService) { 251 Long lastModified = ((TimestampedService) object).getLastModified(); 252 ((TimestampedService) res).setLastModified(lastModified); 253 } 254 return res; 255 } 256 257 @Override 258 public String[] getPropertyNames() { 259 Set<String> set = ri.getProperties().keySet(); 260 return set.toArray(new String[set.size()]); 261 } 262 263 @Override 264 public Property getProperty(String property) { 265 return ri.getProperties().get(property); 266 } 267 268 @Override 269 public RuntimeContext getRuntimeContext() { 270 return ri.getContext(); 271 } 272 273 @Override 274 public Object getPropertyValue(String property) { 275 return getPropertyValue(property, null); 276 } 277 278 @Override 279 public Object getPropertyValue(String property, Object defValue) { 280 Property prop = getProperty(property); 281 if (prop != null) { 282 return prop.getValue(); 283 } else { 284 return defValue; 285 } 286 } 287 288 @Override 289 public String[] getProvidedServiceNames() { 290 return ri.getProvidedServiceNames(); 291 } 292 293 /** 294 * Register provided services as OSGi services 295 */ 296 public void registerServices() { 297 if (!Framework.isOSGiServiceSupported()) { 298 return; 299 } 300 String[] names = getProvidedServiceNames(); 301 if (names != null && names.length > 0) { 302 factories = new ArrayList<ComponentInstanceImpl.OSGiServiceFactory>(); 303 for (String className : names) { 304 OSGiServiceFactory factory = new OSGiServiceFactory(className); 305 factory.register(); 306 factories.add(factory); 307 } 308 } 309 } 310 311 public void unregisterServices() { 312 // TODO the reload method is not reloading services. do we want this? 313 if (factories != null) { 314 for (OSGiServiceFactory factory : factories) { 315 factory.unregister(); 316 } 317 factories = null; 318 } 319 } 320 321 @Override 322 public String toString() { 323 if (ri == null) { 324 return super.toString(); 325 } 326 return ri.toString(); 327 } 328 329 protected class OSGiServiceFactory implements ServiceFactory { 330 protected Class<?> clazz; 331 332 protected ServiceRegistration reg; 333 334 public OSGiServiceFactory(String className) { 335 this(ri.getContext().getBundle(), className); 336 } 337 338 public OSGiServiceFactory(Bundle bundle, String className) { 339 try { 340 clazz = ri.getContext().getBundle().loadClass(className); 341 } catch (ClassNotFoundException e) { 342 throw new RuntimeServiceException(e); 343 } 344 } 345 346 @Override 347 public Object getService(Bundle bundle, ServiceRegistration registration) { 348 return getAdapter(clazz); 349 } 350 351 @Override 352 public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { 353 // do nothing 354 } 355 356 public void register() { 357 reg = ri.getContext().getBundle().getBundleContext().registerService(clazz.getName(), this, null); 358 } 359 360 public void unregister() { 361 if (reg != null) { 362 reg.unregister(); 363 } 364 reg = null; 365 } 366 } 367 368}