001/* 002 * (C) Copyright 2006-2018 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 Object object; 088 try { 089 object = ri.getContext().loadClass(ri.getImplementation()).newInstance(); 090 } catch (ReflectiveOperationException e) { 091 throw new RuntimeServiceException(e); 092 } 093 if (object instanceof Component) { 094 ((Component) object).setName(ri.getName().getName()); 095 } 096 return object; 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.getContext(); 110 } 111 112 @Override 113 public ComponentName getName() { 114 return ri.getName(); 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 if (instance != this) { 125 // try by reflection 126 Method meth = instance.getClass().getDeclaredMethod("activate", ComponentContext.class); 127 meth.setAccessible(true); 128 meth.invoke(instance, this); 129 } 130 registerServices(); 131 } catch (NoSuchMethodException e) { 132 // ignore this exception since the activate method is not mandatory 133 } catch (SecurityException | IllegalAccessException | InvocationTargetException e) { 134 handleError("Failed to activate component: " + getName(), e); 135 } 136 } 137 138 // TODO: cache info about implementation to avoid computing it each time 139 @Override 140 public void deactivate() { 141 // activate the implementation instance 142 try { 143 unregisterServices(); 144 if (instance instanceof Component) { 145 ((Component) instance).deactivate(this); 146 } else if (instance != this) { 147 // try by reflection 148 Method meth = instance.getClass().getDeclaredMethod("deactivate", ComponentContext.class); 149 meth.setAccessible(true); 150 meth.invoke(instance, this); 151 } 152 } catch (NoSuchMethodException e) { 153 // ignore this exception since the activate method is not mandatory 154 } catch (SecurityException | IllegalAccessException | InvocationTargetException e) { 155 handleError("Failed to deactivate component: " + getName(), e); 156 } 157 } 158 159 /** 160 * @since 9.3 161 */ 162 @Override 163 public void start() { 164 if (instance instanceof Component) { 165 ((Component) instance).start(this); 166 } 167 } 168 169 /** 170 * @since 9.3 171 */ 172 @Override 173 public void stop() throws InterruptedException { 174 if (instance instanceof Component) { 175 ((Component) instance).stop(this); 176 } 177 } 178 179 /** 180 * @deprecated since 9.3, but in fact since 5.6, only usage in {@link RegistrationInfoImpl} 181 */ 182 @Deprecated 183 @Override 184 public void reload() { 185 // activate the implementation instance 186 try { 187 Method meth = instance.getClass().getDeclaredMethod("reload", ComponentContext.class); 188 meth.setAccessible(true); 189 meth.invoke(instance, this); 190 } catch (NoSuchMethodException e) { 191 // ignore this exception since the reload method is not mandatory 192 } catch (ReflectiveOperationException e) { 193 handleError("Failed to reload component: " + getName(), e); 194 } 195 } 196 197 // TODO: cache info about implementation to avoid computing it each time 198 @Override 199 public void registerExtension(Extension extension) { 200 // if this the target extension point is extending another extension 201 // point from another component 202 // then delegate the registration to the that component component 203 Optional<ExtensionPoint> optXp = ri.getExtensionPoint(extension.getExtensionPoint()); 204 if (optXp.isPresent()) { 205 String superCo = optXp.get().getSuperComponent(); 206 if (superCo != null) { 207 // we don't implement extension point overriding for now in new implementation of RegistrationInfo 208 ((ExtensionImpl) extension).target = new ComponentName(superCo); 209 ((RegistrationInfoImpl) ri).manager.registerExtension(extension); 210 return; 211 } 212 // this extension is for us - register it 213 // activate the implementation instance 214 if (instance instanceof Component) { 215 ((Component) instance).registerExtension(extension); 216 } else if (instance != this) { 217 // try by reflection, avoiding stack overflow 218 try { 219 Method meth = instance.getClass().getDeclaredMethod("registerExtension", Extension.class); 220 meth.setAccessible(true); 221 meth.invoke(instance, extension); 222 } catch (ReflectiveOperationException e) { 223 handleError("Error registering " + extension.getComponent().getName(), e); 224 } 225 } 226 } else { 227 String message = "Warning: target extension point '" + extension.getExtensionPoint() + "' of '" 228 + extension.getTargetComponent().getName() + "' is unknown. Check your extension in component " 229 + extension.getComponent().getName(); 230 handleError(message, null); 231 } 232 } 233 234 // TODO: cache info about implementation to avoid computing it each time 235 @Override 236 public void unregisterExtension(Extension extension) { 237 // activate the implementation instance 238 if (instance instanceof Component) { 239 ((Component) instance).unregisterExtension(extension); 240 } else if (instance != this) { 241 // try by reflection, avoiding stack overflow 242 try { 243 Method meth = instance.getClass().getDeclaredMethod("unregisterExtension", Extension.class); 244 meth.setAccessible(true); 245 meth.invoke(instance, extension); 246 } catch (ReflectiveOperationException e) { 247 handleError("Error unregistering " + extension.getComponent().getName(), e); 248 } 249 } 250 } 251 252 protected void handleError(String message, Exception e) { 253 Exception ee = e; 254 if (e != null) { 255 ee = ExceptionUtils.unwrapInvoke(e); 256 } 257 log.error(message, ee); 258 Framework.getRuntime().getMessageHandler().addError(message); 259 } 260 261 @Override 262 public <T> T getAdapter(Class<T> adapter) { 263 T res = null; 264 Object object = getInstance(); 265 if (object == null) { 266 return null; 267 } 268 if (object instanceof Adaptable) { 269 res = ((Adaptable) object).getAdapter(adapter); 270 } else if (adapter.isAssignableFrom(object.getClass())) { 271 res = adapter.cast(object); 272 } 273 // to handle hot reload 274 if (res instanceof TimestampedService && object instanceof TimestampedService) { 275 Long lastModified = ((TimestampedService) object).getLastModified(); 276 ((TimestampedService) res).setLastModified(lastModified); 277 } 278 return res; 279 } 280 281 @Override 282 public String[] getPropertyNames() { 283 Set<String> set = ri.getProperties().keySet(); 284 return set.toArray(new String[set.size()]); 285 } 286 287 @Override 288 public Property getProperty(String property) { 289 return ri.getProperties().get(property); 290 } 291 292 @Override 293 public RuntimeContext getRuntimeContext() { 294 return ri.getContext(); 295 } 296 297 @Override 298 public Object getPropertyValue(String property) { 299 return getPropertyValue(property, null); 300 } 301 302 @Override 303 public Object getPropertyValue(String property, Object defValue) { 304 Property prop = getProperty(property); 305 if (prop != null) { 306 return prop.getValue(); 307 } else { 308 return defValue; 309 } 310 } 311 312 @Override 313 public String[] getProvidedServiceNames() { 314 return ri.getProvidedServiceNames(); 315 } 316 317 /** 318 * Register provided services as OSGi services 319 */ 320 public void registerServices() { 321 if (!Framework.isOSGiServiceSupported()) { 322 return; 323 } 324 String[] names = getProvidedServiceNames(); 325 if (names != null && names.length > 0) { 326 factories = new ArrayList<>(); 327 for (String className : names) { 328 OSGiServiceFactory factory = new OSGiServiceFactory(className); 329 factory.register(); 330 factories.add(factory); 331 } 332 } 333 } 334 335 public void unregisterServices() { 336 // TODO the reload method is not reloading services. do we want this? 337 if (factories != null) { 338 for (OSGiServiceFactory factory : factories) { 339 factory.unregister(); 340 } 341 factories = null; 342 } 343 } 344 345 @Override 346 public String toString() { 347 if (ri == null) { 348 return super.toString(); 349 } 350 return ri.toString(); 351 } 352 353 protected class OSGiServiceFactory implements ServiceFactory { 354 protected Class<?> clazz; 355 356 protected ServiceRegistration reg; 357 358 public OSGiServiceFactory(String className) { 359 this(ri.getContext().getBundle(), className); 360 } 361 362 public OSGiServiceFactory(Bundle bundle, String className) { 363 try { 364 clazz = ri.getContext().getBundle().loadClass(className); 365 } catch (ClassNotFoundException e) { 366 throw new RuntimeServiceException(e); 367 } 368 } 369 370 @Override 371 public Object getService(Bundle bundle, ServiceRegistration registration) { 372 return getAdapter(clazz); 373 } 374 375 @Override 376 public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { 377 // do nothing 378 } 379 380 public void register() { 381 reg = ri.getContext().getBundle().getBundleContext().registerService(clazz.getName(), this, null); 382 } 383 384 public void unregister() { 385 if (reg != null) { 386 reg.unregister(); 387 } 388 reg = null; 389 } 390 } 391 392 @Override 393 public RegistrationInfo getRegistrationInfo() { 394 return ri; 395 } 396 397}