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