//========================================================================
//$Id: AndroidClassLoader.java 391 2011-02-08 01:06:04Z janb.webtide $
//Copyright 2008 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//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.mortbay.ijetty.webapp;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.jetty.webapp.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import dalvik.system.DexClassLoader;
/**
* AndroidClassLoader
*
* Loads classes dynamically from dex files wrapped inside a zip.
*/
@SuppressWarnings("unchecked")
public class AndroidClassLoader extends ClassLoader //extends WebAppClassLoader
{
private WebAppContext _context;
private ClassLoader _parent;
private ClassLoader _delegate;
private String _path = "";
public AndroidClassLoader(String path, ClassLoader parent, WebAppContext context) throws IOException
{
this(parent, context);
_path = path;
}
public AndroidClassLoader(WebAppContext context)
throws IOException
{
this (ClassLoader.getSystemClassLoader(), context);
}
public AndroidClassLoader(ClassLoader parent, WebAppContext context)
throws IOException
{
//super(parent, context);
_parent = parent;
_context = context;
}
public WebAppContext getContext ()
{
return _context;
}
public void init ()
throws IOException
{
if (_path==null || "".equals(_path.trim()))
_delegate = new DexClassLoader("", ((WebAppContext)getContext()).getTempDirectory().getCanonicalPath(),null,_parent);
else
_delegate = new DexClassLoader(_path, ((WebAppContext)getContext()).getTempDirectory().getCanonicalPath(), null, _parent);
if (Log.isDebugEnabled()) Log.debug("Android webapp classloader path= "+_path+" tmpdir="+ ((WebAppContext)getContext()).getTempDirectory()+" dexloader = "+_delegate+" parentloader="+_parent);
}
public void addClassPath(Resource resource)
throws IOException
{
if (resource instanceof ResourceCollection)
{
for (Resource r : ((ResourceCollection)resource).getResources())
addClassPath(r);
}
else
{
addClassPath(resource.getFile().getAbsolutePath());
}
}
/**
* Accept a pre-made classpath.
* NOTE: the path elements must be separated by ":" chars, not ";"
* @see org.eclipse.jetty.webapp.WebAppClassLoader#addClassPath(java.lang.String)
*/
public void addClassPath(String classPath) throws IOException
{
if (classPath == null)
return;
if (!"".equals(_path) && !_path.endsWith(":"))
_path += ":";
_path += classPath;
Log.debug("Path = "+_path);
}
public void addJars(Resource lib)
{
if (lib.exists() && lib.isDirectory())
{
String[] files=lib.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
{
Resource fn=lib.addPath(files[f]);
String fnlc=fn.getName().toLowerCase();
if (!fn.isDirectory() && isAndroidArchive(fnlc))
{
String jar=fn.getFile().getAbsolutePath();
addClassPath(jar);
}
}
catch (Exception ex)
{
Log.warn(Log.EXCEPTION,ex);
}
}
}
}
protected boolean isAndroidArchive (String filename)
{
int dot = filename.lastIndexOf('.');
if (dot == -1)
return false;
String extension = filename.substring(dot);
return ".zip".equals(extension) || ".apk".equals(extension);
}
public Enumeration<URL> getResources(String name) throws IOException
{
boolean system_class=_context.isSystemClass(name);
boolean server_class=_context.isServerClass(name);
List<URL> from_parent = toList(server_class?null:_parent.getResources(name));
List<URL> from_webapp = toList((system_class&&!from_parent.isEmpty())?null:this.findResources(name));
if (_context.isParentLoaderPriority())
{
from_parent.addAll(from_webapp);
return Collections.enumeration(from_parent);
}
from_webapp.addAll(from_parent);
return Collections.enumeration(from_webapp);
}
private List<URL> toList(Enumeration<URL> e)
{
List<URL> list = new ArrayList<URL>();
while (e!=null && e.hasMoreElements())
list.add(e.nextElement());
return list;
}
public URL getResource(String name)
{
URL url= null;
boolean tried_parent= false;
boolean system_class=_context.isSystemClass(name);
boolean server_class=_context.isServerClass(name);
if (system_class && server_class)
return null;
if (_parent!=null &&(_context.isParentLoaderPriority() || system_class ) && !server_class)
{
tried_parent= true;
if (_parent!=null)
url= _parent.getResource(name);
}
if (url == null)
{
url= this.findResource(name);
if (url == null && name.startsWith("/"))
{
if (Log.isDebugEnabled())
Log.debug("HACK leading / off " + name);
url= this.findResource(name.substring(1));
}
}
if (url == null && !tried_parent && !server_class )
{
if (_parent!=null)
url= _parent.getResource(name);
}
if (url != null)
if (Log.isDebugEnabled())
Log.debug("getResource("+name+")=" + url);
return url;
}
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException
{
Class<?> c= findLoadedClass(name);
ClassNotFoundException ex= null;
boolean tried_parent= false;
boolean system_class=((WebAppContext)getContext()).isSystemClass(name);
boolean server_class=((WebAppContext)getContext()).isServerClass(name);
if (system_class && server_class)
{
return null;
}
if (c == null && _parent!=null && (((WebAppContext)getContext()).isParentLoaderPriority() || system_class) && !server_class)
{
tried_parent= true;
try
{
c= _parent.loadClass(name);
if (Log.isDebugEnabled())
Log.debug("loaded " + c);
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
if (c == null)
{
try
{
if (_delegate != null)
{
if (Log.isDebugEnabled()) Log.debug("loading class "+name+" trying delegate loader" +_delegate);
c = _delegate.loadClass(name);
if (Log.isDebugEnabled()) Log.debug("delegate loaded " + c);
}
}
catch (ClassNotFoundException e)
{
ex= e;
}
}
if (c == null && _parent!=null && !tried_parent && !server_class )
c= _parent.loadClass(name);
if (c == null)
throw ex;
if (resolve)
resolveClass(c);
if (Log.isDebugEnabled())
Log.debug("loaded " + c+ " from "+c.getClassLoader());
return c;
}
@Override
public String toString()
{
return "(AndroidClassLoader, delegate=" + _delegate + ")";
}
}