Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/ServiceManager.java


1   /*
2    * ServiceManager.java - Handles services.xml files in plugins
3    * :tabSize=8:indentSize=8:noTabs=false:
4    * :folding=explicit:collapseFolds=1:
5    *
6    * Copyright (C) 2003 Slava Pestov
7    *
8    * This program is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License
10   * as published by the Free Software Foundation; either version 2
11   * of the License, or any later version.
12   *
13   * This program is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   * GNU General Public License for more details.
17   *
18   * You should have received a copy of the GNU General Public License
19   * along with this program; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21   */
22  
23  package org.gjt.sp.jedit;
24  
25  import com.microstar.xml.*;
26  import java.io.*;
27  import java.net.URL;
28  import java.util.*;
29  import org.gjt.sp.util.Log;
30  
31  /**
32   * A generic way for plugins to provide various API extensions.<p>
33   * 
34   * Services are loaded from files named <code>services.xml</code> inside the
35   * plugin JAR. A service definition file has the following form:
36   *
37   * <pre>&lt;?xml version="1.0"?&gt;
38   *&lt;!DOCTYPE SERVICES SYSTEM "services.dtd"&gt;
39   *&lt;SERVICES&gt;
40   *    &lt;SERVICE NAME="service name" CLASS="fully qualified class name"&gt;
41   *        // BeanShell code evaluated when the sevice is first activated
42   *    &lt;/SERVICE&gt;
43   *&lt;/SERVICES&gt;</pre>
44   *
45   * The following elements are valid:
46   *
47   * <ul>
48   * <li>
49   * <code>SERVICES</code> is the top-level element and refers
50   * to the set of services offered by the plugin.
51   * </li>
52   * <li>
53   * A <code>SERVICE</code> contains the data for a particular service
54   * activation.
55   * It has two attributes, both required: <code>NAME</code> and
56   * <code>CLASS</code>. The <code>CLASS</code> attribute must be the name of
57   * a known sevice type; see below.
58   * </li>
59   * <li>
60   * A <code>SERVICE</code> element should the BeanShell code that returns a
61   * new instance of the named class. Note that this code can return
62   * <code>null</code>.
63   * </li>
64   * </ul>
65   *
66   * The jEdit core defines the following service types:
67   * <ul>
68   * <li>{@link org.gjt.sp.jedit.buffer.FoldHandler}</li>
69   * <li>{@link org.gjt.sp.jedit.io.VFS}</li>
70   * </ul>
71   *
72   * Plugins may provide more.<p>
73   *
74   * To have your plugin accept services, no extra steps are needed other than
75   * a piece of code somewhere that calls {@link #getServiceNames(String)} and
76   * {@link #getService(String,String)}.
77   *
78   * @see BeanShell
79   * @see PluginJAR
80   *
81   * @since jEdit 4.2pre1
82   * @author Slava Pestov
83   * @version $Id: ServiceManager.java,v 1.7 2003/05/10 00:43:19 spestov Exp $
84   */
85  public class ServiceManager
86  {
87    //{{{ loadServices() method
88    /**
89     * Loads a <code>services.xml</code> file.
90     * @since jEdit 4.2pre1
91     */
92    public static void loadServices(PluginJAR plugin, URL uri,
93      PluginJAR.PluginCacheEntry cache)
94    {
95      Reader in = null;
96  
97      try
98      {
99        Log.log(Log.DEBUG,jEdit.class,"Loading services from " + uri);
100 
101       ServiceListHandler dh = new ServiceListHandler(plugin,uri);
102       XmlParser parser = new XmlParser();
103       parser.setHandler(dh);
104       in = new BufferedReader(
105         new InputStreamReader(
106         uri.openStream()));
107       parser.parse(null, null, in);
108       if(cache != null)
109         cache.cachedServices = dh.getCachedServices();
110     }
111     catch(XmlException xe)
112     {
113       int line = xe.getLine();
114       String message = xe.getMessage();
115       Log.log(Log.ERROR,ServiceManager.class,uri + ":" + line
116         + ": " + message);
117     }
118     catch(Exception e)
119     {
120       Log.log(Log.ERROR,ServiceManager.class,e);
121     }
122     finally
123     {
124       try
125       {
126         if(in != null)
127           in.close();
128       }
129       catch(IOException io)
130       {
131         Log.log(Log.ERROR,ServiceManager.class,io);
132       }
133     }
134   } //}}}
135 
136   //{{{ unloadServices() method
137   /**
138    * Removes all services belonging to the specified plugin.
139    * @param plugin The plugin
140    * @since jEdit 4.2pre1
141    */
142   public static void unloadServices(PluginJAR plugin)
143   {
144     Iterator descriptors = serviceMap.keySet().iterator();
145     while(descriptors.hasNext())
146     {
147       Descriptor d = (Descriptor)descriptors.next();
148       if(d.plugin == plugin)
149         descriptors.remove();
150     }
151   } //}}}
152 
153   //{{{ registerService() method
154   /**
155    * Registers a service. Plugins should provide a 
156    * <code>services.xml</code> file instead of calling this directly.
157    *
158    * @param clazz The service class
159    * @param name The service name
160    * @param code BeanShell code to create an instance of this
161    * @param plugin The plugin JAR, or null if this is a built-in service
162    *
163    * @since jEdit 4.2pre1
164    */
165   public static void registerService(String clazz, String name,
166     String code, PluginJAR plugin)
167   {
168     Descriptor d = new Descriptor(clazz,name,code,plugin);
169     serviceMap.put(d,d);
170   } //}}}
171 
172   //{{{ unregisterService() method
173   /**
174    * Unregisters a service.
175    *
176    * @param clazz The service class
177    * @param name The service name
178    * @param code BeanShell code to create an instance of this
179    *
180    * @since jEdit 4.2pre1
181    */
182   public static void unregisterService(String clazz, String name)
183   {
184     Descriptor d = new Descriptor(clazz,name);
185     serviceMap.remove(d);
186   } //}}}
187 
188   //{{{ getServiceTypes() method
189   /**
190    * Returns all known service class types.
191    *
192    * @since jEdit 4.2pre1
193    */
194   public static String[] getServiceTypes()
195   {
196     HashSet returnValue = new HashSet();
197 
198     Iterator descriptors = serviceMap.keySet().iterator();
199     while(descriptors.hasNext())
200     {
201       Descriptor d = (Descriptor)descriptors.next();
202       returnValue.add(d.clazz);
203     }
204 
205     return (String[])returnValue.toArray(
206       new String[returnValue.size()]);
207   } //}}}
208 
209   //{{{ getServiceNames() method
210   /**
211    * Returns the names of all registered services with the given
212    * class. For example, calling this with a parameter of
213    * "org.gjt.sp.jedit.io.VFS" returns all known virtual file
214    * systems.
215    *
216    * @param clazz The class name
217    * @since jEdit 4.2pre1
218    */
219   public static String[] getServiceNames(String clazz)
220   {
221     ArrayList returnValue = new ArrayList();
222 
223     Iterator descriptors = serviceMap.keySet().iterator();
224     while(descriptors.hasNext())
225     {
226       Descriptor d = (Descriptor)descriptors.next();
227       if(d.clazz.equals(clazz))
228         returnValue.add(d.name);
229     }
230 
231     return (String[])returnValue.toArray(
232       new String[returnValue.size()]);
233   } //}}}
234 
235   //{{{ getService() method
236   /**
237    * Returns an instance of the given service. The first time this is
238    * called for a given service, the BeanShell code is evaluated. The
239    * result is cached for future invocations, so in effect services are
240    * singletons.
241    *
242    * @param clazz The service class
243    * @param name The service name
244    * @since jEdit 4.2pre1
245    */
246   public static Object getService(String clazz, String name)
247   {
248     // they never taught you this in undergrad computer science
249     Descriptor key = new Descriptor(clazz,name);
250     Descriptor value = (Descriptor)serviceMap.get(key);
251     if(value == null)
252     {
253       // unknown service - <clazz,name> not in table
254       return null;
255     }
256     else
257     {
258       if(value.code == null)
259       {
260         loadServices(value.plugin,
261           value.plugin.getServicesURI(),
262           null);
263         value = (Descriptor)serviceMap.get(key);
264       }
265       return value.getInstance();
266     }
267   } //}}}
268 
269   //{{{ Package-private members
270 
271   //{{{ registerService() method
272   /**
273    * Registers a service.
274    *
275    * @since jEdit 4.2pre1
276    */
277   static void registerService(Descriptor d)
278   {
279     serviceMap.put(d,d);
280   } //}}}
281 
282   //}}}
283 
284   //{{{ Private members
285   private static Map serviceMap = new HashMap();
286   //}}}
287 
288   //{{{ Descriptor class
289   static class Descriptor
290   {
291     String clazz;
292     String name;
293     String code;
294     PluginJAR plugin;
295     Object instance;
296     boolean instanceIsNull;
297 
298     // this constructor keys the hash table
299     Descriptor(String clazz, String name)
300     {
301       this.clazz = clazz;
302       this.name  = name;
303     }
304 
305     // this constructor is the value of the hash table
306     Descriptor(String clazz, String name, String code,
307       PluginJAR plugin)
308     {
309       this.clazz  = clazz;
310       this.name   = name;
311       this.code   = code;
312       this.plugin = plugin;
313     }
314 
315     Object getInstance()
316     {
317       if(instanceIsNull)
318         return null;
319       else if(instance == null)
320       {
321         // lazy instantiation
322         instance = BeanShell.eval(null,
323           BeanShell.getNameSpace(),
324           code);
325         if(instance == null)
326         {
327           // avoid re-running script if it gives
328           // us null
329           instanceIsNull = true;
330         }
331       }
332 
333       return instance;
334     }
335     public int hashCode()
336     {
337       return name.hashCode();
338     }
339 
340     public boolean equals(Object o)
341     {
342       if(o instanceof Descriptor)
343       {
344         Descriptor d = (Descriptor)o;
345         return d.clazz.equals(clazz)
346           && d.name.equals(name);
347       }
348       else
349         return false;
350     }
351   } //}}}
352 }