blob: a140f5375fc52ea31dbf5102759b37a1bf428032 [file] [log] [blame]
/*
* 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.searchpolicy;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.jar.Manifest;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.SecureAction;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.manifestparser.ManifestParser;
import org.apache.felix.moduleloader.*;
public class ContentLoaderImpl implements IContentLoader
{
private final Logger m_logger;
private final IContent m_content;
private IContent[] m_contentPath;
private IContent[] m_fragmentContents = null;
private ISearchPolicy m_searchPolicy = null;
private IURLPolicy m_urlPolicy = null;
private ContentClassLoader m_classLoader;
private ProtectionDomain m_protectionDomain = null;
private static SecureAction m_secureAction = new SecureAction();
public ContentLoaderImpl(Logger logger, IContent content)
{
m_logger = logger;
m_content = content;
}
public Logger getLogger()
{
return m_logger;
}
public synchronized void close()
{
m_content.close();
for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
{
m_contentPath[i].close();
}
for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
{
m_fragmentContents[i].close();
}
synchronized (this)
{
m_classLoader = null;
}
}
public IContent getContent()
{
return m_content;
}
public synchronized IContent[] getClassPath()
{
if (m_contentPath == null)
{
try
{
m_contentPath = initializeContentPath();
}
catch (Exception ex)
{
m_logger.log(Logger.LOG_ERROR, "Unable to get module class path.", ex);
}
}
return m_contentPath;
}
public synchronized void attachFragmentContents(IContent[] fragmentContents)
throws Exception
{
// Close existing fragment contents.
if (m_fragmentContents != null)
{
for (int i = 0; i < m_fragmentContents.length; i++)
{
m_fragmentContents[i].close();
}
}
m_fragmentContents = fragmentContents;
if (m_contentPath != null)
{
for (int i = 0; i < m_contentPath.length; i++)
{
m_contentPath[i].close();
}
}
m_contentPath = initializeContentPath();
}
public synchronized void setSearchPolicy(ISearchPolicy searchPolicy)
{
m_searchPolicy = searchPolicy;
}
public synchronized ISearchPolicy getSearchPolicy()
{
return m_searchPolicy;
}
public synchronized void setURLPolicy(IURLPolicy urlPolicy)
{
m_urlPolicy = urlPolicy;
}
public synchronized IURLPolicy getURLPolicy()
{
return m_urlPolicy;
}
public synchronized void setSecurityContext(Object securityContext)
{
m_protectionDomain = (ProtectionDomain) securityContext;
}
public synchronized Object getSecurityContext()
{
return m_protectionDomain;
}
public Class getClass(String name)
{
synchronized (this)
{
if (m_classLoader == null)
{
m_classLoader = m_secureAction.createContentClassLoader(this,
m_protectionDomain);
}
}
try
{
return m_classLoader.loadClassFromModule(name);
}
catch (ClassNotFoundException ex)
{
return null;
}
}
public URL getResource(String name)
{
URL url = null;
// Remove leading slash, if present, but special case
// "/" so that it returns a root URL...this isn't very
// clean or meaninful, but the Spring guys want it.
if (name.equals("/"))
{
// Just pick a class path index since it doesn't really matter.
url = getURLPolicy().createURL(1, name);
}
else if (name.startsWith("/"))
{
name = name.substring(1);
}
// Check the module class path.
IContent[] contentPath = getClassPath();
for (int i = 0;
(url == null) &&
(i < contentPath.length); i++)
{
if (contentPath[i].hasEntry(name))
{
url = getURLPolicy().createURL(i + 1, name);
}
}
return url;
}
public Enumeration getResources(String name)
{
Vector v = new Vector();
// Special case "/" so that it returns a root URLs for
// each bundle class path entry...this isn't very
// clean or meaningful, but the Spring guys want it.
if (name.equals("/"))
{
for (int i = 0; i < getClassPath().length; i++)
{
v.addElement(getURLPolicy().createURL(i + 1, name));
}
}
else
{
// Remove leading slash, if present.
if (name.startsWith("/"))
{
name = name.substring(1);
}
// Check the module class path.
IContent[] contentPath = getClassPath();
for (int i = 0; i < contentPath.length; i++)
{
if (contentPath[i].hasEntry(name))
{
// Use the class path index + 1 for creating the path so
// that we can differentiate between module content URLs
// (where the path will start with 0) and module class
// path URLs.
v.addElement(getURLPolicy().createURL(i + 1, name));
}
}
}
return v.elements();
}
// TODO: API: Investigate how to handle this better, perhaps we need
// multiple URL policies, one for content -- one for class path.
public URL getResourceFromContent(String name)
{
URL url = null;
// Check for the special case of "/", which represents
// the root of the bundle according to the spec.
if (name.equals("/"))
{
url = getURLPolicy().createURL(0, "/");
}
if (url == null)
{
// Remove leading slash, if present.
if (name.startsWith("/"))
{
name = name.substring(1);
}
// Check the module content.
if (getContent().hasEntry(name))
{
// Module content URLs start with 0, whereas module
// class path URLs start with the index into the class
// path + 1.
url = getURLPolicy().createURL(0, name);
}
}
return url;
}
public boolean hasInputStream(int index, String urlPath)
{
if (urlPath.startsWith("/"))
{
urlPath = urlPath.substring(1);
}
if (index == 0)
{
return m_content.hasEntry(urlPath);
}
return getClassPath()[index - 1].hasEntry(urlPath);
}
public InputStream getInputStream(int index, String urlPath)
throws IOException
{
if (urlPath.startsWith("/"))
{
urlPath = urlPath.substring(1);
}
if (index == 0)
{
return m_content.getEntryAsStream(urlPath);
}
return getClassPath()[index - 1].getEntryAsStream(urlPath);
}
public synchronized String toString()
{
return m_searchPolicy.toString();
}
private IContent[] initializeContentPath() throws Exception
{
List contentList = new ArrayList();
calculateContentPath(m_content, contentList, true);
for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
{
calculateContentPath(m_fragmentContents[i], contentList, false);
}
return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
}
private List calculateContentPath(IContent content, List contentList, boolean searchFragments)
throws Exception
{
// Creating the content path entails examining the bundle's
// class path to determine whether the bundle JAR file itself
// is on the bundle's class path and then creating content
// objects for everything on the class path.
// Create a list to contain the content path for the specified content.
List localContentList = new ArrayList();
// Get the bundle's manifest header.
InputStream is = null;
Map headers = null;
try
{
// TODO: REFACTOR - It seems that we shouldn't have to get the manifest
// here since we already have it in our module definition, but we
// don't have access to the module definition here. This is something
// to be considered when we refactor the module layer.
is = content.getEntryAsStream("META-INF/MANIFEST.MF");
headers = new StringMap(new Manifest(is).getMainAttributes(), false);
}
finally
{
if (is != null) is.close();
}
// Find class path meta-data.
String classPath = (headers == null)
? null : (String) headers.get(FelixConstants.BUNDLE_CLASSPATH);
// Parse the class path into strings.
String[] classPathStrings = ManifestParser.parseDelimitedString(
classPath, FelixConstants.CLASS_PATH_SEPARATOR);
if (classPathStrings == null)
{
classPathStrings = new String[0];
}
// Create the bundles class path.
for (int i = 0; i < classPathStrings.length; i++)
{
// Remove any leading slash, since all bundle class path
// entries are relative to the root of the bundle.
classPathStrings[i] = (classPathStrings[i].startsWith("/"))
? classPathStrings[i].substring(1)
: classPathStrings[i];
// Check for the bundle itself on the class path.
if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
{
localContentList.add(content);
}
else
{
// Try to find the embedded class path entry in the current
// content.
IContent embeddedContent = content.getEntryAsContent(classPathStrings[i]);
// If the embedded class path entry was not found, it might be
// in one of the fragments if the current content is the bundle,
// so try to search the fragments if necessary.
for (int fragIdx = 0;
searchFragments && (embeddedContent == null)
&& (m_fragmentContents != null) && (fragIdx < m_fragmentContents.length);
fragIdx++)
{
embeddedContent = m_fragmentContents[fragIdx].getEntryAsContent(classPathStrings[i]);
}
// If we found the embedded content, then add it to the
// class path content list.
if (embeddedContent != null)
{
localContentList.add(embeddedContent);
}
else
{
// TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
// need to create an "Eventer" class like "Logger" perhaps.
m_logger.log(Logger.LOG_INFO,
"Class path entry not found: "
+ classPathStrings[i]);
}
}
}
// If there is nothing on the class path, then include
// "." by default, as per the spec.
if (localContentList.size() == 0)
{
localContentList.add(content);
}
// Now add the local contents to the global content list and return it.
contentList.addAll(localContentList);
return contentList;
}
}