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.components.url;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.util.Iterator;
022:
023: import org.apache.avalon.framework.CascadingRuntimeException;
024: import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
025: import org.apache.batik.util.AbstractParsedURLProtocolHandler;
026: import org.apache.batik.util.ParsedURL;
027: import org.apache.batik.util.ParsedURLData;
028: import org.apache.batik.util.ParsedURLProtocolHandler;
029: import org.apache.cocoon.CascadingIOException;
030: import org.apache.excalibur.source.Source;
031: import org.apache.excalibur.source.SourceResolver;
032:
033: /**
034: * A Batik protocol handler that handles all Cocoon sources. This allows
035: * <svg:image xlink:href="..."/> to use any of the protocols handled by Cocoon.
036: *
037: * @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
038: * @version CVS $Id: SourceProtocolHandler.java 433543 2006-08-22 06:22:54Z crossley $
039: */
040: public class SourceProtocolHandler extends
041: AbstractParsedURLProtocolHandler {
042:
043: /** Thread-local source resolver */
044: protected final static InheritableThreadLocal localResolver = new InheritableThreadLocal();
045:
046: /** Batik's original default handler */
047: protected final static ParsedURLProtocolHandler defaultHandler;
048:
049: /**
050: * Change the default handler used by Batik to resolve URLs to a handler
051: * based on <code>SourceResolver</code> and <code>SourceHandler</code>.
052: * <p>
053: * Note : Batik handlers are defined statically, and are thus shared by
054: * all its users. However, this shouldn't be a problem since different
055: * web applications live in different classloaders.
056: *
057: * @param manager the component manager used to get the <code>SourceHandler</code>
058: * @param logger the logger for logging.
059: */
060: static {
061: // Keep the default handler, if any
062: defaultHandler = ParsedURL.getHandler(null);
063:
064: // Set the default handler to our handler
065: ParsedURL.registerHandler(new SourceProtocolHandler(null));
066:
067: // Add a new image registry entry to handle image streams
068: ImageTagRegistry.getRegistry().register(
069: new StreamJDKRegistryEntry());
070: }
071:
072: /**
073: * Set the resolver to be used within the current thread.
074: */
075: public static void setup(SourceResolver resolver) {
076: localResolver.set(resolver);
077: }
078:
079: /**
080: * Get the thread-local resolver.
081: */
082: public static SourceResolver getSourceResolver() {
083: SourceResolver resolver = (SourceResolver) localResolver.get();
084: return resolver;
085: }
086:
087: //-------------------------------------------------------------------------
088:
089: public SourceProtocolHandler(String protocol) {
090: super (protocol);
091: }
092:
093: public ParsedURLData parseURL(ParsedURL baseURL, String urlStr) {
094: SourceResolver resolver = (SourceResolver) localResolver.get();
095: if (resolver == null) {
096: // Fall back to the previous default handler
097: return defaultHandler == null ? null : defaultHandler
098: .parseURL(baseURL, urlStr);
099: } else {
100: return new SourceParsedURLData(urlStr, resolver);
101: }
102: }
103:
104: public ParsedURLData parseURL(String urlStr) {
105: SourceResolver resolver = (SourceResolver) localResolver.get();
106: if (resolver == null) {
107: return defaultHandler == null ? null : defaultHandler
108: .parseURL(urlStr);
109: } else {
110: return new SourceParsedURLData(urlStr, resolver);
111: }
112: }
113:
114: /**
115: * Reimplementation of some methods of ParsedURLData since we cannot use <code>java.net.URL</code>.
116: */
117: static class SourceParsedURLData extends ParsedURLData {
118: public String url;
119:
120: private Source source;
121: private SourceResolver resolver;
122:
123: public SourceParsedURLData(String urlStr,
124: SourceResolver resolver) {
125: this .url = urlStr;
126: this .resolver = resolver;
127:
128: // ParsedURLData has some public members which seems to be required to
129: // have a value. This sucks.
130: int pidx = 0, idx;
131:
132: idx = urlStr.indexOf(':');
133: if (idx != -1) {
134: // May have a protocol spec...
135: this .protocol = urlStr.substring(pidx, idx);
136: if (this .protocol.indexOf('/') == -1) {
137: pidx = idx + 1;
138: } else {
139: // Got a slash in protocol probably means
140: // no protocol given, (host and port?)
141: this .protocol = null;
142: pidx = 0;
143: }
144: }
145:
146: idx = urlStr.indexOf(',', pidx);
147: if (idx != -1) {
148: this .host = urlStr.substring(pidx, idx);
149: pidx = idx + 1;
150: }
151: if (pidx != urlStr.length()) {
152: this .path = urlStr.substring(pidx);
153: }
154:
155: // Now do the real job
156:
157: // Setup source
158: try {
159: this .source = resolver.resolveURI(this .url);
160: } catch (Exception e) {
161: throw new CascadingRuntimeException("Cannot resolve "
162: + this .url, e);
163: }
164:
165: // Get Mime-type
166:
167: // First try the source itself
168: this .contentType = this .source.getMimeType();
169:
170: if (this .contentType == null) {
171: // Guess it from the URL extension
172: if (url.endsWith(".gif")) {
173: this .contentType = "image/gif";
174: } else if (url.endsWith(".jpeg")
175: || url.endsWith(".jpg")) {
176: this .contentType = "image/jpeg";
177: } else if (url.endsWith(".png")) {
178: this .contentType = "image/png";
179: }
180: }
181: }
182:
183: public boolean complete() {
184: return (this .url != null);
185: }
186:
187: public String getPortStr() {
188: String portStr = protocol + ":";
189: if (host != null)
190: portStr += host;
191: portStr += ",";
192: return portStr;
193: }
194:
195: public String toString() {
196: return this .url;
197: }
198:
199: /**
200: * Open a stream for the data. If the thread-local <code>SourceResolver</code> exists,
201: * then use it, otherwise fall back to <code>SourceHandler</code>.
202: */
203: protected InputStream openStreamInternal(String userAgent,
204: Iterator mimeTypes, Iterator encodingTypes)
205: throws IOException {
206:
207: try {
208: return source.getInputStream();
209: } catch (Exception e) {
210: throw new CascadingIOException("Cannot open URL "
211: + this.url, e);
212: } finally {
213: this.resolver.release(this.source);
214: }
215: }
216: }
217: }
|