gerv%gerv.net 8b69962ee3 Bug 236613: change to MPL/LGPL/GPL tri-license.
git-svn-id: svn://10.0.0.236/trunk@155500 18797224-902f-48f8-a5cc-f745e15eee43
2004-04-25 21:07:34 +00:00

583 lines
22 KiB
Java

/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package netscape.plugin.composer;
import java.util.*;
import java.awt.image.ImageProducer;
import java.awt.image.MemoryImageSource;
import java.io.*;
import java.net.URL;
import netscape.javascript.JSObject;
/* This class is private to the composer. It manages the plugins.
*/
class PluginManager {
public PluginManager(){
}
public void registerPlugin(String pluginFileName, String iniContents){
Properties properties = new Properties();
File pluginFile = new File(pluginFileName);
try {
InputStream stream = new StringBufferInputStream(iniContents);
properties.load(stream);
stream.close();
} catch ( IOException e) {
System.err.println("Caught exception while parsing .ini contents:\n" + iniContents);
e.printStackTrace();
}
registerPlugins(pluginFile, properties);
registerEncoders(pluginFile, properties);
registerEvents(properties);
}
public void registerPlugins(File pluginFile, Properties properties){
Enumeration plugins = null;
String factoryName = null;
try {
factoryName = trimWhitespace(properties.getProperty("netscape.plugin.composer.Factory",
"netscape.plugin.composer.Factory"));
Factory factory = (Factory) Class.forName(factoryName).newInstance();
plugins = factory.getPlugins(pluginFile, properties);
} catch ( Throwable t ) {
System.err.println("Caught exception while instantiating " + factoryName);
t.printStackTrace();
}
registerPlugins(categories, plugins);
}
public static Enumeration instantiateClassList(String classNames){
Vector result = new Vector();
if ( classNames != null ) {
StringTokenizer tokenizer = new StringTokenizer(classNames, ":");
while( tokenizer.hasMoreTokens() ){
String className = PluginManager.trimWhitespace(tokenizer.nextToken());
try {
Object object = Class.forName(className).newInstance();
result.addElement(object);
} catch (Throwable t) {
System.err.println("Caught exception while instantiating " + className);
t.printStackTrace();
}
}
}
return result.elements();
}
protected void registerEvents(Properties properties){
String eventHandlers =
properties.getProperty("netscape.plugin.composer.eventHandlers", "");
// System.err.println("registerEvents " + eventHandlers);
registerPlugins(events, eventHandlers);
}
protected static void registerPlugins(SortedStringTable table, String classNames) {
registerPlugins(table, instantiateClassList(classNames));
}
protected static void registerPlugins(SortedStringTable table, Enumeration plugins){
if ( plugins != null ) {
while(plugins.hasMoreElements()){
Plugin plugin = (Plugin) plugins.nextElement();
if ( plugin != null ) {
try {
table.getOrCreateTable(plugin.getCategory()).put(
plugin.getName(), plugin);
} catch(Throwable t){
System.err.println("Trouble registering plugin:" + plugin);
t.printStackTrace();
}
}
}
}
}
public static String trimWhitespace(String string){
int length = string.length();
StringBuffer out = new StringBuffer(length);
for(int i = 0; i < length; i++ ){
char c = string.charAt(i);
if ( !Character.isSpace(c) ) {
out.append(c);
}
}
return out.toString();
}
public void registerEncoders(File pluginFile, Properties properties){
Enumeration encoderList = null;
String factoryName = null;
try {
factoryName = trimWhitespace(properties.getProperty("netscape.plugin.composer.ImageEncoderFactory",
"netscape.plugin.composer.ImageEncoderFactory"));
ImageEncoderFactory factory = (ImageEncoderFactory) Class.forName(factoryName).newInstance();
encoderList = factory.getImageEncoders(pluginFile, properties);
} catch ( Throwable t ) {
System.err.println("Caught exception while instantiating " + factoryName);
t.printStackTrace();
}
if ( encoderList != null ) {
while(encoderList.hasMoreElements()){
Object object = null;
try {
object = encoderList.nextElement();
ImageEncoder encoder = (ImageEncoder) object;
registerEncoderInstance(encoder);
} catch ( Throwable t ) {
System.err.println("Caught exception while processing "
+ object);
t.printStackTrace();
}
}
}
}
private void registerEncoderInstance(ImageEncoder encoder){
ImageEncoder.register(encoder);
String name = encoder.getName();
encoders.put(name, encoder);
}
public int getNumberOfCategories(){
return categories.length();
}
public int getNumberOfPlugins(int category){
SortedStringTable categoryTable = (SortedStringTable) categories.get(category);
if ( categoryTable == null ) {
return 0;
}
return categoryTable.length();
}
public String getCategoryName(int category){
return categories.getKey(category);
}
public String getPluginName(int type, int index){
return getPlugin(type, index).getName();
}
public String getPluginHint(int type, int index){
return getPlugin(type, index).getHint();
}
/** Start a plugin operation.
*/
public boolean performPlugin(Composer composer, int category, int index,
String in, String base, String workDirectory, String workDirectoryURL,
JSObject jsobject){
return performPlugin2(composer, getPlugin(category, index), in, base,
workDirectory, workDirectoryURL, null, null,jsobject);
}
public boolean performPluginByName(Composer composer, String pluginName,
String in, String base, String workDirectory, String workDirectoryURL,
JSObject jsobject){
Plugin plugin = null;
String destinationURL = null;
String eventName = null;
try {
int eventMarker = pluginName.indexOf(':');
if ( eventMarker >= 0 ) {
eventName = pluginName.substring(0,eventMarker);
destinationURL = pluginName.substring(eventMarker + 1);
if ( destinationURL.length() == 0 ) {
destinationURL = null;
}
SortedStringTable table = events;
// System.err.println("Executing plug-ins for event " + eventName + " " + table);
if ( table != null ) {
plugin = new GroupPlugin(table);
}
}
else {
plugin = (Plugin) Class.forName(pluginName).newInstance();
}
if ( plugin != null ) {
return performPlugin2(composer, plugin, in, base,
workDirectory, workDirectoryURL, eventName, destinationURL, jsobject);
}
}
catch(Throwable t){
t.printStackTrace();
}
return false;
}
public boolean performPlugin2(Composer composer, Plugin plugin,
String in, String base, String workDirectory, String workDirectoryURL,
String eventName, String destinationURL, JSObject jsobject){
String result = in;
if ( plugin != null ){
try {
URL baseURL = base != null ? new URL(base) : null;
URL workDirectoryURLURL = null;
URL destinationURLURL = null;
if ( workDirectoryURL != null ) {
String slash = "/";
// The workDirectoryURL needs to end in a "/" so that
// relative URLs work.
if ( ! workDirectoryURL.endsWith(slash) ){
workDirectoryURL = workDirectoryURL + slash;
}
// Netscape on Windows likes urls of the form
// file:///C|/temp/
// But this freaks out the URL parser in java.
// Try to work around this by matching the file:
// part.
String prefix = "file:";
if ( workDirectoryURL.indexOf(prefix) == 0 ){
workDirectoryURLURL = new URL("file","",
workDirectoryURL.substring(prefix.length()));
}
else {
workDirectoryURLURL = new URL(workDirectoryURL);
}
//System.err.println("Source: " + workDirectoryURL);
//System.err.println("Simple result: " + workDirectoryURLURL );
//System.err.println("URL(file,,///C|/temp) = "+ new URL("file","","///C|/temp"));
//System.err.println("Relative URL: " + new URL(workDirectoryURLURL, "Floop.jpg"));
}
if ( destinationURL != null ) {
// Netscape on Windows likes urls of the form
// file:///C|/temp/
// But this freaks out the URL parser in java.
// Try to work around this by matching the file:
// part.
String prefix = "file:";
if ( destinationURL.indexOf(prefix) == 0 ){
destinationURLURL = new URL("file","",
destinationURL.substring(prefix.length()));
}
else {
destinationURLURL = new URL(destinationURL);
}
}
ComposerDocument document = new ComposerDocument(composer, in, baseURL,
workDirectory, workDirectoryURLURL, eventName, destinationURLURL,
jsobject);
SecurityManager.enablePrivilege("SuperUser");
ThreadGroup threadGroup = new ThreadGroup(
Thread.currentThread().getThreadGroup(),
plugin.getName());
Thread thread = new Thread(threadGroup, new PluginRunner(plugin, document, this),
plugin.getName());
thread.start();
pluginThreads.put(composer, threadGroup);
} catch (IOException e) {
System.err.println("Composer plugin runner threw this exception:");
e.printStackTrace();
return false;
}
}
else {
return false;
}
return true;
}
/** Stop a plugin operation
*/
public void stopPlugin(Composer composer){
killGroup(composer);
}
public int getNumberOfEncoders(){
return encoders.length();
}
public String getEncoderName(int index){
return getEncoder(index).getName();
}
public String getEncoderFileType(int index){
byte[] fileType = new byte[4];
getEncoder(index).getFileType(fileType);
return new String(fileType, 0);
}
public boolean getEncoderNeedsQuantizedSource(int index){
return getEncoder(index).needsQuantizedSource();
}
public String getEncoderFileExtension(int index){
return getEncoder(index).getFileExtension();
}
public String getEncoderHint(int index){
return getEncoder(index).getHint();
}
protected ImageEncoder getEncoder(int index) {
return (ImageEncoder) encoders.get(index);
}
/** Start an encoder operation.
*/
public boolean startEncoder(Composer composer, int index,
int width, int height, byte[][] pixels,
String fileName){
Plugin encoderPlugin = new ImageEncoderPlugin(getEncoder(index),
width, height, pixels, fileName);
return performPlugin2(composer, encoderPlugin, null, null, null, null, null, null,null);
}
protected Plugin getPlugin(int category, int index){
Plugin result = null;
SortedStringTable categoryTable = (SortedStringTable) categories.get(category);
if ( categoryTable != null ) {
result = (Plugin) categoryTable.get(index);
}
return result;
}
void pluginFinished(Composer composer, int status, Object argument){
composer.pluginFinished(status, argument);
killGroup(composer);
}
void killGroup(Composer composer){
ThreadGroup group = (ThreadGroup) pluginThreads.remove(composer);
if ( group != null ) {
pluginKiller.kill(group);
}
}
private SortedStringTable categories = new SortedStringTable();
private SortedStringTable encoders = new SortedStringTable();
private SortedStringTable events = new SortedStringTable();
private Hashtable pluginThreads = new Hashtable(); // Composer composer, Thread
private PluginKiller pluginKiller = new PluginKiller();
}
class PluginRunner implements Runnable {
public PluginRunner(Plugin plugin, ComposerDocument document,
PluginManager manager){
this.plugin = plugin;
this.document = document;
this.manager = manager;
}
public void run() {
Composer composer = document.getComposer();
// Take this out someday when security manager works better.
// SecurityManager.enablePrivilege("SuperUser");
try {
boolean result = plugin.perform(document);
manager.pluginFinished(composer, result ? Composer.PLUGIN_OK : Composer.PLUGIN_CANCEL, null);
} catch( ThreadDeath t) {
System.err.println("Composer plugin " + plugin + " was killed.");
} catch (Throwable t){
System.err.println("Composer plugin " + plugin + " threw this exception:");
t.printStackTrace();
manager.pluginFinished(composer, Composer.PLUGIN_FAIL, t.toString());
}
}
private Plugin plugin;
private ComposerDocument document;
private PluginManager manager;
}
class ImageEncoderPlugin extends Plugin {
public ImageEncoderPlugin(ImageEncoder encoder,
int width, int height, byte[][] pixels,
String fileName) {
this.encoder = encoder;
this.width = width;
this.height = height;
this.pixels = pixels;
this.fileName = fileName;
}
public boolean perform(Document document) throws IOException {
// Create the image.
int length = width * height;
int[] pixels2 = new int[length];
int i = 0;
for(int y = 0; y < height; y++) {
int j = 0;
byte[] line = pixels[y];
for(int x = 0; x < width; x++ ) {
pixels2[i++] = (0xff << 24)
| ((0xff & line[j]) << 16)
| ((0xff & line[j+1]) << 8)
| ((0xff & line[j+2]));
j += 3;
}
}
ImageProducer source = new MemoryImageSource(width, height, pixels2, 0, width);
ByteArrayOutputStream temp = new ByteArrayOutputStream(width*height/2);
boolean result = encoder.encode(source, temp);
if ( result ) {
// Create the output
SecurityManager.enablePrivilege("SuperUser");
OutputStream output = new FileOutputStream(new File(fileName));
output.write(temp.toByteArray());
output.close();
}
return result;
}
private ImageEncoder encoder;
private int width;
private int height;
private byte[][] pixels;
private String fileName;
}
/** Processes a sequence of plugins.
*/
class GroupPlugin extends Plugin {
public GroupPlugin(SortedStringTable table){
table_ = table;
}
public boolean perform(Document document) throws IOException {
// Perform each of the plug-ins in order
boolean result = true;
// System.err.println("GroupPlugin ready to go! " + table_);
if ( table_ != null ) {
Enumeration categories = table_.values();
while ( categories.hasMoreElements() && result ){
try {
SortedStringTable category =
(SortedStringTable) categories.nextElement();
Enumeration plugins = category.values();
while ( plugins.hasMoreElements() && result ){
Plugin plugin = null;
try {
plugin = (Plugin) plugins.nextElement();
result = plugin.perform(document);
}
catch(Throwable t){
System.err.print("Exception while executing " + plugin);
t.printStackTrace();
result = false;
}
}
}
catch(Throwable t){
t.printStackTrace();
result = false;
}
}
}
return result;
}
SortedStringTable table_;
}
class PluginKiller implements Runnable {
PluginKiller() {
list = new Vector();
// Temporary security work-around for pre-4.0PR3
SecurityManager.enablePrivilege("SuperUser");
Thread killer = new Thread(this, "Composer Plug-in Killer");
killer.start();
}
synchronized void kill(ThreadGroup group) {
list.addElement(group);
notifyAll();
}
synchronized ThreadGroup getPluginThreadGroup() {
while (list.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
}
}
int last = list.size() - 1;
ThreadGroup threadGroup = (ThreadGroup) list.elementAt(last);
list.removeElementAt(last);
return threadGroup;
}
public void run() {
while (true) {
// this thread cannot die for any reason.
// Catch any exception and keep killing
try {
ThreadGroup group = getPluginThreadGroup();
synchronized (group) {
//System.err.println("killing threadgroup " + group);
// Give the thread some time to exit. If it exits
// sooner then we will be notified when the group runs
// out of threads.
try {
group.wait(300);
} catch (InterruptedException e) {
}
// Check and see if thread is really gone
if (group.activeCount() > 0) {
// Gun down the remaining threads.
try {
SecurityManager.enablePrivilege("SuperUser");
group.stop();
group.wait(5000);
} catch (InterruptedException e) {
}
finally {
SecurityManager.revertPrivilege();
}
}
// Finally, destroy the thread group
try {
SecurityManager.enablePrivilege("SuperUser");
group.destroy();
} catch (Exception e) {
}
finally {
SecurityManager.revertPrivilege();
}
}
} catch (Exception e) {
System.out.println("Exception occurred while deleting Composer Plug-in thread group: ");
e.printStackTrace();
}
}
}
public String toString() {
return new String("Plugin thread killer! Composer plugins to be disposed of: " + list);
}
// the list of composer plug-in groups to finish
private Vector list; // Of ThreadGroup
}