|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* ServiceManager.java - Handles services.xml files in plugins
* :tabSize=8:indentSize=8:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2003 Slava Pestov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit;
import com.microstar.xml.*;
import java.io.*;
import java.net.URL;
import java.util.*;
import org.gjt.sp.util.Log;
/**
* A generic way for plugins to provide various API extensions.
*
* Services are loaded from files named services.xml inside the
* plugin JAR. A service definition file has the following form:
*
* <?xml version="1.0"?>
*<!DOCTYPE SERVICES SYSTEM "services.dtd">
*<SERVICES>
* <SERVICE NAME="service name" CLASS="fully qualified class name">
* // BeanShell code evaluated when the sevice is first activated
* </SERVICE>
*</SERVICES>
*
* The following elements are valid:
*
*
* -
*
SERVICES is the top-level element and refers
* to the set of services offered by the plugin.
*
* -
* A
SERVICE contains the data for a particular service
* activation.
* It has two attributes, both required: NAME and
* CLASS . The CLASS attribute must be the name of
* a known sevice type; see below.
*
* -
* A
SERVICE element should the BeanShell code that returns a
* new instance of the named class. Note that this code can return
* null .
*
*
*
* The jEdit core defines the following service types:
*
* - {@link org.gjt.sp.jedit.buffer.FoldHandler}
* - {@link org.gjt.sp.jedit.io.VFS}
*
*
* Plugins may provide more.
*
* To have your plugin accept services, no extra steps are needed other than
* a piece of code somewhere that calls {@link #getServiceNames(String)} and
* {@link #getService(String,String)}.
*
* @see BeanShell
* @see PluginJAR
*
* @since jEdit 4.2pre1
* @author Slava Pestov
* @version $Id: ServiceManager.java,v 1.8 2004/03/28 00:07:26 spestov Exp $
*/
public class ServiceManager
{
//{{{ loadServices() method
/**
* Loads a services.xml file.
* @since jEdit 4.2pre1
*/
public static void loadServices(PluginJAR plugin, URL uri,
PluginJAR.PluginCacheEntry cache)
{
Reader in = null;
try
{
Log.log(Log.DEBUG,jEdit.class,"Loading services from " + uri);
ServiceListHandler dh = new ServiceListHandler(plugin,uri);
XmlParser parser = new XmlParser();
parser.setHandler(dh);
in = new BufferedReader(
new InputStreamReader(
uri.openStream()));
parser.parse(null, null, in);
if(cache != null)
cache.cachedServices = dh.getCachedServices();
}
catch(XmlException xe)
{
int line = xe.getLine();
String message = xe.getMessage();
Log.log(Log.ERROR,ServiceManager.class,uri + ":" + line
+ ": " + message);
}
catch(Exception e)
{
Log.log(Log.ERROR,ServiceManager.class,e);
}
finally
{
try
{
if(in != null)
in.close();
}
catch(IOException io)
{
Log.log(Log.ERROR,ServiceManager.class,io);
}
}
} //}}}
//{{{ unloadServices() method
/**
* Removes all services belonging to the specified plugin.
* @param plugin The plugin
* @since jEdit 4.2pre1
*/
public static void unloadServices(PluginJAR plugin)
{
Iterator descriptors = serviceMap.keySet().iterator();
while(descriptors.hasNext())
{
Descriptor d = (Descriptor)descriptors.next();
if(d.plugin == plugin)
descriptors.remove();
}
} //}}}
//{{{ registerService() method
/**
* Registers a service. Plugins should provide a
* services.xml file instead of calling this directly.
*
* @param clazz The service class
* @param name The service name
* @param code BeanShell code to create an instance of this
* @param plugin The plugin JAR, or null if this is a built-in service
*
* @since jEdit 4.2pre1
*/
public static void registerService(String clazz, String name,
String code, PluginJAR plugin)
{
Descriptor d = new Descriptor(clazz,name,code,plugin);
serviceMap.put(d,d);
} //}}}
//{{{ unregisterService() method
/**
* Unregisters a service.
*
* @param clazz The service class
* @param name The service name
*
* @since jEdit 4.2pre1
*/
public static void unregisterService(String clazz, String name)
{
Descriptor d = new Descriptor(clazz,name);
serviceMap.remove(d);
} //}}}
//{{{ getServiceTypes() method
/**
* Returns all known service class types.
*
* @since jEdit 4.2pre1
*/
public static String[] getServiceTypes()
{
HashSet returnValue = new HashSet();
Iterator descriptors = serviceMap.keySet().iterator();
while(descriptors.hasNext())
{
Descriptor d = (Descriptor)descriptors.next();
returnValue.add(d.clazz);
}
return (String[])returnValue.toArray(
new String[returnValue.size()]);
} //}}}
//{{{ getServiceNames() method
/**
* Returns the names of all registered services with the given
* class. For example, calling this with a parameter of
* "org.gjt.sp.jedit.io.VFS" returns all known virtual file
* systems.
*
* @param clazz The class name
* @since jEdit 4.2pre1
*/
public static String[] getServiceNames(String clazz)
{
ArrayList returnValue = new ArrayList();
Iterator descriptors = serviceMap.keySet().iterator();
while(descriptors.hasNext())
{
Descriptor d = (Descriptor)descriptors.next();
if(d.clazz.equals(clazz))
returnValue.add(d.name);
}
return (String[])returnValue.toArray(
new String[returnValue.size()]);
} //}}}
//{{{ getService() method
/**
* Returns an instance of the given service. The first time this is
* called for a given service, the BeanShell code is evaluated. The
* result is cached for future invocations, so in effect services are
* singletons.
*
* @param clazz The service class
* @param name The service name
* @since jEdit 4.2pre1
*/
public static Object getService(String clazz, String name)
{
// they never taught you this in undergrad computer science
Descriptor key = new Descriptor(clazz,name);
Descriptor value = (Descriptor)serviceMap.get(key);
if(value == null)
{
// unknown service - not in table
return null;
}
else
{
if(value.code == null)
{
loadServices(value.plugin,
value.plugin.getServicesURI(),
null);
value = (Descriptor)serviceMap.get(key);
}
return value.getInstance();
}
} //}}}
//{{{ Package-private members
//{{{ registerService() method
/**
* Registers a service.
*
* @since jEdit 4.2pre1
*/
static void registerService(Descriptor d)
{
serviceMap.put(d,d);
} //}}}
//}}}
//{{{ Private members
private static Map serviceMap = new HashMap();
//}}}
//{{{ Descriptor class
static class Descriptor
{
String clazz;
String name;
String code;
PluginJAR plugin;
Object instance;
boolean instanceIsNull;
// this constructor keys the hash table
Descriptor(String clazz, String name)
{
this.clazz = clazz;
this.name = name;
}
// this constructor is the value of the hash table
Descriptor(String clazz, String name, String code,
PluginJAR plugin)
{
this.clazz = clazz;
this.name = name;
this.code = code;
this.plugin = plugin;
}
Object getInstance()
{
if(instanceIsNull)
return null;
else if(instance == null)
{
// lazy instantiation
instance = BeanShell.eval(null,
BeanShell.getNameSpace(),
code);
if(instance == null)
{
// avoid re-running script if it gives
// us null
instanceIsNull = true;
}
}
return instance;
}
public int hashCode()
{
return name.hashCode();
}
public boolean equals(Object o)
{
if(o instanceof Descriptor)
{
Descriptor d = (Descriptor)o;
return d.clazz.equals(clazz)
&& d.name.equals(name);
}
else
return false;
}
} //}}}
}
|