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