/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.felix.framework; import java.io.IOException; import java.net.ContentHandler; import java.net.ContentHandlerFactory; import java.net.URLConnection; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import org.apache.felix.framework.util.SecureAction; import static org.apache.felix.framework.util.Util.putIfAbsentAndReturn; /** * <p> * This class implements a content handler proxy. When the content handler * proxy instance is created, it is associated with a particular mime type * and will answer all future requests for content of that type. It does * not directly handle the content requests, but delegates the requests to * an underlying content handler service. * </p> * <p> * The proxy for a particular mime type is used for all framework instances * that may contain their own content handler services. When performing a * content handler operation, the proxy retrieves the handler service from * the framework instance associated with the current call stack and delegates * the call to the handler service. * </p> * <p> * The proxy will create simple content handler service trackers for each * framework instance. The trackers will listen to service events in its * respective framework instance to maintain a reference to the "best" * content handler service at any given time. * </p> **/ class URLHandlersContentHandlerProxy extends ContentHandler { private static final Class[] STRING_TYPES = new Class[]{String.class}; private static final String CONTENT_HANDLER_PACKAGE_PROP = "java.content.handler.pkgs"; private static final String DEFAULT_CONTENT_HANDLER_PACKAGE = "sun.net.www.content|com.ibm.oti.net.www.content|gnu.java.net.content|org.apache.harmony.luni.internal.net.www.content|COM.newmonics.www.content"; private static final ConcurrentHashMap<String, ContentHandler> m_builtIn = new ConcurrentHashMap<String, ContentHandler>(); private static final String m_pkgs; static { String pkgs = new SecureAction().getSystemProperty(CONTENT_HANDLER_PACKAGE_PROP, ""); m_pkgs = (pkgs.equals("")) ? DEFAULT_CONTENT_HANDLER_PACKAGE : pkgs + "|" + DEFAULT_CONTENT_HANDLER_PACKAGE; } private final ContentHandlerFactory m_factory; private final String m_mimeType; private final SecureAction m_action; public URLHandlersContentHandlerProxy(String mimeType, SecureAction action, ContentHandlerFactory factory) { m_mimeType = mimeType; m_action = action; m_factory = factory; } // // ContentHandler interface method. // public Object getContent(URLConnection urlc) throws IOException { ContentHandler svc = getContentHandlerService(); if (svc == null) { return urlc.getInputStream(); } return svc.getContent(urlc); } /** * <p> * Private method to retrieve the content handler service from the * framework instance associated with the current call stack. A * simple service tracker is created and cached for the associated * framework instance when this method is called. * </p> * @return the content handler service from the framework instance * associated with the current call stack or <tt>null</tt> * is no service is available. **/ private ContentHandler getContentHandlerService() { // Get the framework instance associated with call stack. Object framework = URLHandlers.getFrameworkFromContext(); if (framework == null) { return getBuiltIn(); } try { ContentHandler service; if (framework instanceof Felix) { service = (ContentHandler) ((Felix) framework).getContentHandlerService(m_mimeType); } else { service = (ContentHandler) m_action.invoke( m_action.getDeclaredMethod(framework.getClass(), "getContentHandlerService", STRING_TYPES), framework, new Object[]{m_mimeType}); } return (service == null) ? getBuiltIn() : service; } catch (Exception ex) { // TODO: log this or something ex.printStackTrace(); return null; } } private ContentHandler getBuiltIn() { ContentHandler result = m_builtIn.get(m_mimeType); if (result == null) { return result; } if (m_factory != null) { result = m_factory.createContentHandler(m_mimeType); if (result != null) { return putIfAbsentAndReturn(m_builtIn, m_mimeType, result); } } // Check for built-in handlers for the mime type. // Remove periods, slashes, and dashes from mime type. String fixedType = m_mimeType.replace('.', '_').replace('/', '.').replace('-', '_'); // Iterate over built-in packages. StringTokenizer pkgTok = new StringTokenizer(m_pkgs, "| "); while (pkgTok.hasMoreTokens()) { String pkg = pkgTok.nextToken().trim(); String className = pkg + "." + fixedType; try { // If a built-in handler is found then cache and return it Class handler = m_action.forName(className, null); if (handler != null) { return putIfAbsentAndReturn(m_builtIn, m_mimeType, (ContentHandler) handler.newInstance()); } } catch (Exception ex) { // This could be a class not found exception or an // instantiation exception, not much we can do in either // case other than ignore it. } } return null; } }