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