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