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