001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.serialization;
018:
019: import java.io.File;
020: import java.io.OutputStream;
021: import java.io.Serializable;
022: import java.net.MalformedURLException;
023: import java.util.Map;
024: import java.util.HashMap;
025:
026: import org.apache.avalon.framework.CascadingRuntimeException;
027: import org.apache.avalon.framework.configuration.Configurable;
028: import org.apache.avalon.framework.configuration.Configuration;
029: import org.apache.avalon.framework.configuration.ConfigurationException;
030: import org.apache.avalon.framework.logger.Logger;
031: import org.apache.avalon.framework.service.ServiceException;
032: import org.apache.avalon.framework.service.ServiceManager;
033: import org.apache.avalon.framework.service.Serviceable;
034: import org.apache.cocoon.caching.CacheableProcessingComponent;
035: import org.apache.cocoon.components.renderer.ExtendableRendererFactory;
036: import org.apache.cocoon.components.renderer.RendererFactory;
037: import org.apache.cocoon.components.source.SourceUtil;
038: import org.apache.cocoon.util.ClassUtils;
039: import org.apache.excalibur.source.Source;
040: import org.apache.excalibur.source.SourceResolver;
041: import org.apache.excalibur.source.SourceValidity;
042: import org.apache.excalibur.source.impl.validity.NOPValidity;
043: import org.apache.fop.apps.Driver;
044: import org.apache.fop.apps.Options;
045: import org.apache.fop.configuration.ConfigurationParser;
046: import org.apache.fop.messaging.MessageHandler;
047: import org.apache.fop.render.Renderer;
048:
049: /**
050: * @author ?
051: * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
052: * @version CVS $Id: FOPSerializer.java 433543 2006-08-22 06:22:54Z crossley $
053: */
054: public class FOPSerializer extends AbstractSerializer implements
055: Configurable, CacheableProcessingComponent, Serviceable/*, Disposable */{
056:
057: //protected SourceResolver resolver;
058:
059: /**
060: * The Renderer Factory to use
061: */
062: protected final static RendererFactory factory = ExtendableRendererFactory
063: .getRendererFactoryImplementation();
064:
065: /**
066: * The <code>Driver</code> which is FOP.
067: */
068: protected Driver driver;
069:
070: /**
071: * The current <code>Renderer</code>.
072: */
073: protected Renderer renderer;
074:
075: /**
076: * The current <code>mime-type</code>.
077: */
078: protected String mimetype;
079:
080: /**
081: * The renderer name if configured
082: */
083: protected String rendererName;
084:
085: /**
086: * Should we set the content length ?
087: */
088: protected boolean setContentLength = true;
089:
090: /**
091: * This logger is used for FOP
092: */
093: protected Logger logger;
094:
095: /**
096: * It is used to make sure that default Options loaded only once.
097: */
098: private static boolean configured = false;
099:
100: /**
101: * Manager to get URLFactory from.
102: */
103: protected ServiceManager manager;
104:
105: /**
106: * Set the component manager for this serializer.
107: */
108: public void service(ServiceManager manager) throws ServiceException {
109: this .manager = manager;
110: //this.resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);
111: }
112:
113: /*
114: public void dispose() {
115: this.manager.release(this.resolver);
116: }
117: */
118: /**
119: * Set the configurations for this serializer.
120: */
121: public void configure(Configuration conf)
122: throws ConfigurationException {
123:
124: this .logger = getLogger().getChildLogger("fop");
125: MessageHandler.setScreenLogger(this .logger);
126:
127: // FIXME: VG: Initialize static FOP configuration with defaults, only once.
128: // FOP has static config, but that's going to change in the near future.
129: // Then this code should be reviewed.
130: synchronized (FOPSerializer.class) {
131: if (!configured) {
132: try {
133: if (getLogger().isDebugEnabled()) {
134: getLogger().debug(
135: "Loading default configuration");
136: }
137: new Options();
138: } catch (Exception e) {
139: getLogger()
140: .error(
141: "Cannot load default configuration. Proceeding.",
142: e);
143: }
144: configured = true;
145: }
146: }
147:
148: this .setContentLength = conf.getChild("set-content-length")
149: .getValueAsBoolean(true);
150:
151: // Old syntax: Attribute src of element user-config contains file
152: String configUrl = conf.getChild("user-config").getAttribute(
153: "src", null);
154: if (configUrl != null) {
155: getLogger()
156: .warn(
157: "Attribute src of user-config element is deprecated. "
158: + "Provide Cocoon URI as value of the element instead");
159: try {
160: // VG: Old version of serializer supported only files
161: configUrl = new File(configUrl).toURL()
162: .toExternalForm();
163: } catch (MalformedURLException e) {
164: getLogger().warn(
165: "Can not load config file " + configUrl, e);
166: configUrl = null;
167: }
168: } else {
169: // New syntax: Element user-config contains URL
170: configUrl = conf.getChild("user-config").getValue(null);
171: }
172:
173: if (configUrl != null) {
174: Source configSource = null;
175: SourceResolver resolver = null;
176: try {
177: resolver = (SourceResolver) this .manager
178: .lookup(SourceResolver.ROLE);
179: configSource = resolver.resolveURI(configUrl);
180: if (getLogger().isDebugEnabled()) {
181: getLogger().debug(
182: "Loading configuration from "
183: + configSource.getURI());
184: }
185: SourceUtil.toSAX(configSource,
186: new ConfigurationParser());
187: } catch (Exception e) {
188: getLogger().warn(
189: "Cannot load configuration from " + configUrl);
190: throw new ConfigurationException(
191: "Cannot load configuration from " + configUrl,
192: e);
193: } finally {
194: if (resolver != null) {
195: resolver.release(configSource);
196: manager.release(resolver);
197: }
198: }
199: }
200:
201: // Get the mime type.
202: this .mimetype = conf.getAttribute("mime-type");
203:
204: // Iterate through the parameters, looking for a renderer reference
205: Configuration[] parameters = conf.getChildren("parameter");
206: for (int i = 0; i < parameters.length; i++) {
207: String name = parameters[i].getAttribute("name");
208: if ("renderer".equals(name)) {
209: this .rendererName = parameters[i].getAttribute("value");
210: try {
211: this .renderer = (Renderer) ClassUtils
212: .newInstance(rendererName);
213: } catch (Exception ex) {
214: getLogger().error(
215: "Cannot load class " + rendererName, ex);
216: throw new ConfigurationException(
217: "Cannot load class " + rendererName, ex);
218: }
219: }
220: }
221: if (this .renderer == null) {
222: // Using the Renderer Factory, get the default renderer
223: // for this MIME type.
224: this .renderer = factory.createRenderer(mimetype);
225: }
226:
227: // Do we have a renderer yet?
228: if (this .renderer == null) {
229: throw new ConfigurationException(
230: "Could not autodetect renderer for FOPSerializer and "
231: + "no renderer was specified in the sitemap configuration.");
232: }
233:
234: Configuration confRenderer = conf.getChild("renderer-config");
235: if (confRenderer != null) {
236: parameters = confRenderer.getChildren("parameter");
237: if (parameters.length > 0) {
238: Map rendererOptions = new HashMap();
239: for (int i = 0; i < parameters.length; i++) {
240: String name = parameters[i].getAttribute("name");
241: String value = parameters[i].getAttribute("value");
242:
243: if (getLogger().isDebugEnabled()) {
244: getLogger()
245: .debug(
246: "renderer "
247: + String.valueOf(name)
248: + " = "
249: + String.valueOf(value));
250: }
251: rendererOptions.put(name, value);
252: }
253: this .renderer.setOptions(rendererOptions);
254: }
255: }
256: }
257:
258: /**
259: * Return the MIME type.
260: */
261: public String getMimeType() {
262: return mimetype;
263: }
264:
265: /**
266: * Create the FOP driver
267: * Set the <code>OutputStream</code> where the XML should be serialized.
268: */
269: public void setOutputStream(OutputStream out) {
270:
271: // Give the source resolver to Batik which is used by FOP
272: //SourceProtocolHandler.setup(this.resolver);
273:
274: // load the fop driver
275: this .driver = new Driver();
276: this .driver.setLogger(this .logger);
277: if (this .rendererName == null) {
278: this .renderer = factory.createRenderer(mimetype);
279: } else {
280: try {
281: this .renderer = (Renderer) ClassUtils
282: .newInstance(this .rendererName);
283: } catch (Exception e) {
284: if (getLogger().isWarnEnabled()) {
285: getLogger()
286: .warn(
287: "Cannot load class "
288: + this .rendererName, e);
289: }
290: throw new CascadingRuntimeException(
291: "Cannot load class " + this .rendererName, e);
292: }
293: }
294: this .driver.setRenderer(this .renderer);
295: this .driver.setOutputStream(out);
296: setContentHandler(this .driver.getContentHandler());
297: }
298:
299: /**
300: * Generate the unique key.
301: * This key must be unique inside the space of this component.
302: * This method must be invoked before the generateValidity() method.
303: *
304: * @return The generated key or <code>0</code> if the component
305: * is currently not cacheable.
306: */
307: public Serializable getKey() {
308: return "1";
309: }
310:
311: /**
312: * Generate the validity object.
313: * Before this method can be invoked the generateKey() method
314: * must be invoked.
315: *
316: * @return The generated validity object or <code>null</code> if the
317: * component is currently not cacheable.
318: */
319: public SourceValidity getValidity() {
320: return NOPValidity.SHARED_INSTANCE;
321: }
322:
323: /**
324: * Recycle serializer by removing references
325: */
326: public void recycle() {
327: super .recycle();
328: this .driver = null;
329: this .renderer = null;
330: }
331:
332: /**
333: * Test if the component wants to set the content length
334: */
335: public boolean shouldSetContentLength() {
336: return this.setContentLength;
337: }
338:
339: }
|