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