blob: b45f70e04cf0c5136e3fada2a425e00d23907696 [file] [log] [blame]
/*
* Copyright 1999-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.blocks;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
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.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.Constants;
import org.apache.cocoon.Processor;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.components.LifecycleHelper;
import org.apache.cocoon.components.container.CocoonServiceManager;
import org.apache.cocoon.components.container.ComponentContext;
import org.apache.cocoon.core.container.CoreServiceManager;
import org.apache.cocoon.environment.Environment;
import org.apache.cocoon.environment.SourceResolver;
/**
* @version $Id$
*/
public class BlockManager
extends AbstractLogEnabled
implements Block, Configurable, Contextualizable, Disposable, Initializable, Serviceable {
public static String ROLE = BlockManager.class.getName();
private Context context;
private Configuration config;
private ServiceManager parentServiceManager;
private ServiceManager serviceManager;
private Processor blockProcessor;
private BlockWiring blockWiring;
private BlockContext blockContext;
private Blocks blocks;
// Life cycle
public void service(ServiceManager manager) throws ServiceException {
this.parentServiceManager = manager;
}
public void contextualize(Context context) throws ContextException {
this.context = context;
}
public void configure(Configuration config)
throws ConfigurationException {
this.config = config;
}
public void initialize() throws Exception {
this.blockWiring = new BlockWiring();
LifecycleHelper.setupComponent(this.blockWiring,
this.getLogger(),
this.context,
null,
this.config);
getLogger().debug("Initializing new Block Manager: " + this.blockWiring.getId());
this.blockContext = new BlockContext(this.blockWiring, this);
Context newContext = this.getAvalonContext();
String confLocation = this.blockWiring.getContextURL() + "::";
if (this.blockWiring.isCore()) {
this.getLogger().debug("Block with core=true");
this.serviceManager = this.parentServiceManager;
} else {
// Create a service manager for getting components from other blocks
ServiceManager topServiceManager = new InterBlockServiceManager(this.blockWiring, this.blocks);
((InterBlockServiceManager)topServiceManager).enableLogging(this.getLogger());
this.serviceManager =
this.createLocalSourceResolverSM(newContext, topServiceManager, confLocation);
}
// Create a service manager with the exposed components of the block
if (this.blockWiring.getComponentConfiguration() != null) {
DefaultConfiguration componentConf =
new DefaultConfiguration("components", confLocation);
componentConf.addAll(this.blockWiring.getComponentConfiguration());
this.serviceManager = new CocoonServiceManager(this.serviceManager);
LifecycleHelper.setupComponent(this.serviceManager,
this.getLogger(),
newContext,
null,
componentConf);
}
// Create a processor for the block
if (this.blockWiring.getProcessorConfiguration() != null) {
this.blockProcessor = new BlockProcessor();
LifecycleHelper.setupComponent(this.blockProcessor,
this.getLogger(),
newContext,
this.serviceManager,
this.blockWiring.getProcessorConfiguration());
}
}
public void dispose() {
this.parentServiceManager = null;
}
/**
* @throws Exception
*/
protected Context getAvalonContext() throws Exception {
ComponentContext newContext = new ComponentContext(this.context);
// A block is supposed to be an isolated unit so it should not have
// any direct access to the global root context
newContext.put(ContextHelper.CONTEXT_ROOT_URL, new URL(this.blockWiring.getContextURL().toExternalForm()));
newContext.put(Constants.CONTEXT_ENVIRONMENT_CONTEXT, this.blockContext);
newContext.makeReadOnly();
return newContext;
}
/**
* @param newContext
* @param confLocation
* @throws Exception
*/
protected ServiceManager createLocalSourceResolverSM(Context newContext, ServiceManager parentServiceManager, String confLocation) throws Exception {
// The source resolver must be defined in this service
// manager, otherwise the root path will be the one from the
// parent manager, we add a resolver to get it right. If the
// components section contain includes the CoreComponentManager
// use the location of the configuration an the parent SourceResolver
// for resolving the include.
DefaultConfiguration sourceManagerConf =
new DefaultConfiguration("components", confLocation);
// FIXME: Need a local role manager as it is not inherited through the InterBlockServiceManager
DefaultConfiguration roleInclude =
new DefaultConfiguration("include");
roleInclude.setAttribute("src", "resource://org/apache/cocoon/cocoon.roles");
sourceManagerConf.addChild(roleInclude);
DefaultConfiguration resolverConf =
new DefaultConfiguration("source-resolver");
sourceManagerConf.addChild(resolverConf);
ServiceManager sourceResolverSM =
new CoreServiceManager(parentServiceManager);
LifecycleHelper.setupComponent(
sourceResolverSM,
this.getLogger(),
newContext,
null,
sourceManagerConf);
return sourceResolverSM;
}
// Block methods
// The blocks manager should not be available within a block so I
// didn't want to make it part of the parent manager. But this is
// a little bit clumsy. Question is what components, if any, the
// blocks should have in common.
public void setBlocks(Blocks blocks) {
this.blocks = blocks;
}
/**
* Get the mount path of the block
*/
public String getMountPath() {
return this.blockWiring.getMountPath();
}
/**
* Get a block property
*/
public String getProperty(String name) {
String value = this.blockWiring.getProperty(name);
if (value == null) {
// Ask the super block for the property
String superId = this.blockWiring.getBlockId(Block.SUPER);
this.getLogger().debug("Try super property=" + name + " block=" + superId);
Block block = this.blocks.getBlock(superId);
if (block != null) {
value = block.getProperty(name);
}
}
return value;
}
// TODO: We should have a reflection friendly Map getProperties() also
/**
* The exported components of the block. Return null if the block doesn't export components.
*
* @return a ServiceManager containing the blocks exported components
*/
public ServiceManager getServiceManager() {
// Check that the block have a local service manager
if (this.blockWiring.getComponentConfiguration() != null) {
return this.serviceManager;
} else {
return null;
}
}
/**
* Takes the scheme specific part of a block URI (the scheme is
* the responsibilty of the BlockSource) and resolve it with
* respect to the blocks mount point.
*/
public URI absolutizeURI(URI uriToResolve, URI base) throws URISyntaxException {
URI uri = resolveURI(uriToResolve, base);
String blockName = uri.getScheme();
Block block = null;
if (blockName == null) {
// this block
block = this;
} else {
// another block
String blockId = this.blockWiring.getBlockId(blockName);
block = this.blocks.getBlock(blockId);
}
if (block == null)
throw new URISyntaxException(uriToResolve.toString(), "Unknown block name");
String mountPath = block.getMountPath();
if (mountPath == null)
throw new URISyntaxException(uri.toString(), "No mount point for this URI");
if (mountPath.endsWith("/"))
mountPath = mountPath.substring(0, mountPath.length() - 1);
String absoluteURI = mountPath + uri.getSchemeSpecificPart();
getLogger().debug("Resolving " + uri.toString() + " to " + absoluteURI);
return new URI(absoluteURI);
}
/**
* Parses and resolves the scheme specific part of a block URI
* with respect to the base URI of the current sitemap. The scheme
* specific part of the block URI has the form
* <code>foo:/bar</code> when refering to another block, in this
* case only an absolute path is allowed. For reference to the own
* block, both absolute <code>/bar</code> and relative
* <code>./foo</code> paths are allowed.
*/
public URI resolveURI(URI uri, URI base) throws URISyntaxException {
getLogger().debug("BlockManager: resolving " + uri.toString() + " with scheme " +
uri.getScheme() + " and ssp " + uri.getSchemeSpecificPart());
if (uri.getPath() != null && uri.getPath().length() >= 2 &&
uri.getPath().startsWith("./")) {
// self reference relative to the current sitemap, e.g. ./foo
if (uri.isAbsolute())
throw new URISyntaxException(uri.toString(), "When the protocol refers to other blocks the path must be absolute");
URI resolvedURI = base.resolve(uri);
getLogger().debug("BlockManager: resolving " + uri.toString() +
" to " + resolvedURI.toString() + " with base URI " + base.toString());
uri = resolvedURI;
}
return uri;
}
// The Processor methods
public boolean process(Environment environment) throws Exception {
String blockName = (String)environment.getAttribute(Block.NAME);
if (blockName != null) {
// Request to other block.
String blockId = this.blockWiring.getBlockId(blockName);
boolean superCall = false;
// Call to named block
if (blockId != null && !Block.SUPER.equals(blockName)) {
// The block name should not be used in the recieving block.
environment.removeAttribute(Block.NAME);
} else {
if (Block.SUPER.equals(blockName)) {
// Explicit call to super block
// The block name should not be used in the recieving block.
environment.removeAttribute(Block.NAME);
} else if (blockId == null) {
// If there is a super block, the connection might
// be defined there instead.
blockId = this.blockWiring.getBlockId(Block.SUPER);
}
superCall = true;
}
Block block = this.blocks.getBlock(blockId);
if (block == null) {
return false;
}
this.getLogger().debug("Enter processing in block " + blockName);
try {
// A super block should be called in the context of
// the called block to get polymorphic calls resolved
// in the right way. Therefore no new current block is
// set.
if (!superCall) {
// It is important to set the current block each time
// a new block is entered, this is used for the block
// protocol
BlockEnvironmentHelper.enterBlock(block);
}
return block.process(environment);
} finally {
if (!superCall) {
BlockEnvironmentHelper.leaveBlock();
}
this.getLogger().debug("Leaving processing in block " + blockName);
}
} else {
// Request to the own block
boolean result = this.blockProcessor.process(environment);
return result;
// Pipelines seem to throw an exception instead of
// returning false when the pattern is not found. For the
// moment an explicit call of the super block is called in
// the end of the sitemap. It might be better to be
// explicit about it anyway.
// if (result) {
// return true;
// } else if (this.superId != null) {
// // Wasn't defined in the current block try super block
// return this.process(this.superId, environment, true);
// } else {
// return false;
// }
}
}
// FIXME: Not consistently supported for blocks yet. Most of the
// code just use process.
public InternalPipelineDescription buildPipeline(Environment environment)
throws Exception {
return this.blockProcessor.buildPipeline(environment);
}
public Configuration[] getComponentConfigurations() {
return this.blockProcessor.getComponentConfigurations();
}
// A block is supposed to be an isolated unit so it should not have
// any direct access to the global root sitemap
public Processor getRootProcessor() {
return this.blockProcessor;
}
public SourceResolver getSourceResolver() {
return this.blockProcessor.getSourceResolver();
}
public String getContext() {
return this.blockProcessor.getContext();
}
/**
* @see org.apache.cocoon.Processor#getAttribute(java.lang.String)
*/
public Object getAttribute(String name) {
return this.blockProcessor.getAttribute(name);
}
/**
* @see org.apache.cocoon.Processor#removeAttribute(java.lang.String)
*/
public Object removeAttribute(String name) {
return this.blockProcessor.removeAttribute(name);
}
/**
* @see org.apache.cocoon.Processor#setAttribute(java.lang.String, java.lang.Object)
*/
public void setAttribute(String name, Object value) {
this.blockProcessor.setAttribute(name, value);
}
}