/*
* @(#)PlugInManager.java 1.53 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package javax.media.pim;
import java.lang.reflect.Method;
import javax.media.Format;
import javax.media.*;
import com.sun.media.util.Registry;
import com.sun.media.util.Resource;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
import javax.media.protocol.ContentDescriptor;
import com.sun.media.util.DynamicPlugIn;
/**
* This is a PlugIn manager used to search for installed plug-ins and
* to register new plug-ins. <P>
* <H3>Plug-in types</H3>
* Plug-ins can be of different types such as codecs, demultiplexers,
* renderers, etc.
* <P>The recognized plug-in types are listed below. Other custom plug-in types
* can also be registered.<P>
* "demultiplexer"<BR>"codec"<BR>"renderer"<BR>"multiplexer"<BR>"effect"
* @since JMF 2.0
*/
public class
PlugInManager extends javax.media.PlugInManager {
private static Hashtable lists = null;
private static PlugInVector plugins = null;
private static Registry r = null;
private static Format emptyFormat[] = new Format[0];
private static boolean jdkInit = false;
private static Method forName3ArgsM;
private static Method getSystemClassLoaderM;
private static ClassLoader systemClassLoader;
private static Method getContextClassLoaderM;
static {
readFromRegistry();
}
/****************************************************************
* PlugInManager implementation
****************************************************************/
public
PlugInManager() {
readFromRegistry();
}
private static boolean checkIfJDK12() {
if (jdkInit)
return (forName3ArgsM != null);
jdkInit = true;
try {
forName3ArgsM = Class.class.getMethod("forName",
new Class[] {
String.class, boolean.class, ClassLoader.class
});
getSystemClassLoaderM = ClassLoader.class.getMethod("getSystemClassLoader", null);
// TODO: may need to invoke RuntimePermission("getClassLoader") privilege
systemClassLoader = (ClassLoader) getSystemClassLoaderM.invoke(ClassLoader.class, null);
getContextClassLoaderM = Thread.class.getMethod("getContextClassLoader", null);
return true;
} catch (Throwable t) {
forName3ArgsM = null;
return false;
}
}
// This is a Package private class
static Class getClassForName(String className)
throws ClassNotFoundException {
/**
* Note: if we don't want this functionality
* just replace it with Class.forName(className)
*/
try {
return Class.forName(className);
} catch (Exception e) {
if (!checkIfJDK12()) {
throw new ClassNotFoundException(e.getMessage());
}
} catch (Error e) {
if (!checkIfJDK12()) {
throw e;
}
}
/**
* In jdk1.2 application, when you have jmf.jar in the ext directory and
* you want to access a class that is not in jmf.jar but is in the CLASSPATH,
* you have to load it using the the system class loader.
*/
try {
return (Class) forName3ArgsM.invoke(Class.class, new Object[] {
className, new Boolean(true), systemClassLoader});
} catch (Throwable e) {
}
/**
* In jdk1.2 applet, when you have jmf.jar in the ext directory and
* you want to access a class that is not in jmf.jar but applet codebase,
* you have to load it using the the context class loader.
*/
try {
// TODO: may need to invoke RuntimePermission("getClassLoader") privilege
ClassLoader contextClassLoader =
(ClassLoader) getContextClassLoaderM.invoke(Thread.currentThread(), null);
return (Class) forName3ArgsM.invoke(Class.class, new Object[] {
className, new Boolean(true), contextClassLoader});
} catch (Exception e) {
throw new ClassNotFoundException(e.getMessage());
} catch (Error e) {
throw e;
}
}
/**
* Builds a list of plug-ins that satisfy the specified input and
* output formats. Either or both of the formats could be null if
* they are to be ignored. So, if <code>input</code> is null, then
* it returns a list of plug-ins that match the output format and
* vice-versa.
*/
public synchronized static Vector
getPlugInList( Format input, Format output, int type ) {
ListInfo li = (ListInfo) lists.get(new Integer(type));
if (li == null)
return new Vector(1);
Vector pluginList = li.classNames;
Vector result = new Vector(5);
// Do we need light weight?
boolean blw = false;
Boolean lw = (Boolean) Manager.getHint(Manager.LIGHTWEIGHT_RENDERER);
if (lw != null && lw.booleanValue())
blw = true;
Enumeration eClassNames = pluginList.elements();
while (eClassNames.hasMoreElements()) {
ClassNameInfo cni = (ClassNameInfo) eClassNames.nextElement();
PlugInInfo pii = (PlugInInfo) vectorFindPlugIn(type, cni.className,
cni.hashValue);
if (pii != null) {
if ( match(pii.inputFormats, input) &&
match(pii.outputFormats, output) ) {
if (blw) {
if (cni.className.indexOf("com.sun.media.renderer.video") == 0)
if (!cni.className.equals("com.sun.media.renderer.video.LightWeightRenderer"))
continue;
}
result.addElement(cni.className);
}
}
}
return result;
}
/**
* Sets the search order for the list of plug-ins. This list is valid
* for the duration of the session only, unless <code>commit</code> is
* called.
* @see #commit
*/
public synchronized static void
setPlugInList(Vector pluginList, int type) {
// parameter validity check
if (pluginList == null || type < 1)
return;
// Verify that all the classnames are valid and skip those that aren't
Vector validNames = new Vector(5);
Enumeration eClassNames = pluginList.elements();
while (eClassNames.hasMoreElements()) {
String className = (String) eClassNames.nextElement();
ClassNameInfo cni = new ClassNameInfo(className);
// Is the class name and type in the plugins database?
if (vectorFindPlugIn(type, cni.className, cni.hashValue) != null) {
// Add it to the valid names
validNames.addElement(cni);
}
}
// Put this type and ordered list of class names in the lists hashtable
lists.put(new Integer(type), new ListInfo(type, validNames));
// Purge the run-time supported format database from the
// MediaEngine since the plugin list is modified.
Resource.purgeDB();
}
/**
* Commits any changes made to the plug-in list. Also required
* when a new plug-in is added or a plug-in is removed.
*/
public synchronized static void
commit() throws java.io.IOException {
r.removeGroup("PIM");
r.set("PIM.lists", lists.clone());
Enumeration eListInfo = lists.elements();
while (eListInfo.hasMoreElements()) {
// Get the list info
ListInfo li = (ListInfo) eListInfo.nextElement();
if (li == null) {
System.err.println("li is null????");
continue;
}
int type = li.type;
Vector classNames = li.classNames;
// Enumerate each of the classes in the list
Enumeration eClassNames = classNames.elements();
while (eClassNames.hasMoreElements()) {
ClassNameInfo cni = (ClassNameInfo) eClassNames.nextElement();
// Retrieve the plugin's info
PlugInInfo pii = (PlugInInfo) vectorFindPlugIn(type,
cni.className, cni.hashValue);
if (pii == null) {
System.err.println("pii is null for " + cni.className);
continue;
}
// Save the input and output formats.
r.set("PIM." + type + "_" + cni.className + ".in",
pii.inputFormats);
r.set("PIM." + type + "_" + cni.className + ".out",
pii.outputFormats);
}
}
r.commit();
// Destroy the permanent supported table resource database
// since the plugin registry is permanently changed.
Resource.destroy();
}
/**
* Registers a new plug-in. This plug-in is automatically appended
* to the list of plug-ins. Returns false if the new plug-in could not
* be registered. The reason could be that a plug-in by that name
* already exists. <code>commit</code> has to be called to make this
* addition permanent.
* @param className class name of the plug-in class
* @param in list of supported input formats
* @param out list of supported output formats
* @param type the plug-in type
* @param dynamic true if the input and output formats
* need to be determined at runtime by the plug-in rather
* than from the registry.
*/
public synchronized static boolean
addPlugIn(String className, Format in[], Format out[], int type) {
// Search for the class name in the list
Integer typeInt = new Integer(type);
ListInfo li = (ListInfo) lists.get(typeInt);
Vector v;
ClassNameInfo cni;
if (li != null) {
v = li.classNames;
Enumeration eClassNames = v.elements();
while (eClassNames.hasMoreElements()) {
cni = (ClassNameInfo) eClassNames.nextElement();
// Return false if the class already exists in this list
if (cni.className.equals(className))
return false;
}
} else {
// This is the first item in this list.
v = new Vector(5);
}
// TODO : remove this
// Additional verification to make sure such a class exists
try {
Class piClass = getClassForName(className);
} catch (Throwable t) {
// Sorry dude. You cant fool me with random class names
return false;
}
// Now we aren't checking to see if the plugin already exists
// in the plugins hashtable. We'll just overwrite any older def.
// Append it to the list for this type
cni = new ClassNameInfo(className);
v.addElement(cni);
// Set the list on the type
lists.put(typeInt, new ListInfo(type, v));
if (in == null)
in = emptyFormat;
if (out == null)
out = emptyFormat;
vectorInsertPlugIn(new PlugInInfo(type, className,
cni.hashValue, in, out));
// Purge the run-time supported format database from the
// MediaEngine since the plugin list is modified.
Resource.purgeDB();
return true;
}
/**
* Removes an existing plug-in from the registry. Returns false if
* a plug-in by that name couldn't be found. <code>commit</code> has
* to be called to make this change permanent.
*/
public synchronized static boolean
removePlugIn(String className, int type) {
// First remove it from its type list if it exists.
Integer typeInt = new Integer(type);
ListInfo li = (ListInfo) lists.get(typeInt);
ClassNameInfo cni = null;
PlugInInfo pii = null;
if (li != null) {
Vector v = li.classNames;
Enumeration eClassNames = v.elements();
while (eClassNames.hasMoreElements()) {
cni = (ClassNameInfo) eClassNames.nextElement();
if (cni.className.equals(className)) {
v.removeElement(cni);
break;
}
}
}
if (cni != null) {
pii = vectorRemovePlugIn(type, className, cni.hashValue);
// Purge the run-time supported format database from the
// MediaEngine since the plugin list is modified.
Resource.purgeDB();
}
return(pii != null);
}
public synchronized static Format[]
getSupportedInputFormats(String className, int type) {
ClassNameInfo cni = new ClassNameInfo(className);
PlugInInfo pii = (PlugInInfo) vectorFindPlugIn(type, className,
cni.hashValue);
if (pii == null)
return emptyFormat;
return pii.inputFormats;
}
public synchronized static Format[]
getSupportedOutputFormats(String className, int type) {
ClassNameInfo cni = new ClassNameInfo(className);
PlugInInfo pii = (PlugInInfo) vectorFindPlugIn(type, className,
cni.hashValue);
if (pii == null)
return emptyFormat;
return pii.outputFormats;
}
/****************************************************************
* Local Methods
****************************************************************/
static private void
vectorInsertPlugIn(PlugInInfo pii) {
plugins.addElement(pii);
}
static private PlugInInfo
vectorFindPlugIn(int type, String className, long hashValue) {
return(plugins.lookup(type, className, hashValue));
}
static private PlugInInfo
vectorRemovePlugIn(int type, String className, long hashValue) {
PlugInInfo pii = vectorFindPlugIn(type, className, hashValue);
if (pii != null) {
plugins.removeElement(pii);
return pii;
}
return null;
}
// Check if a certain format is compatible with any in an array of formats.
private synchronized static boolean
match(Format [] formats, Format tomatch) {
if (tomatch == null || formats == null)
return true;
for (int i = 0; i < formats.length; i++) {
if (formats[i].matches(tomatch))
return true;
}
return false;
}
private static void
readFromRegistry() {
// If its already initialized, dont read the registry.
if (lists != null)
return;
// Create a hashtable for the plugins info.
plugins = new PlugInVector(40);
// Read the plug-in lists for the different types all stored
// into one hashtable whose key is the type.
r = new Registry();
Object list = r.get("PIM.lists");
if (list != null && list instanceof Hashtable) {
lists = (Hashtable) list;
} else {
// No plugin lists were found.
// This should happen only during compilation of RegistryLib
lists = new Hashtable(8);
System.out.println("No plugins found");
//registerDefaultPlugIns();
return;
}
Enumeration eListInfo = lists.elements();
while (eListInfo.hasMoreElements()) {
// Get the list info
ListInfo li = (ListInfo) eListInfo.nextElement();
int type = li.type;
Vector classNames = li.classNames;
Vector validClassNames = new Vector(5);
// Enumerate each of the classes in the list
Enumeration eClassNames = classNames.elements();
while (eClassNames.hasMoreElements()) {
ClassNameInfo cni = (ClassNameInfo) eClassNames.nextElement();
// Make the key using the type and the classname
String key = type + "_" + cni.className;
// Try to get the input and output formats. If something
// fails during deserialization, the objects will be null
Object f1 = r.get("PIM." + key + ".in");
Object f2 = r.get("PIM." + key + ".out");
// If the format arrays are valid, add it to the plugins hashtable
if ( f1 != null && f1 instanceof Format[] &&
f2 != null && f2 instanceof Format[] ) {
PlugInInfo pii = new PlugInInfo(type, cni.className,
cni.hashValue, (Format[]) f1, (Format[]) f2);
vectorInsertPlugIn(pii);
// This plugin is valid, add to the plugin list for this type
validClassNames.addElement(cni);
}
}
// Add to the list of plugin lists
lists.put(new Integer(type), new ListInfo(type, validClassNames));
}
}
}
class
ListInfo implements Serializable {
public int type;
public Vector classNames;
public
ListInfo(int type, Vector classNames) {
this.type = type;
this.classNames = classNames;
}
}
class
PlugInInfo {
public int type;
public String className;
public long hashValue;
public Format inputFormats[];
public Format outputFormats[];
public
PlugInInfo(int type, String className, long hashValue,
Format inputFormats[], Format outputFormats[]) {
this.type = type;
this.className = className;
this.hashValue = hashValue;
this.inputFormats = inputFormats;
this.outputFormats = outputFormats;
}
}
class
ClassNameInfo implements Serializable {
public String className;
public long hashValue;
public
ClassNameInfo(String className) {
this.className = className;
hashValue = makeHashValue(className);
}
public final static long
makeHashValue(String className) {
final int MAXCHARS = 22;
long h = 0;
char val[] = className.toCharArray();
int len = val.length;
int off = (val.length < MAXCHARS) ? 0 : val.length - MAXCHARS;
for (int i = off; i < len; i++) {
h = (h * 37) + val[off++];
}
return(h);
}
}
/* Would have like to extend the Vector class, but the insertElementAt()
* and elementAt() methods are final there.
*/
class
PlugInVector {
private Vector elements = null;
/**
* Constructs an empty plugin vector with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the vector.
*/
public
PlugInVector(int initialCapacity) {
elements = new Vector(initialCapacity, 0);
}
/**
* Add the new PlugInInfo instance into the sorted (by hashValue)
* vector of PlugInInfo objects.
*
* @param obj the component to be added.
*
* XXX: it's remotely possibly that we are going to get duplicate hash values.
*/
public final synchronized void
addElement(PlugInInfo pii) {
int i;
for (i = 0; i < elements.size(); i++) {
PlugInInfo current = (PlugInInfo) elements.elementAt(i);
if (current.hashValue == pii.hashValue &&
!current.className.equals(pii.className)) {
System.err.println("Problem adding " + pii.className +
" to plugin table.");
System.err.println(" Already hash value of " + pii.hashValue +
" in plugin table for class name of " +
current.className);
break;
} else if (current.hashValue > pii.hashValue) {
break;
}
}
elements.insertElementAt(pii, i);
}
/**
* Removes the first occurrence of the given PlugInInfo instance from
* the vector of plugins.
*
* @param obj the component to be removed.
*
* @return <code>true</code> if the argument was a component of this
* vector; <code>false</code> otherwise.
*/
public final synchronized boolean
removeElement(PlugInInfo pii) {
return(elements.removeElement(pii));
}
/**
* Search to see if a PlugInInfo object with the given type and classname
* is present in the vector of plugins.
*
* @param type the type of the plugin.
* @param className the class name of the plugin.
* @param hashValue the hash value for the class name.
*
* @return the first PlugIniInfo instance with the given type and classname
* if found, otherwise null.
*/
public final synchronized PlugInInfo
lookup(int type, String className, long hashValue) {
PlugInInfo current = null;
int low = 0;
int high = elements.size();
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
current = (PlugInInfo) elements.elementAt(mid);
if (hashValue < current.hashValue) {
high = mid - 1;
} else if (hashValue > current.hashValue) {
low = mid + 1;
} else {
if (type == current.type &&
className.equals(current.className)) {
return(current);
} else {
/* It's remotely possible that there are two (or more) elements in this
* plugin vector with the same hash value. If that's the case, then search
* for them (in both directions). Note for code simplicity sake, it repeats
* the test with the 'mid'th plugin element.
*/
int i = 0;
do {
current = (PlugInInfo) elements.elementAt(mid-i);
if (hashValue != current.hashValue) {
break;
}
if (type == current.type &&
className.equals(current.className)) {
return(current);
}
i++;
} while (mid-i >= 0);
i = 0;
do {
current = (PlugInInfo) elements.elementAt(mid+i);
if (hashValue != current.hashValue) {
break;
}
if (type == current.type &&
className.equals(current.className)) {
return(current);
}
i++;
} while (mid+i < elements.size());
// break from the outer loop if a match
// cannot be found.
break;
}
}
}
return(null);
}
}
|