blob: d0b8c0a9d05b208e8f82466c9fb4b54a8351f4ef [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.components.url;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
import org.apache.batik.util.AbstractParsedURLProtocolHandler;
import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.ParsedURLData;
import org.apache.batik.util.ParsedURLProtocolHandler;
import org.apache.cocoon.CascadingIOException;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceResolver;
/**
* A Batik protocol handler that handles all Cocoon sources. This allows
* <svg:image xlink:href="..."/> to use any of the protocols handled by Cocoon.
*
* @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
* @version CVS $Id: SourceProtocolHandler.java,v 1.2 2004/03/05 13:01:45 bdelacretaz Exp $
*/
public class SourceProtocolHandler extends AbstractParsedURLProtocolHandler {
/** Thread-local source resolver */
protected static InheritableThreadLocal localResolver = new InheritableThreadLocal();
/** Batik's original default handler */
protected static ParsedURLProtocolHandler defaultHandler;
/**
* Change the default handler used by Batik to resolve URLs to a handler
* based on <code>SourceResolver</code> and <code>SourceHandler</code>.
* <p>
* Note : Batik handlers are defined statically, and are thus shared by
* all its users. However, this shouldn't be a problem since different
* web applications live in different classloaders.
*
* @param manager the component manager used to get the <code>SourceHandler</code>
* @param logger the logger for logging.
*/
static {
// Keep the default handler, if any
SourceProtocolHandler.defaultHandler = ParsedURL.getHandler(null);
// Set the default handler to our handler
ParsedURL.registerHandler(new SourceProtocolHandler(null));
// Add a new image registry entry to handle image streams
ImageTagRegistry.getRegistry().register(new StreamJDKRegistryEntry());
}
/**
* Set the resolver to be used within the current thread.
*/
public static void setup(SourceResolver resolver) {
localResolver.set(resolver);
}
/**
* Get the thread-local resolver.
*/
public static SourceResolver getSourceResolver()
{
SourceResolver resolver = (SourceResolver)localResolver.get();
return resolver;
}
//-------------------------------------------------------------------------
public SourceProtocolHandler(String protocol) {
super(protocol);
}
public ParsedURLData parseURL(ParsedURL baseURL, String urlStr) {
SourceResolver resolver = (SourceResolver)localResolver.get();
if (resolver == null) {
// Fall back to the previous default handler
return defaultHandler == null ? null : defaultHandler.parseURL(baseURL, urlStr);
} else {
return new SourceParsedURLData(urlStr, resolver);
}
}
public ParsedURLData parseURL(String urlStr) {
SourceResolver resolver = (SourceResolver)localResolver.get();
if (resolver == null) {
return defaultHandler == null ? null : defaultHandler.parseURL(urlStr);
} else {
return new SourceParsedURLData(urlStr, resolver);
}
}
/**
* Reimplementation of some methods of ParsedURLData since we cannot use <code>java.net.URL</code>.
*/
static class SourceParsedURLData extends ParsedURLData {
public String url;
private Source source;
private SourceResolver resolver;
public SourceParsedURLData(String urlStr, SourceResolver resolver) {
this.url = urlStr;
this.resolver = resolver;
// ParsedURLData has some public members which seems to be required to
// have a value. This sucks.
int pidx=0, idx;
idx = urlStr.indexOf(':');
if (idx != -1) {
// May have a protocol spec...
this.protocol = urlStr.substring(pidx, idx);
if (this.protocol.indexOf('/') == -1)
pidx = idx+1;
else {
// Got a slash in protocol probably means
// no protocol given, (host and port?)
this.protocol = null;
pidx = 0;
}
}
idx = urlStr.indexOf(',',pidx);
if (idx != -1) {
this.host = urlStr.substring(pidx, idx);
pidx = idx+1;
}
if (pidx != urlStr.length())
this.path = urlStr.substring(pidx);
// Now do the real job
// Setup source
try {
this.source = resolver.resolveURI(this.url);
} catch(Exception e) {
throw new CascadingRuntimeException("Cannot resolve " + this.url, e);
}
// Get Mime-type
// First try the source itself
this.contentType = this.source.getMimeType();
if (this.contentType == null) {
// Guess it from the URL extension
if (url.endsWith(".gif")) {
this.contentType = "image/gif";
} else if (url.endsWith(".jpeg") || url.endsWith(".jpg")) {
this.contentType = "image/jpeg";
} else if (url.endsWith(".png")) {
this.contentType = "image/png";
}
}
}
public boolean complete() {
return (this.url != null);
}
public String getPortStr() {
String portStr = protocol+":";
if (host != null) portStr += host;
portStr += ",";
return portStr;
}
public String toString() {
return this.url;
}
/**
* Open a stream for the data. If the thread-local <code>SourceResolver</code> exists,
* then use it, otherwise fall back to <code>SourceHandler</code>.
*/
protected InputStream openStreamInternal (String userAgent, Iterator mimeTypes, Iterator encodingTypes)
throws IOException {
try {
return source.getInputStream();
} catch(Exception e) {
throw new CascadingIOException("Cannot open URL " + this.url, e);
} finally {
this.resolver.release(this.source);
}
}
}
}