| /* |
| * Copyright 2005 The Apache Software Foundation |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.core; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Constructor; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.apache.avalon.excalibur.logger.Log4JConfLoggerManager; |
| import org.apache.avalon.excalibur.logger.LoggerManageable; |
| import org.apache.avalon.excalibur.logger.LoggerManager; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.configuration.DefaultConfiguration; |
| import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; |
| import org.apache.avalon.framework.container.ContainerUtil; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.DefaultContext; |
| import org.apache.avalon.framework.logger.Logger; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.cocoon.Cocoon; |
| import org.apache.cocoon.Constants; |
| import org.apache.cocoon.Modifiable; |
| import org.apache.cocoon.Processor; |
| import org.apache.cocoon.components.ContextHelper; |
| import org.apache.cocoon.components.container.ComponentContext; |
| import org.apache.cocoon.configuration.ConfigurationBuilder; |
| import org.apache.cocoon.core.container.SingleComponentServiceManager; |
| import org.apache.cocoon.core.logging.CocoonLogKitLoggerManager; |
| import org.apache.cocoon.core.logging.PerRequestLoggerManager; |
| import org.apache.cocoon.core.logging.SettingsContext; |
| import org.apache.cocoon.core.source.SimpleSourceResolver; |
| import org.apache.cocoon.environment.Environment; |
| import org.apache.cocoon.matching.helpers.WildcardHelper; |
| import org.apache.cocoon.util.ClassUtils; |
| import org.apache.cocoon.util.StringUtils; |
| import org.apache.excalibur.source.Source; |
| import org.apache.excalibur.source.SourceResolver; |
| import org.apache.excalibur.source.TraversableSource; |
| |
| /** |
| * This is an utility class to create a new Cocoon instance. |
| * |
| * TODO - Remove dependencies to LogKit and Log4J |
| * |
| * @version $Id$ |
| * @since 2.2 |
| */ |
| public class CoreUtil { |
| |
| /** Parameter map for the context protocol */ |
| protected static final Map CONTEXT_PARAMETERS = Collections.singletonMap("force-traversable", Boolean.TRUE); |
| |
| /** The callback to the real environment. */ |
| protected final BootstrapEnvironment env; |
| |
| /** "legacy" support: create an avalon context. */ |
| protected final DefaultContext appContext = new ComponentContext(); |
| |
| /** The settings. */ |
| protected MutableSettings settings; |
| |
| /** The parent service manager. */ |
| protected ServiceManager parentManager; |
| |
| /** The root logger. */ |
| protected Logger log; |
| |
| /** The logger manager. */ |
| protected LoggerManager loggerManager; |
| |
| /** The Root processor instance */ |
| protected Processor processor; |
| |
| /** Is this a per request logger manager */ |
| protected boolean isPerRequestLoggerManager = false; |
| |
| protected ClassLoader classloader; |
| |
| /** |
| * Setup a new instance. |
| * @param environment The hook back to the environment. |
| * @throws Exception |
| */ |
| public CoreUtil(BootstrapEnvironment environment) |
| throws Exception { |
| this.env = environment; |
| this.init(); |
| this.createClassloader(); |
| } |
| |
| protected void init() |
| throws Exception { |
| // first let's set up the appContext with some values to make |
| // the simple source resolver work |
| |
| // add root url |
| try { |
| appContext.put(ContextHelper.CONTEXT_ROOT_URL, |
| new URL(this.env.getContextURL())); |
| } catch (MalformedURLException ignore) { |
| // we simply ignore this |
| } |
| |
| // add environment context |
| this.appContext.put(Constants.CONTEXT_ENVIRONMENT_CONTEXT, |
| this.env.getEnvironmentContext()); |
| |
| // now add environment specific information |
| this.env.configure(appContext); |
| |
| // create settings |
| this.settings = this.createSettings(); |
| |
| // first init the work-directory for the logger. |
| // this is required if we are running inside a war file! |
| final String workDirParam = this.settings.getWorkDirectory(); |
| File workDir; |
| if (workDirParam != null) { |
| if (this.env.getContextForWriting() == null) { |
| // No context path : consider work-directory as absolute |
| workDir = new File(workDirParam); |
| } else { |
| // Context path exists : is work-directory absolute ? |
| File workDirParamFile = new File(workDirParam); |
| if (workDirParamFile.isAbsolute()) { |
| // Yes : keep it as is |
| workDir = workDirParamFile; |
| } else { |
| // No : consider it relative to context path |
| workDir = new File(this.env.getContextForWriting(), workDirParam); |
| } |
| } |
| } else { |
| workDir = new File("cocoon-files"); |
| } |
| workDir.mkdirs(); |
| this.appContext.put(Constants.CONTEXT_WORK_DIR, workDir); |
| this.settings.setWorkDirectory(workDir.getAbsolutePath()); |
| |
| // Init logger |
| this.initLogger(); |
| this.env.setLogger(this.log); |
| |
| // Output some debug info |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Context URL: " + this.env.getContextURL()); |
| this.log.debug("Writeable Context: " + this.env.getContextForWriting()); |
| if (workDirParam != null) { |
| this.log.debug("Using work-directory " + workDir); |
| } else { |
| this.log.debug("Using default work-directory " + workDir); |
| } |
| } |
| |
| final String uploadDirParam = this.settings.getUploadDirectory(); |
| File uploadDir; |
| if (uploadDirParam != null) { |
| if (this.env.getContextForWriting() == null) { |
| uploadDir = new File(uploadDirParam); |
| } else { |
| // Context path exists : is upload-directory absolute ? |
| File uploadDirParamFile = new File(uploadDirParam); |
| if (uploadDirParamFile.isAbsolute()) { |
| // Yes : keep it as is |
| uploadDir = uploadDirParamFile; |
| } else { |
| // No : consider it relative to context path |
| uploadDir = new File(this.env.getContextForWriting(), uploadDirParam); |
| } |
| } |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Using upload-directory " + uploadDir); |
| } |
| } else { |
| uploadDir = new File(workDir, "upload-dir" + File.separator); |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Using default upload-directory " + uploadDir); |
| } |
| } |
| uploadDir.mkdirs(); |
| appContext.put(Constants.CONTEXT_UPLOAD_DIR, uploadDir); |
| this.settings.setUploadDirectory(uploadDir.getAbsolutePath()); |
| |
| String cacheDirParam = this.settings.getCacheDirectory(); |
| File cacheDir; |
| if (cacheDirParam != null) { |
| if (this.env.getContextForWriting() == null) { |
| cacheDir = new File(cacheDirParam); |
| } else { |
| // Context path exists : is cache-directory absolute ? |
| File cacheDirParamFile = new File(cacheDirParam); |
| if (cacheDirParamFile.isAbsolute()) { |
| // Yes : keep it as is |
| cacheDir = cacheDirParamFile; |
| } else { |
| // No : consider it relative to context path |
| cacheDir = new File(this.env.getContextForWriting(), cacheDirParam); |
| } |
| } |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Using cache-directory " + cacheDir); |
| } |
| } else { |
| cacheDir = new File(workDir, "cache-dir" + File.separator); |
| File parent = cacheDir.getParentFile(); |
| if (parent != null) { |
| parent.mkdirs(); |
| } |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("cache-directory was not set - defaulting to " + cacheDir); |
| } |
| } |
| cacheDir.mkdirs(); |
| appContext.put(Constants.CONTEXT_CACHE_DIR, cacheDir); |
| this.settings.setCacheDirectory(cacheDir.getAbsolutePath()); |
| |
| // update configuration |
| final URL u = this.env.getConfigFile(this.settings.getConfiguration()); |
| this.settings.setConfiguration(u.toExternalForm()); |
| this.appContext.put(Constants.CONTEXT_CONFIG_URL, u); |
| |
| // set encoding |
| this.appContext.put(Constants.CONTEXT_DEFAULT_ENCODING, settings.getFormEncoding()); |
| |
| // set class loader |
| this.appContext.put(Constants.CONTEXT_CLASS_LOADER, this.classloader); |
| |
| // create the Core object |
| final Core core = this.createCore(); |
| |
| // create parent service manager |
| this.parentManager = this.getParentServiceManager(core); |
| |
| // settings can't be changed anymore |
| settings.makeReadOnly(); |
| |
| // put the core into the context - this is for internal use only |
| // The Cocoon container fetches the Core object using the context. |
| this.appContext.put(Core.ROLE, core); |
| } |
| |
| public Core getCore() { |
| try { |
| return (Core)this.parentManager.lookup(Core.ROLE); |
| } catch (ServiceException neverIgnore) { |
| // this should never happen! |
| throw new CoreFatalException("Fatal exception: no Cocoon core available.", neverIgnore); |
| } |
| } |
| |
| /** |
| * Create a new core instance. |
| * This method can be overwritten in sub classes. |
| * @return A new core object. |
| */ |
| protected Core createCore() { |
| final Core c = new Core(this.settings, this.appContext); |
| return c; |
| } |
| |
| /** |
| * Return the settings object. |
| */ |
| public Settings getSettings() { |
| return this.settings; |
| } |
| |
| /** |
| * Instatiates the parent service manager, as specified in the |
| * parent-service-manager init parameter. |
| * |
| * If none is specified, the method returns <code>null</code>. |
| * |
| * @return the parent service manager, or <code>null</code>. |
| */ |
| protected ServiceManager getParentServiceManager(Core core) { |
| String parentServiceManagerClass = this.settings.getParentServiceManagerClassName(); |
| String parentServiceManagerInitParam = null; |
| if (parentServiceManagerClass != null) { |
| int dividerPos = parentServiceManagerClass.indexOf('/'); |
| if (dividerPos != -1) { |
| parentServiceManagerInitParam = parentServiceManagerInitParam.substring(dividerPos + 1); |
| parentServiceManagerClass = parentServiceManagerClass.substring(0, dividerPos); |
| } |
| } |
| |
| ServiceManager parentServiceManager = null; |
| if (parentServiceManagerClass != null) { |
| try { |
| Class pcm = ClassUtils.loadClass(parentServiceManagerClass); |
| Constructor pcmc = pcm.getConstructor(new Class[]{String.class}); |
| parentServiceManager = (ServiceManager) pcmc.newInstance(new Object[]{parentServiceManagerInitParam}); |
| |
| ContainerUtil.enableLogging(parentServiceManager, this.log); |
| ContainerUtil.contextualize(parentServiceManager, this.appContext); |
| ContainerUtil.initialize(parentServiceManager); |
| } catch (Exception e) { |
| if (this.log.isErrorEnabled()) { |
| this.log.error("Could not initialize parent component manager.", e); |
| } |
| } |
| } |
| return new SingleComponentServiceManager(parentServiceManager, core, Core.ROLE); |
| } |
| |
| /** |
| * Get the settings for Cocoon. |
| * This method reads several property files and merges the result. If there |
| * is more than one definition for a property, the last one wins. |
| * The property files are read in the following order: |
| * 1) context://WEB-INF/properties/*.properties |
| * Default values for the core and each block - the order in which the files are read is not guaranteed. |
| * 2) context://WEB-INF/properties/[RUNNING_MODE]/*.properties |
| * Default values for the running mode - the order in which the files are read is not guaranteed. |
| * 3) Property providers (ToBeDocumented) |
| * 4) The environment (CLI, Servlet etc.) adds own properties (e.g. from web.xml) |
| * 5) Additional property file specified by the "org.apache.cocoon.settings" system property or |
| * if the property is not found, the file ".cocoon/settings.properties" is tried to be read from |
| * the user directory. |
| * 6) System properties |
| * |
| * @return A new Settings object |
| */ |
| protected MutableSettings createSettings() { |
| // get the running mode |
| final String mode = System.getProperty(Settings.PROPERTY_RUNNING_MODE, Settings.DEFAULT_RUNNING_MODE); |
| this.env.log("Running in mode: " + mode); |
| |
| // create an empty settings objects |
| final MutableSettings s = new MutableSettings(); |
| |
| // we need our own resolver |
| final SourceResolver resolver = this.createSourceResolver(new LoggerWrapper(this.env)); |
| |
| // now read all properties from the properties directory |
| this.readProperties("context://WEB-INF/properties", s, resolver); |
| // read all properties from the mode dependent directory |
| this.readProperties("context://WEB-INF/properties/" + mode, s, resolver); |
| |
| // Next look for custom property providers |
| Iterator i = s.getPropertyProviders().iterator(); |
| while ( i.hasNext() ) { |
| final String className = (String)i.next(); |
| try { |
| PropertyProvider provider = (PropertyProvider)ClassUtils.newInstance(className); |
| s.fill(provider.getProperties()); |
| } catch (Exception ignore) { |
| env.log("Unable to get property provider for class " + className, ignore); |
| env.log("Continuing initialization."); |
| } |
| } |
| // fill from the environment configuration, like web.xml etc. |
| env.configure(s); |
| |
| // read additional properties file |
| String additionalPropertyFile = s.getProperty(Settings.PROPERTY_USER_SETTINGS, |
| System.getProperty(Settings.PROPERTY_USER_SETTINGS)); |
| // if there is no property defining the addition file, we try it in the home directory |
| if ( additionalPropertyFile == null ) { |
| additionalPropertyFile = System.getProperty("user.home") + File.separator + ".cocoon/settings.properties"; |
| final File testFile = new File(additionalPropertyFile); |
| if ( !testFile.exists() ) { |
| additionalPropertyFile = null; |
| } |
| } |
| if ( additionalPropertyFile != null ) { |
| env.log("Reading user settings from '" + additionalPropertyFile + "'"); |
| final Properties p = new Properties(); |
| try { |
| FileInputStream fis = new FileInputStream(additionalPropertyFile); |
| p.load(fis); |
| fis.close(); |
| } catch (IOException ignore) { |
| env.log("Unable to read '" + additionalPropertyFile + "'.", ignore); |
| env.log("Continuing initialization."); |
| } |
| } |
| // now overwrite with system properties |
| s.fill(System.getProperties()); |
| |
| return s; |
| } |
| |
| /** |
| * Read all property files from the given directory and apply them to the settings. |
| */ |
| protected void readProperties(String directoryName, |
| MutableSettings s, |
| SourceResolver resolver) { |
| Source directory = null; |
| try { |
| directory = resolver.resolveURI(directoryName, null, CONTEXT_PARAMETERS); |
| if (directory.exists() && directory instanceof TraversableSource) { |
| final Iterator c = ((TraversableSource) directory).getChildren().iterator(); |
| while (c.hasNext()) { |
| final Source src = (Source) c.next(); |
| if ( src.getURI().endsWith(".properties") ) { |
| final InputStream propsIS = src.getInputStream(); |
| env.log("Reading settings from '" + src.getURI() + "'."); |
| final Properties p = new Properties(); |
| p.load(propsIS); |
| propsIS.close(); |
| s.fill(p); |
| } |
| } |
| } |
| } catch (IOException ignore) { |
| env.log("Unable to read from directory 'WEB-INF/properties'.", ignore); |
| env.log("Continuing initialization."); |
| } finally { |
| resolver.release(directory); |
| } |
| } |
| |
| /** |
| * Initialize the current request. |
| * This method can be used to initialize anything required for processing |
| * the request. For example, if the logger manager is a {@link PerRequestLoggerManager} |
| * than this manager is invoked to initialize the logging context for the request. |
| * This method returns a handle that should be used to clean up everything |
| * when the request is finished by calling {@link #cleanUpRequest(Object)}. |
| */ |
| public Object initializeRequest(Environment env) { |
| if ( this.isPerRequestLoggerManager ) { |
| return ((PerRequestLoggerManager)this.loggerManager).initializePerRequestLoggingContext(env); |
| } |
| return null; |
| } |
| |
| /** |
| * Cleanup everything initialized during the request processing in |
| * {@link #initializeRequest(Environment)}. |
| */ |
| public void cleanUpRequest(Object handle) { |
| if ( handle != null && this.isPerRequestLoggerManager) { |
| ((PerRequestLoggerManager)this.loggerManager).cleanPerRequestLoggingContext(handle); |
| } |
| } |
| |
| /** |
| * Create a simple source resolver. |
| */ |
| protected SourceResolver createSourceResolver(Logger logger) { |
| // Create our own resolver |
| final SimpleSourceResolver resolver = new SimpleSourceResolver(); |
| resolver.enableLogging(logger); |
| try { |
| resolver.contextualize(this.appContext); |
| } catch (ContextException ce) { |
| throw new CoreInitializationException( |
| "Cannot setup source resolver.", ce); |
| } |
| return resolver; |
| } |
| |
| protected void initLogger() { |
| String logLevel = settings.getBootstrapLogLevel(); |
| if (logLevel == null) { |
| logLevel = "INFO"; |
| } |
| |
| String accesslogger = settings.getEnvironmentLogger(); |
| if (accesslogger == null) { |
| accesslogger = "cocoon"; |
| } |
| |
| // create bootstrap logger |
| final BootstrapEnvironment.LogLevel level = BootstrapEnvironment.LogLevel.getLogLevelForName(logLevel); |
| final Logger bootstrapLogger = this.env.getBootstrapLogger(level); |
| |
| // Create our own resolver |
| final SourceResolver resolver = this.createSourceResolver(bootstrapLogger); |
| |
| // create an own service manager for the logger manager |
| final ServiceManager loggerManagerServiceManager = new SingleComponentServiceManager( |
| null, resolver, SourceResolver.ROLE); |
| |
| // create an own context for the logger manager |
| final DefaultContext subcontext = new SettingsContext(this.appContext, this.settings); |
| subcontext.put("context-work", new File(this.settings.getWorkDirectory())); |
| if (this.env.getContextForWriting() == null) { |
| File logSCDir = new File(this.settings.getWorkDirectory(), "log"); |
| logSCDir.mkdirs(); |
| subcontext.put("context-root", logSCDir.toString()); |
| } else { |
| subcontext.put("context-root", this.env.getContextForWriting().toString()); |
| } |
| this.env.configureLoggingContext(subcontext); |
| |
| String loggerManagerClass = settings.getLoggerManagerClassName(); |
| |
| // the log4j support requires currently that the log4j system is already |
| // configured elsewhere |
| |
| final LoggerManager loggerManager = this.newLoggerManager(loggerManagerClass); |
| ContainerUtil.enableLogging(loggerManager, bootstrapLogger); |
| |
| try { |
| ContainerUtil.contextualize(loggerManager, subcontext); |
| ContainerUtil.service(loggerManager, loggerManagerServiceManager); |
| |
| this.loggerManager = loggerManager; |
| |
| if (loggerManager instanceof Configurable) { |
| //Configure the logkit management |
| String logkitConfig = settings.getLoggingConfiguration(); |
| |
| if ( logkitConfig != null ) { |
| Source source = null; |
| try { |
| source = resolver.resolveURI(logkitConfig); |
| final ConfigurationBuilder builder = new ConfigurationBuilder( |
| settings); |
| final Configuration conf = builder.build(source.getInputStream()); |
| final DefaultConfiguration categories = (DefaultConfiguration) conf |
| .getChild("categories"); |
| final DefaultConfiguration targets = (DefaultConfiguration) conf |
| .getChild("targets"); |
| final DefaultConfiguration factories = (DefaultConfiguration) conf |
| .getChild("factories"); |
| |
| // now process includes |
| final Configuration[] children = conf |
| .getChildren("include"); |
| for (int i = 0; i < children.length; i++) { |
| String directoryURI = children[i].getAttribute("dir"); |
| final String pattern = children[i].getAttribute( |
| "pattern", null); |
| int[] parsedPattern = null; |
| if (pattern != null) { |
| parsedPattern = WildcardHelper |
| .compilePattern(pattern); |
| } |
| Source directory = null; |
| try { |
| directory = resolver.resolveURI(directoryURI, |
| source.getURI(), CONTEXT_PARAMETERS); |
| if (directory instanceof TraversableSource) { |
| final Iterator c = ((TraversableSource) directory) |
| .getChildren().iterator(); |
| while (c.hasNext()) { |
| final Source s = (Source) c.next(); |
| if (parsedPattern == null |
| || this.match(s.getURI(), |
| parsedPattern)) { |
| final Configuration includeConf = builder |
| .build(s.getInputStream()); |
| // add targets and categories |
| categories.addAllChildren(includeConf |
| .getChild("categories")); |
| targets.addAllChildren(includeConf |
| .getChild("targets")); |
| factories.addAllChildren(includeConf |
| .getChild("factories")); |
| } |
| } |
| } else { |
| throw new ConfigurationException( |
| "Include.dir must point to a directory, '" |
| + directory.getURI() |
| + "' is not a directory.'"); |
| } |
| } catch (IOException ioe) { |
| throw new ConfigurationException( |
| "Unable to read configurations from " |
| + directoryURI); |
| } finally { |
| resolver.release(directory); |
| } |
| |
| // finally remove include |
| ((DefaultConfiguration) conf).removeChild(children[i]); |
| } |
| // override log level? |
| if (settings.getOverrideLogLevel() != null) { |
| this.overrideLogLevel(conf.getChild("categories"), |
| settings.getOverrideLogLevel()); |
| } |
| ContainerUtil.configure(loggerManager, conf); |
| } finally { |
| resolver.release(source); |
| } |
| } |
| } |
| ContainerUtil.initialize(loggerManager); |
| } catch (Exception e) { |
| bootstrapLogger.error( |
| "Could not set up Cocoon Logger, will use screen instead", |
| e); |
| } |
| |
| this.log = this.loggerManager.getLoggerForCategory(accesslogger); |
| } |
| |
| /** |
| * Create a new logger manager. |
| * @param loggerManagerClass The class name or one of the allowed shortcuts. |
| * @return A new logger manager. |
| */ |
| private LoggerManager newLoggerManager(String loggerManagerClass) { |
| if ("LogKit".equalsIgnoreCase(loggerManagerClass) || loggerManagerClass == null) { |
| loggerManagerClass = CocoonLogKitLoggerManager.class.getName(); |
| } else if ("LOG4J".equalsIgnoreCase(loggerManagerClass)) { |
| loggerManagerClass = Log4JConfLoggerManager.class.getName(); |
| } |
| try { |
| Class clazz = Class.forName(loggerManagerClass); |
| if ( PerRequestLoggerManager.class.isAssignableFrom(clazz) ) { |
| this.isPerRequestLoggerManager = true; |
| } |
| return (LoggerManager) clazz.newInstance(); |
| } catch (Exception e) { |
| this.isPerRequestLoggerManager = true; |
| return new CocoonLogKitLoggerManager(); |
| } |
| } |
| |
| protected void overrideLogLevel(Configuration root, String value) { |
| Configuration[] c = root.getChildren("category"); |
| for(int i=0;i<c.length;i++) { |
| ((DefaultConfiguration)c[i]).setAttribute("log-level", value); |
| this.overrideLogLevel(c[i], value); |
| } |
| } |
| |
| private boolean match(String uri, int[] parsedPattern ) { |
| int pos = uri.lastIndexOf('/'); |
| if ( pos != -1 ) { |
| uri = uri.substring(pos+1); |
| } |
| return WildcardHelper.match(null, uri, parsedPattern); |
| } |
| |
| /** |
| * Create the classloader that inlcudes all the [block]/BLOCK-INF/classes directories. |
| * @throws Exception |
| */ |
| protected void createClassloader() throws Exception { |
| // get the wiring |
| final SourceResolver resolver = this.createSourceResolver(this.log); |
| Source wiringSource = null; |
| final Configuration wiring; |
| try { |
| wiringSource = resolver.resolveURI(Constants.WIRING); |
| DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); |
| wiring = builder.build( wiringSource.getInputStream() ); |
| } catch(org.apache.excalibur.source.SourceNotFoundException snfe) { |
| throw new WiringNotFoundException("wiring.xml not found in the root directory of your Cocoon application."); |
| } finally { |
| resolver.release(wiringSource); |
| } |
| |
| // get all wired blocks and add their classed directory to the classloader |
| List urlList = new ArrayList(); |
| Configuration[] blocks = wiring.getChildren("block"); |
| for(int i = 0; i < blocks.length; i++) { |
| String location = blocks[i].getAttribute("location"); |
| if(this.log.isDebugEnabled()) { |
| this.log.debug("Found block " + blocks[i].getAttribute("id") + " at " + location); |
| } |
| Source classesDir = null; |
| try { |
| classesDir = resolver.resolveURI(location + "/" + Constants.BLOCK_META_DIR + "/classes"); |
| if(classesDir.exists()) { |
| String classesDirURI = classesDir.getURI(); |
| urlList.add(new URL(classesDirURI)); |
| if(this.log.isDebugEnabled()) { |
| this.log.debug("added " + classesDir.getURI()); |
| } |
| } |
| } finally { |
| resolver.release(classesDir); |
| } |
| } |
| |
| // setup the classloader using the current classloader as parent |
| ClassLoader parentClassloader = Thread.currentThread().getContextClassLoader(); |
| URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]); |
| URLClassLoader classloader = new URLClassLoader(urls, parentClassloader); |
| Thread.currentThread().setContextClassLoader(classloader); |
| this.classloader = Thread.currentThread().getContextClassLoader(); |
| } |
| |
| /** |
| * Creates the Cocoon object and handles exception handling. |
| */ |
| public synchronized Cocoon createCocoon() |
| throws Exception { |
| this.createProcessor(); |
| return (Cocoon)this.processor; |
| } |
| |
| /** |
| * Gets the current cocoon object. |
| * Reload cocoon if configuration changed or we are reloading. |
| * Ensure that the correct classloader is set. |
| */ |
| public Cocoon getCocoon(final String pathInfo, final String reloadParam) |
| throws Exception { |
| this.getProcessor(pathInfo, reloadParam); |
| return (Cocoon)this.processor; |
| } |
| |
| /** |
| * Creates the root processor object and handles exception handling. |
| */ |
| public synchronized Processor createProcessor() |
| throws Exception { |
| |
| this.updateEnvironment(); |
| this.forceLoad(); |
| this.forceProperty(); |
| |
| try { |
| if (this.log.isInfoEnabled()) { |
| this.log.info("Reloading from: " + this.settings.getConfiguration()); |
| } |
| Processor p = (Processor)ClassUtils.newInstance(this.settings.getProcessorClassName()); |
| ContainerUtil.enableLogging(p, getCocoonLogger()); |
| if (p instanceof LoggerManageable) { |
| ((LoggerManageable)p).setLoggerManager(this.loggerManager); |
| } |
| ContainerUtil.contextualize(p, this.appContext); |
| |
| // create the Core object |
| final Core core = this.createCore(); |
| this.parentManager = this.getParentServiceManager(core); |
| ContainerUtil.service(p, this.parentManager); |
| |
| ContainerUtil.initialize(p); |
| this.settings.setCreationTime(System.currentTimeMillis()); |
| this.processor = p; |
| } catch (Exception e) { |
| this.log.error("Exception reloading root processor.", e); |
| this.disposeProcessor(); |
| throw e; |
| } |
| return this.processor; |
| } |
| |
| /** |
| * Gets the current root processor object. |
| * Reload the root processor if configuration changed or we are reloading. |
| * Ensure that the correct classloader is set. |
| */ |
| public Processor getProcessor(final String pathInfo, final String reloadParam) |
| throws Exception { |
| |
| // set the blocks classloader for this thread |
| Thread.currentThread().setContextClassLoader(this.classloader); |
| |
| if (this.settings.isReloadingEnabled("config")) { |
| boolean reload = false; |
| |
| if (this.processor != null) { |
| if (this.processor instanceof Modifiable && ((Modifiable)this.processor).modifiedSince(this.settings.getCreationTime())) { |
| if (this.log.isInfoEnabled()) { |
| this.log.info("Configuration changed reload attempt"); |
| } |
| reload = true; |
| } else if (pathInfo == null && reloadParam != null) { |
| if (this.log.isInfoEnabled()) { |
| this.log.info("Forced reload attempt"); |
| } |
| reload = true; |
| } |
| } else if (pathInfo == null && reloadParam != null) { |
| if (this.log.isInfoEnabled()) { |
| this.log.info("Invalid configurations reload"); |
| } |
| reload = true; |
| } |
| |
| if (reload) { |
| this.init(); |
| this.createProcessor(); |
| } |
| } |
| return this.processor; |
| } |
| |
| /** |
| * Destroy root processor |
| */ |
| protected final void disposeProcessor() { |
| if (this.processor != null) { |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Disposing root processor"); |
| } |
| ContainerUtil.dispose(this.processor); |
| this.processor = null; |
| } |
| ContainerUtil.dispose(this.parentManager); |
| this.parentManager = null; |
| } |
| |
| protected Logger getCocoonLogger() { |
| final String rootlogger = this.settings.getCocoonLogger(); |
| if (rootlogger != null) { |
| return this.loggerManager.getLoggerForCategory(rootlogger); |
| } |
| return this.log; |
| } |
| |
| /** |
| * Handle the <code>load-class</code> parameter. This overcomes |
| * limits in many classpath issues. One of the more notorious |
| * ones is a bug in WebSphere that does not load the URL handler |
| * for the <code>classloader://</code> protocol. In order to |
| * overcome that bug, set <code>load-class</code> parameter to |
| * the <code>com.ibm.servlet.classloader.Handler</code> value. |
| * |
| * <p>If you need to load more than one class, then separate each |
| * entry with whitespace, a comma, or a semi-colon. Cocoon will |
| * strip any whitespace from the entry.</p> |
| */ |
| protected void forceLoad() { |
| final Iterator i = this.settings.getLoadClasses().iterator(); |
| while (i.hasNext()) { |
| final String fqcn = (String)i.next(); |
| try { |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Loading: " + fqcn); |
| } |
| ClassUtils.loadClass(fqcn).newInstance(); |
| } catch (Exception e) { |
| if (this.log.isWarnEnabled()) { |
| this.log.warn("Could not load class: " + fqcn, e); |
| } |
| // Do not throw an exception, because it is not a fatal error. |
| } |
| } |
| } |
| |
| /** |
| * Handle the "force-property" parameter. |
| * |
| * If you need to force more than one property to load, then |
| * separate each entry with whitespace, a comma, or a semi-colon. |
| * Cocoon will strip any whitespace from the entry. |
| */ |
| protected void forceProperty() { |
| if (this.settings.getForceProperties().size() > 0) { |
| final Iterator i = this.settings.getForceProperties().entrySet().iterator(); |
| while (i.hasNext()) { |
| final Map.Entry current = (Map.Entry)i.next(); |
| try { |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("Setting: " + current.getKey() + "=" + current.getValue()); |
| } |
| System.setProperty(current.getKey().toString(), current.getValue().toString()); |
| } catch (Exception e) { |
| if (this.log.isWarnEnabled()) { |
| this.log.warn("Could not set property: " + current.getKey(), e); |
| } |
| // Do not throw an exception, because it is not a fatal error. |
| } |
| } |
| } |
| } |
| |
| /** |
| * Method to update the environment before Cocoon instances are created. |
| * |
| * This is also useful if you wish to customize any of the 'protected' |
| * variables from this class before a Cocoon instance is built in a derivative |
| * of this class (eg. Cocoon Context). |
| */ |
| protected void updateEnvironment() throws Exception { |
| // // concatenate the class path and the extra class path |
| // String classPath = this.env.getClassPath(this.settings); |
| // StringBuffer buffer = new StringBuffer(); |
| // if ( classPath != null && classPath.length() > 0 ) { |
| // buffer.append(classPath); |
| // } |
| // classPath = this.getExtraClassPath(); |
| // if ( classPath != null && classPath.length() > 0 ) { |
| // if ( buffer.length() > 0 ) { |
| // buffer.append(File.pathSeparatorChar); |
| // } |
| // buffer.append(classPath); |
| // } |
| // FIXME - for now we just set an empty string as this information is looked up |
| // by other components |
| this.appContext.put(Constants.CONTEXT_CLASSPATH, ""); |
| } |
| |
| /** |
| * Dispose the root processor when environment is destroyed |
| */ |
| public void destroy() { |
| this.disposeProcessor(); |
| } |
| |
| /** |
| * Retreives the "extra-classpath" attribute, that needs to be |
| * added to the class path. |
| */ |
| protected String getExtraClassPath() { |
| if (this.settings.getExtraClasspaths().size() > 0) { |
| StringBuffer sb = new StringBuffer(); |
| final Iterator iter = this.settings.getExtraClasspaths().iterator(); |
| int i = 0; |
| while (iter.hasNext()) { |
| String s = (String)iter.next(); |
| if (i++ > 0) { |
| sb.append(File.pathSeparatorChar); |
| } |
| if ((s.charAt(0) == File.separatorChar) || |
| (s.charAt(1) == ':')) { |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("extraClassPath is absolute: " + s); |
| } |
| sb.append(s); |
| |
| } else { |
| if (s.indexOf("${") != -1) { |
| String path = StringUtils.replaceToken(s); |
| sb.append(path); |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("extraClassPath is not absolute replacing using token: [" + s + "] : " + path); |
| } |
| } else { |
| String path = null; |
| if (this.env.getContextForWriting() != null) { |
| path = this.env.getContextForWriting() + s; |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("extraClassPath is not absolute pre-pending context path: " + path); |
| } |
| } else { |
| path = this.settings.getWorkDirectory() + s; |
| if (this.log.isDebugEnabled()) { |
| this.log.debug("extraClassPath is not absolute pre-pending work-directory: " + path); |
| } |
| } |
| sb.append(path); |
| } |
| } |
| } |
| return sb.toString(); |
| } |
| return ""; |
| } |
| |
| protected static final class LoggerWrapper implements Logger { |
| private final BootstrapEnvironment env; |
| |
| public LoggerWrapper(BootstrapEnvironment env) { |
| this.env = env; |
| } |
| |
| protected void text(String arg0, Throwable arg1) { |
| if ( arg1 != null ) { |
| this.env.log(arg0, arg1); |
| } else { |
| this.env.log(arg0); |
| } |
| } |
| |
| public void debug(String arg0, Throwable arg1) { |
| // we ignore debug |
| } |
| |
| public void debug(String arg0) { |
| // we ignore debug |
| } |
| |
| public void error(String arg0, Throwable arg1) { |
| this.text(arg0, arg1); |
| } |
| |
| public void error(String arg0) { |
| this.text(arg0, null); |
| } |
| |
| public void fatalError(String arg0, Throwable arg1) { |
| this.text(arg0, arg1); |
| } |
| |
| public void fatalError(String arg0) { |
| this.text(arg0, null); |
| } |
| |
| public Logger getChildLogger(String arg0) { |
| return this; |
| } |
| |
| public void info(String arg0, Throwable arg1) { |
| // we ignore info |
| } |
| |
| public void info(String arg0) { |
| // we ignore info |
| } |
| |
| public boolean isDebugEnabled() { |
| return false; |
| } |
| |
| public boolean isErrorEnabled() { |
| return true; |
| } |
| |
| public boolean isFatalErrorEnabled() { |
| return true; |
| } |
| |
| public boolean isInfoEnabled() { |
| return false; |
| } |
| |
| public boolean isWarnEnabled() { |
| return false; |
| } |
| |
| public void warn(String arg0, Throwable arg1) { |
| // we ignore warn |
| } |
| |
| public void warn(String arg0) { |
| // we ignore warn |
| } |
| } |
| } |