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

Quick Search    Search Deep

Source code: javax/ide/extension/ExtensionRegistry.java


1   package javax.ide.extension;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   
6   import java.net.URI;
7   
8   import java.util.ArrayList;
9   import java.util.Collection;
10  import java.util.Collections;
11  import java.util.Iterator;
12  import java.util.LinkedHashMap;
13  import java.util.Map;
14  import java.util.logging.Level;
15  import java.util.logging.Logger;
16  
17  import javax.ide.Service;
18  import javax.ide.extension.spi.DefaultElementContext;
19  import javax.ide.extension.spi.DefaultHookVisitorFactory;
20  import javax.ide.extension.spi.DependencyTree;
21  import javax.ide.extension.spi.ExtensionSource;
22  import javax.ide.extension.spi.ExtensionVisitor;
23  import javax.ide.extension.spi.SAXManifestParser;
24  import javax.ide.spi.ProviderNotFoundException;
25  
26  import javax.ide.util.Version;
27  import javax.xml.parsers.ParserConfigurationException;
28  
29  import org.xml.sax.InputSource;
30  import org.xml.sax.SAXException;
31  
32  /**
33   * The extension registry provides access to information about installed
34   * extensions.
35   */
36  public abstract class ExtensionRegistry extends Service
37  {
38    private Map _extensions;
39    private ElementVisitorFactory _hookFactory;
40    
41    /**
42     * Find the {@link Extension} identified by the given <code>id</code>.
43     * 
44     * @param id the id of an extension.
45     * @return the specified extension, or null if no such extension is
46     *    registered.
47     */
48    public final Extension findExtension( String id )
49    {
50      if ( _extensions == null )
51      {
52        throw new IllegalStateException( 
53          "ExtensionRegistry has not been initialized.");
54      }
55      return (Extension) _extensions.get( id );
56    }
57  
58    /**
59     * Get a collection of registered extensions.
60     *
61     * @return a collection of all registered extensions.
62     */
63    public final Collection getExtensions()
64    {
65      return Collections.unmodifiableCollection( _extensions.values() );
66    }
67    
68    
69    /**
70     * Get the hook for the specified element name.
71     * 
72     * @param hookElement the element name of a hook to retrieve. Must not be
73     *    null.
74     * @return the hook for the specified element, or null if no such hook
75     *    is defined.
76     */
77    public ExtensionHook getHook( ElementName hookElement )
78    {
79      if ( hookElement == null )
80      {
81        throw new NullPointerException( "Null hookElement" );
82      }
83      return (ExtensionHook) _hookFactory.getVisitor( hookElement );
84    }
85    
86    /**
87     * Find all valid extension sources. A source is typically a JAR file 
88     * containing an extension manifest in its META-INF directory. However, this
89     * implementation is not limited to processing extensions bundled that way.
90     * 
91     * @return a collection of ExtensionSource instances, one for every potential
92     *    extension.
93     */
94    protected abstract Collection findAllExtensionSources();
95    
96    /**
97     * Create the initial parsing context. For the SAXManifestParser, this must
98     * be an instance of DefaultElementContext.
99     * 
100    * @return the initial parsing context.
101    */
102   protected ElementContext createInitialContext()
103   {
104     return new DefaultElementContext();
105   }
106   
107   /**
108    * Loads all extensions. <p>
109    * 
110    * This implementation obtains the collection of extension sources by calling
111    * {@link #findAllExtensionSources()}. It then refines this list to only
112    * sources which are valid (have a non-null URI).<p>
113    * 
114    * This implementation performs dependency analysis of extensions to determine
115    * the correct load order. Given multiple extensions with the same ID but
116    * different versions, it will always choose the latest version (even if that
117    * causes a dependency from another extension to be unsatisfied).<p>
118    * 
119    * When the list of extensions to load in order is determined, this
120    * implementation calls {@link #loadExtensions( Collection )}. It also calls
121    * {@link #cycleEncountered( Collection )} if any cyclic dependencies were
122    * found and {@link #unsatisfiedExtensionDependencies( Extension, Collection )}
123    * if unsatisfied dependencies were found.
124    * 
125    * @return a collection of all available extensions.
126    */
127   protected Collection loadExtensions()
128   {
129     Collection sources = findAllExtensionSources();
130     
131     Collection validSources = new ArrayList();
132     for ( Iterator i = sources.iterator(); i.hasNext(); )
133     {
134       ExtensionSource source = (ExtensionSource) i.next();
135       if ( source.getManifestURI() != null )
136       {
137         validSources.add( source );
138       }
139     }
140 
141     DependencyTree dt = DependencyTree.buildTree( validSources, 
142       new DependencyTree.EnabledExtensionLookup() {
143         public boolean isExtensionEnabled( Extension e )
144         {
145           return ExtensionRegistry.this.isExtensionEnabled( e.getID(), e.getVersion() );
146         }
147       }, createExtensionLogger(), (DefaultElementContext)createInitialContext()
148     );
149     
150     ArrayList orderedSources = new ArrayList();
151     for ( Iterator i = dt.getSortedExtensionIDs().iterator(); i.hasNext(); )
152     {
153       String id = (String) i.next();
154       orderedSources.add( dt.getSource( id ) );
155     }
156     
157     Collection extensions = loadExtensions( orderedSources );
158     
159     for ( Iterator i = dt.getCycles().iterator(); i.hasNext(); )
160     {
161       cycleEncountered( (Collection) i.next() );
162     }
163     
164     for ( Iterator i = dt.getUnsatisfiedExtensions().iterator(); i.hasNext(); )
165     {
166       Extension unsatisfied = (Extension) i.next();
167       Collection deps = dt.getUnsatisfiedDependencies( unsatisfied );
168       unsatisfiedExtensionDependencies( unsatisfied, deps );
169     }
170 
171     return extensions;
172   }
173   
174   /**
175    * Gets whether the specified extension is enabled in this IDE. <p>
176    * 
177    * This implementation always returns true.
178    * 
179    * @param id the id of an extension
180    * @param version the version of an extension
181    * @return true if the specified extension is enabled in this IDE.
182    */
183   protected boolean isExtensionEnabled( String id, Version version )
184   {
185     return true;
186   }
187   
188   /**
189    * A cycle was encountered. This is called after all extensions have been loaded to 
190    * notify the extension registry that there were cyclic dependencies between
191    * extensions. The extension loading implementation in this class will 
192    * load extensions even if there are cycles. cycleEncountered() is called
193    * to give IDEs a chance to report cycles to users.<p>
194    * 
195    * The returned collection contains partially populated Extension instances in
196    * dependency order. The last extension in the collection is a duplicate of 
197    * one of the previous extensions in the collection and is the first extension
198    * on which a cycle was detected.<p>
199    * 
200    * This implementation does nothing.
201    *
202    * @param cycle a collection of Extensions that have a cyclic dependency.
203    */
204   protected void cycleEncountered( Collection cycle )
205   {
206     
207   }
208   
209   /**
210    * An extension with unsatisfied dependencies was encountered. This is called
211    * after all extensions have been loaded to notify the extension registry that
212    * there were extensions which had unsatisfied dependencies. The extension
213    * loading implementation in this class will not load extensions which have
214    * unsatisfied dependencies. unsatisfiedExtensionDependencies() is called to
215    * give IDEs a chance to report unsatisfied dependencies to users.<p>
216    * 
217    * This implementation does nothing.
218    * 
219    * @param ext an extension which was not loaded due to unresolved 
220    *    dependencies on other extensions.
221    * @param deps a collection of <tt>ExtensionDependency</tt> objects, one for
222    *    each dependency that was not satisfied.
223    */
224   protected void unsatisfiedExtensionDependencies( Extension ext, 
225     Collection deps )
226   {
227     
228   }
229   
230   /**
231    * Load extensions in the specified order.<p>
232    * 
233    * This implementation uses SAXManifestParser to load each extension in 
234    * turn.
235    * 
236    * @param orderedSources a collection of ExtensionSource instances. The
237    *    iterator order of this collection is the correct order to load the
238    *    extensions based on their dependencies.
239    * @return a collection of fully loaded Extensions.
240    */
241   protected Collection loadExtensions( Collection orderedSources )
242   {
243     if ( _hookFactory == null )
244     {
245       _hookFactory = createHookVisitorFactory();
246     }
247     
248     SAXManifestParser parser = new SAXManifestParser(
249       (DefaultElementContext) createInitialContext()
250     );
251     Logger logger = createExtensionLogger();
252     ((DefaultElementContext)parser.getContext()).setMessageReporter( logger );
253     ExtensionVisitor visitor = createExtensionVisitor( _hookFactory );
254     parser.getContext().registerChildVisitor( ExtensionVisitor.ELEMENT, 
255       visitor );
256     
257     for ( Iterator i = orderedSources.iterator(); i.hasNext(); )
258     {
259       ExtensionSource source = (ExtensionSource) i.next();
260             
261       loadExtension(parser, logger, source);
262     }
263     return visitor.getExtensions();
264   }
265 
266   protected void loadExtension(SAXManifestParser parser, 
267     Logger logger, ExtensionSource source)
268   {
269     parser.getContext().getScopeData().put(
270       ExtensionVisitor.KEY_EXTENSION_SOURCE, 
271       source
272     );
273     
274     URI uri = source.getManifestURI();
275     InputStream inStream = null;
276     try
277     {
278       inStream = source.getInputStream();
279       InputSource inSource = new InputSource( inStream );
280       inSource.setSystemId( uri.toString() );
281       addToClassPath( source );
282       parser.parse( inSource );
283     }
284     catch ( IOException ioe )
285     {
286       logger.log( Level.SEVERE, "Error loading manifest from "+ source.getName(), ioe );
287     }
288     catch ( ParserConfigurationException pce )
289     {
290       throw new IllegalStateException( "Badly configured jaxb" );
291     }
292     catch ( SAXException saxe )
293     {
294       logger.log( Level.SEVERE, "Failed to parse manifest from "+source.getName(), 
295         saxe );
296     }
297     catch ( RuntimeException re )
298     {
299       logger.log( Level.SEVERE, "RuntimeException parsing manifest from "+source.getName()+": "+re );
300       re.printStackTrace();
301     }
302     finally
303     {
304       try { if ( inStream!=null) inStream.close(); } catch ( IOException ioe )
305       {
306         logger.log( Level.SEVERE, "Error closing stream for "+source.getName(), ioe );
307       }
308     }
309   }
310     
311   /**
312    * Create a logger which will log validation and error messages while
313    * parsing manifest files.<p>
314    * 
315    * This implementation returns a default logger created using the log
316    * manager.
317    * 
318    * @return a logger implementation which will log validation and error
319    *    messages.
320    */
321   protected Logger createExtensionLogger()
322   {
323     return Logger.getLogger( ExtensionRegistry.class.getName() );
324   }
325 
326   
327   /**
328    * Create the element visitor that is responsible for visiting the root
329    * element in extension manifests.
330    * 
331    * @param hookVisitorFactory the visitor factory for the hooks section
332    *    of the manifest.
333    * @return an element visitor implementation for visiting the root 
334    *    element in extension manifests. Typically, this will be a subclass
335    *    of ExtensionVisitor.
336    */
337   protected abstract ExtensionVisitor createExtensionVisitor( 
338     ElementVisitorFactory hookVisitorFactory );
339   
340   /**
341    * Add the specified extension source to the classpath, if required.
342    * 
343    * @param source the source of an extension. E.g if this is a JAR, it 
344    *    needs to be added to the classpath.
345    */
346   protected abstract void addToClassPath( ExtensionSource source );
347   
348   /**
349    * Create the visitor factory for hooks. This implementation returns a new
350    * instance of javax.ide.extension.spi.DefaultHookVisitorFactory.
351    * 
352    * @return an ElementVisitorFactory for hooks.
353    */
354   protected ElementVisitorFactory createHookVisitorFactory()
355   {
356     return new DefaultHookVisitorFactory();
357   }
358   
359   /**
360    * Initializes the extension registry.
361    * 
362    * This implementation 
363    */
364   protected void initialize()
365   {
366     Collection extensions = loadExtensions();
367     
368     _extensions = new LinkedHashMap();
369     for ( Iterator i = extensions.iterator(); i.hasNext(); )
370     {
371       Extension ext = (Extension) i.next();
372       _extensions.put( ext.getID(), ext );
373     }
374   }
375 
376   /**
377    * Get the extension registry implementation for this IDE.
378    * 
379    * @return the extension registry implementation for this ide.
380    */
381   public static ExtensionRegistry getExtensionRegistry()
382   {
383     try
384     {
385       return (ExtensionRegistry) getService( ExtensionRegistry.class );
386     }
387     catch ( ProviderNotFoundException nse )
388     {
389       nse.printStackTrace();
390       throw new IllegalStateException( "No extension registry" );
391     }
392   }
393 }