blob: 49092a4fb27775a3409464b709bad3e12763de26 [file] [log] [blame]
/*
* Copyright 1999-2004 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.serialization;
import java.io.File;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.Map;
import java.util.HashMap;
import org.apache.avalon.framework.CascadingRuntimeException;
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.logger.Logger;
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.caching.CacheableProcessingComponent;
import org.apache.cocoon.components.renderer.ExtendableRendererFactory;
import org.apache.cocoon.components.renderer.RendererFactory;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.util.ClassUtils;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceValidity;
import org.apache.excalibur.source.impl.validity.NOPValidity;
import org.apache.fop.apps.Driver;
import org.apache.fop.apps.Options;
import org.apache.fop.configuration.ConfigurationParser;
import org.apache.fop.messaging.MessageHandler;
import org.apache.fop.render.Renderer;
/**
* @author ?
* @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
* @version CVS $Id: FOPSerializer.java,v 1.10 2004/03/05 13:01:56 bdelacretaz Exp $
*/
public class FOPSerializer extends AbstractSerializer implements
Configurable, CacheableProcessingComponent, Serviceable/*, Disposable */{
//protected SourceResolver resolver;
/**
* The Renderer Factory to use
*/
protected static RendererFactory factory = ExtendableRendererFactory.getRendererFactoryImplementation();
/**
* The <code>Driver</code> which is FOP.
*/
protected Driver driver;
/**
* The current <code>Renderer</code>.
*/
protected Renderer renderer;
/**
* The current <code>mime-type</code>.
*/
protected String mimetype;
/**
* The renderer name if configured
*/
protected String rendererName;
/**
* Should we set the content length ?
*/
protected boolean setContentLength = true;
/**
* This logger is used for FOP
*/
protected Logger logger;
/**
* It is used to make sure that default Options loaded only once.
*/
private static boolean configured = false;
/**
* Manager to get URLFactory from.
*/
protected ServiceManager manager;
/**
* Set the component manager for this serializer.
*/
public void service(ServiceManager manager) throws ServiceException {
this.manager = manager;
//this.resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);
}
/*
public void dispose() {
this.manager.release(this.resolver);
}
*/
/**
* Set the configurations for this serializer.
*/
public void configure(Configuration conf) throws ConfigurationException {
this.logger = getLogger().getChildLogger("fop");
MessageHandler.setScreenLogger(this.logger);
// FIXME: VG: Initialize static FOP configuration with defaults, only once.
// FOP has static config, but that's going to change in the near future.
// Then this code should be reviewed.
synchronized (FOPSerializer.class) {
if (!configured) {
try {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Loading default configuration");
}
new Options();
} catch (Exception e) {
getLogger().error("Cannot load default configuration. Proceeding.", e);
}
configured = true;
}
}
this.setContentLength = conf.getChild("set-content-length").getValueAsBoolean(true);
// Old syntax: Attribute src of element user-config contains file
String configUrl = conf.getChild("user-config").getAttribute("src", null);
if (configUrl != null) {
getLogger().warn("Attribute src of user-config element is deprecated. "
+ "Provide Cocoon URI as value of the element instead");
try {
// VG: Old version of serializer supported only files
configUrl = new File(configUrl).toURL().toExternalForm();
} catch (MalformedURLException e) {
getLogger().warn("Can not load config file " + configUrl, e);
configUrl = null;
}
} else {
// New syntax: Element user-config contains URL
configUrl = conf.getChild("user-config").getValue(null);
}
if (configUrl != null) {
Source configSource = null;
SourceResolver resolver = null;
try {
resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);
configSource = resolver.resolveURI(configUrl);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Loading configuration from " + configSource.getURI());
}
SourceUtil.toSAX(configSource, new ConfigurationParser());
} catch (Exception e) {
getLogger().warn("Cannot load configuration from " + configUrl);
throw new ConfigurationException("Cannot load configuration from " + configUrl, e);
} finally {
if (resolver != null) {
resolver.release(configSource);
manager.release(resolver);
}
}
}
// Get the mime type.
this.mimetype = conf.getAttribute("mime-type");
// Iterate through the parameters, looking for a renderer reference
Configuration[] parameters = conf.getChildren("parameter");
for (int i = 0; i < parameters.length; i++) {
String name = parameters[i].getAttribute("name");
if ("renderer".equals(name)) {
this.rendererName = parameters[i].getAttribute("value");
try {
this.renderer = (Renderer)ClassUtils.newInstance(rendererName);
} catch (Exception ex) {
getLogger().error("Cannot load class " + rendererName, ex);
throw new ConfigurationException("Cannot load class " + rendererName, ex);
}
}
}
if (this.renderer == null) {
// Using the Renderer Factory, get the default renderer
// for this MIME type.
this.renderer = factory.createRenderer(mimetype);
}
// Do we have a renderer yet?
if (this.renderer == null ) {
throw new ConfigurationException(
"Could not autodetect renderer for FOPSerializer and "
+ "no renderer was specified in the sitemap configuration."
);
}
Configuration confRenderer = conf.getChild("renderer-config");
if (confRenderer != null) {
parameters = confRenderer.getChildren("parameter");
if (parameters.length > 0) {
Map rendererOptions = new HashMap();
for (int i = 0; i < parameters.length; i++) {
String name = parameters[i].getAttribute("name");
String value = parameters[i].getAttribute("value");
if (getLogger().isDebugEnabled()) {
getLogger().debug("renderer " + String.valueOf(name) + " = " + String.valueOf(value));
}
rendererOptions.put(name,value);
}
this.renderer.setOptions(rendererOptions);
}
}
}
/**
* Return the MIME type.
*/
public String getMimeType() {
return mimetype;
}
/**
* Create the FOP driver
* Set the <code>OutputStream</code> where the XML should be serialized.
*/
public void setOutputStream(OutputStream out) {
// Give the source resolver to Batik which is used by FOP
//SourceProtocolHandler.setup(this.resolver);
// load the fop driver
this.driver = new Driver();
this.driver.setLogger(this.logger);
if (this.rendererName == null) {
this.renderer = factory.createRenderer(mimetype);
} else {
try {
this.renderer = (Renderer)ClassUtils.newInstance(this.rendererName);
} catch (Exception e) {
if (getLogger().isWarnEnabled()) {
getLogger().warn("Cannot load class " + this.rendererName, e);
}
throw new CascadingRuntimeException("Cannot load class " + this.rendererName, e);
}
}
this.driver.setRenderer(this.renderer);
this.driver.setOutputStream(out);
setContentHandler(this.driver.getContentHandler());
}
/**
* Generate the unique key.
* This key must be unique inside the space of this component.
* This method must be invoked before the generateValidity() method.
*
* @return The generated key or <code>0</code> if the component
* is currently not cacheable.
*/
public Serializable getKey() {
return "1";
}
/**
* Generate the validity object.
* Before this method can be invoked the generateKey() method
* must be invoked.
*
* @return The generated validity object or <code>null</code> if the
* component is currently not cacheable.
*/
public SourceValidity getValidity() {
return NOPValidity.SHARED_INSTANCE;
}
/**
* Recycle serializer by removing references
*/
public void recycle() {
super.recycle();
this.driver = null;
this.renderer = null;
}
/**
* Test if the component wants to set the content length
*/
public boolean shouldSetContentLength() {
return this.setContentLength;
}
}