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