/**
* $RCSfile: ,v $
* $Revision: $
* $Date: $
*
* Copyright (C) 2004-2011 Jive Software. All rights reserved.
*
* 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.jivesoftware.spark.plugin;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.spark.util.URLFileSystem;
import org.jivesoftware.spark.util.log.Log;
import org.xmlpull.mxp1.MXParser;
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* A simple classloader to extend the classpath to
* include all jars in a lib directory.<p>
* <p/>
* The new classpath includes all <tt>*.jar files.
*
* @author Derek DeMoro
*/
public class PluginClassLoader extends URLClassLoader {
/**
* Constructs the classloader.
*
* @param parent the parent class loader (or null for none).
* @param libDir the directory to load jar files from.
* @throws java.net.MalformedURLException if the libDir path is not valid.
*/
public PluginClassLoader(ClassLoader parent, File libDir) throws MalformedURLException {
super(new URL[]{libDir.toURI().toURL()}, parent);
}
/**
* Adds all archives in a plugin to the classpath.
*
* @param pluginDir the directory of the plugin.
* @throws MalformedURLException the exception thrown if URL is not valid.
*/
public void addPlugin(File pluginDir) throws MalformedURLException {
File libDir = new File(pluginDir, "lib");
File[] jars = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
boolean accept = false;
String smallName = name.toLowerCase();
if (smallName.endsWith(".jar")) {
accept = true;
}
else if (smallName.endsWith(".zip")) {
accept = true;
}
return accept;
}
});
// Do nothing if no jar or zip files were found
if (jars == null) {
return;
}
for (File jar : jars) {
if (jar.isFile()) {
final URL url = jar.toURL();
addURL(url);
try {
checkForSmackProviders(url);
}
catch (Throwable e) {
Log.error(e);
}
}
}
}
private void checkForSmackProviders(URL jarURL) throws Throwable {
ZipFile zipFile = new JarFile(URLFileSystem.url2File(jarURL));
ZipEntry entry = zipFile.getEntry("META-INF/smack.providers");
if (entry != null) {
InputStream zin = zipFile.getInputStream(entry);
loadSmackProvider(zin);
}
}
private void loadSmackProvider(InputStream providerStream) throws Exception {
// Get an array of class loaders to try loading the providers files from.
try {
XmlPullParser parser = new MXParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(providerStream, "UTF-8");
int eventType = parser.getEventType();
do {
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("iqProvider")) {
parser.next();
parser.next();
String elementName = parser.nextText();
parser.next();
parser.next();
String namespace = parser.nextText();
parser.next();
parser.next();
String className = parser.nextText();
// Only add the provider for the namespace if one isn't
// already registered.
// Attempt to load the provider class and then create
// a new instance if it's an IQProvider. Otherwise, if it's
// an IQ class, add the class object itself, then we'll use
// reflection later to create instances of the class.
try {
// Add the provider to the map.
Class<?> provider = this.loadClass(className);
if (IQProvider.class.isAssignableFrom(provider)) {
ProviderManager.getInstance().addIQProvider(elementName, namespace, provider.newInstance());
}
else if (IQ.class.isAssignableFrom(provider)) {
ProviderManager.getInstance().addIQProvider(elementName, namespace, provider.newInstance());
}
}
catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
else if (parser.getName().equals("extensionProvider")) {
parser.next();
parser.next();
String elementName = parser.nextText();
parser.next();
parser.next();
String namespace = parser.nextText();
parser.next();
parser.next();
String className = parser.nextText();
// Only add the provider for the namespace if one isn't
// already registered.
// Attempt to load the provider class and then create
// a new instance if it's a Provider. Otherwise, if it's
// a PacketExtension, add the class object itself and
// then we'll use reflection later to create instances
// of the class.
try {
// Add the provider to the map.
Class<?> provider = this.loadClass(className);
if (PacketExtensionProvider.class.isAssignableFrom(
provider)) {
ProviderManager.getInstance().addExtensionProvider(elementName, namespace, provider.newInstance());
}
else if (PacketExtension.class.isAssignableFrom(
provider)) {
ProviderManager.getInstance().addExtensionProvider(elementName, namespace, provider.newInstance());
}
}
catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
}
eventType = parser.next();
}
while (eventType != XmlPullParser.END_DOCUMENT);
}
finally {
try {
providerStream.close();
}
catch (Exception e) {
// Nothing to do
}
}
}
}