001/* 002 * (C) Copyright 2006-2015 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 */ 020 021package org.nuxeo.runtime; 022 023import java.io.File; 024import java.net.URL; 025import java.time.Duration; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.List; 030import java.util.Map; 031import java.util.Map.Entry; 032import java.util.Set; 033import java.util.logging.Level; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.nuxeo.common.codec.CryptoProperties; 038import org.nuxeo.common.logging.JavaUtilLoggingHelper; 039import org.nuxeo.common.utils.TextTemplate; 040import org.nuxeo.runtime.api.Framework; 041import org.nuxeo.runtime.api.ServicePassivator; 042import org.nuxeo.runtime.model.ComponentInstance; 043import org.nuxeo.runtime.model.ComponentManager; 044import org.nuxeo.runtime.model.ComponentName; 045import org.nuxeo.runtime.model.RuntimeContext; 046import org.nuxeo.runtime.model.impl.ComponentManagerImpl; 047import org.nuxeo.runtime.model.impl.DefaultRuntimeContext; 048import org.osgi.framework.Bundle; 049 050/** 051 * Abstract implementation of the Runtime Service. 052 * <p> 053 * Implementors are encouraged to extend this class instead of directly implementing the {@link RuntimeService} 054 * interface. 055 * 056 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 057 */ 058public abstract class AbstractRuntimeService implements RuntimeService { 059 060 /** 061 * Property that controls whether or not to redirect JUL to JCL. By default is true (JUL will be redirected) 062 */ 063 public static final String REDIRECT_JUL = "org.nuxeo.runtime.redirectJUL"; 064 065 public static final String REDIRECT_JUL_THRESHOLD = "org.nuxeo.runtime.redirectJUL.threshold"; 066 067 private static final Log log = LogFactory.getLog(RuntimeService.class); 068 069 protected boolean isStarted = false; 070 071 protected boolean isShuttingDown = false; 072 073 protected File workingDir; 074 075 protected CryptoProperties properties = new CryptoProperties(System.getProperties()); 076 077 protected ComponentManager manager; 078 079 protected final RuntimeContext context; 080 081 protected final List<RuntimeExtension> extensions = new ArrayList<>(); 082 083 protected AbstractRuntimeService(DefaultRuntimeContext context) { 084 this(context, null); 085 } 086 087 // warnings during the deployment. Here are collected all errors occurred 088 // during the startup 089 protected final List<String> warnings = new ArrayList<>(); 090 091 protected AbstractRuntimeService(DefaultRuntimeContext context, Map<String, String> properties) { 092 this.context = context; 093 context.setRuntime(this); 094 if (properties != null) { 095 this.properties.putAll(properties); 096 } 097 // get errors set by NuxeoDeployer 098 String errs = System.getProperty("org.nuxeo.runtime.deployment.errors"); 099 if (errs != null) { 100 warnings.addAll(Arrays.asList(errs.split("\n"))); 101 System.clearProperty("org.nuxeo.runtime.deployment.errors"); 102 } 103 } 104 105 @Override 106 public List<String> getWarnings() { 107 return warnings; 108 } 109 110 protected ComponentManager createComponentManager() { 111 return new ComponentManagerImpl(this); 112 } 113 114 protected static URL getBuiltinFeatureURL() { 115 return Thread.currentThread() 116 .getContextClassLoader() 117 .getResource("org/nuxeo/runtime/nx-feature.xml"); 118 } 119 120 @Override 121 public synchronized void start() { 122 if (isStarted) { 123 return; 124 } 125 if (Boolean.parseBoolean(getProperty(REDIRECT_JUL, "false"))) { 126 Level threshold = Level.parse(getProperty(REDIRECT_JUL_THRESHOLD, "INFO").toUpperCase()); 127 JavaUtilLoggingHelper.redirectToApacheCommons(threshold); 128 } 129 log.info("Starting Nuxeo Runtime service " + getName() + "; version: " + getVersion()); 130 // NXRuntime.setInstance(this); 131 manager = createComponentManager(); 132 Framework.sendEvent(new RuntimeServiceEvent(RuntimeServiceEvent.RUNTIME_ABOUT_TO_START, this)); 133 ServicePassivator.passivate() 134 .withQuietDelay(Duration.ofSeconds(0)) 135 .monitor() 136 .withTimeout(Duration.ofSeconds(0)) 137 .withEnforceMode(false) 138 .await() 139 .proceed(() -> { 140 try { 141 doStart(); 142 startExtensions(); 143 } finally { 144 Framework.sendEvent(new RuntimeServiceEvent(RuntimeServiceEvent.RUNTIME_STARTED, this)); 145 isStarted = true; 146 } 147 }); 148 } 149 150 @Override 151 public synchronized void stop() { 152 if (!isStarted) { 153 return; 154 } 155 isShuttingDown = true; 156 try { 157 log.info("Stopping Nuxeo Runtime service " + getName() + "; version: " + getVersion()); 158 Framework.sendEvent(new RuntimeServiceEvent(RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP, this)); 159 ServicePassivator.passivate() 160 .withQuietDelay(Duration.ofSeconds(0)) 161 .monitor() 162 .withTimeout(Duration.ofSeconds(0)) 163 .withEnforceMode(false) 164 .await() 165 .proceed(() -> { 166 try { 167 stopExtensions(); 168 doStop(); 169 manager.shutdown(); 170 } finally { 171 isStarted = false; 172 Framework.sendEvent(new RuntimeServiceEvent(RuntimeServiceEvent.RUNTIME_STOPPED, this)); 173 manager = null; 174 } 175 }); 176 } finally { 177 JavaUtilLoggingHelper.reset(); 178 isShuttingDown = false; 179 } 180 } 181 182 @Override 183 public boolean isStarted() { 184 return isStarted; 185 } 186 187 @Override 188 public boolean isShuttingDown() { 189 return isShuttingDown; 190 } 191 192 protected void doStart() { 193 } 194 195 protected void doStop() { 196 } 197 198 @Override 199 public File getHome() { 200 return workingDir; 201 } 202 203 public void setHome(File home) { 204 workingDir = home; 205 } 206 207 @Override 208 public String getDescription() { 209 return toString(); 210 } 211 212 @Override 213 public CryptoProperties getProperties() { 214 // do not unreference properties: some methods rely on this to set 215 // variables here... 216 return properties; 217 } 218 219 @Override 220 public String getProperty(String name) { 221 return getProperty(name, null); 222 } 223 224 @Override 225 public String getProperty(String name, String defValue) { 226 String value = properties.getProperty(name, defValue); 227 if (value == null || ("${" + name + "}").equals(value)) { 228 // avoid loop, don't expand 229 return value; 230 } 231 return expandVars(value); 232 } 233 234 @Override 235 public void setProperty(String name, Object value) { 236 properties.setProperty(name, value.toString()); 237 } 238 239 @Override 240 public String toString() { 241 StringBuilder sb = new StringBuilder(); 242 return sb.append(getName()) 243 .append(" version ") 244 .append(getVersion().toString()) 245 .toString(); 246 } 247 248 @Override 249 public Object getComponent(String name) { 250 ComponentInstance co = getComponentInstance(name); 251 return co != null ? co.getInstance() : null; 252 } 253 254 @Override 255 public Object getComponent(ComponentName name) { 256 ComponentInstance co = getComponentInstance(name); 257 return co != null ? co.getInstance() : null; 258 } 259 260 @Override 261 public ComponentInstance getComponentInstance(String name) { 262 return manager.getComponent(new ComponentName(name)); 263 } 264 265 @Override 266 public ComponentInstance getComponentInstance(ComponentName name) { 267 return manager.getComponent(name); 268 } 269 270 @Override 271 public ComponentManager getComponentManager() { 272 return manager; 273 } 274 275 @Override 276 public RuntimeContext getContext() { 277 return context; 278 } 279 280 protected void startExtensions() { 281 for (RuntimeExtension ext : extensions) { 282 ext.start(); 283 } 284 } 285 286 protected void stopExtensions() { 287 for (RuntimeExtension ext : extensions) { 288 ext.stop(); 289 } 290 } 291 292 @Override 293 public <T> T getService(Class<T> serviceClass) { 294 return manager.getService(serviceClass); 295 } 296 297 @Override 298 public String expandVars(String expression) { 299 return new TextTemplate(properties).processText(expression); 300 } 301 302 @Override 303 public File getBundleFile(Bundle bundle) { 304 return null; 305 } 306 307 @Override 308 public Bundle getBundle(String symbolicName) { 309 throw new UnsupportedOperationException("Not implemented"); 310 } 311 312 /** 313 * @since 5.5 314 * @param msg summary message about all components loading status 315 * @return true if there was no detected error, else return false 316 */ 317 @Override 318 public boolean getStatusMessage(StringBuilder msg) { 319 String hr = "======================================================================"; 320 if (!warnings.isEmpty()) { 321 msg.append(hr) 322 .append("\n= Component Loading Errors:\n"); 323 for (String warning : warnings) { 324 msg.append(" * ") 325 .append(warning) 326 .append('\n'); 327 } 328 } 329 Map<ComponentName, Set<ComponentName>> pendingRegistrations = manager.getPendingRegistrations(); 330 Collection<ComponentName> unstartedRegistrations = manager.getActivatingRegistrations(); 331 unstartedRegistrations.addAll(manager.getStartFailureRegistrations()); 332 msg.append(hr) 333 .append("\n= Component Loading Status: Pending: ") 334 .append(pendingRegistrations.size()) 335 .append(" / Unstarted: ") 336 .append(unstartedRegistrations.size()) 337 .append(" / Total: ") 338 .append(manager.getRegistrations() 339 .size()) 340 .append('\n'); 341 for (Entry<ComponentName, Set<ComponentName>> e : pendingRegistrations.entrySet()) { 342 msg.append(" * ") 343 .append(e.getKey()) 344 .append(" requires ") 345 .append(e.getValue()) 346 .append('\n'); 347 } 348 for (ComponentName componentName : unstartedRegistrations) { 349 msg.append(" - ") 350 .append(componentName) 351 .append('\n'); 352 } 353 msg.append(hr); 354 return (warnings.isEmpty() && pendingRegistrations.isEmpty() && unstartedRegistrations.isEmpty()); 355 } 356 357}