001    /*
002     * Copyright 2006 Stephen McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.station;
020    
021    import dpml.lang.DOM3DocumentBuilder;
022    import dpml.util.SimpleResolver;
023    import dpml.util.DefaultLogger;
024    
025    import java.io.IOException;
026    import java.lang.management.ManagementFactory;
027    import java.net.URI;
028    import java.net.URL;
029    import java.net.URLConnection;
030    import java.util.Hashtable;
031    import java.util.ArrayList;
032    import java.util.List;
033    
034    import javax.management.MBeanServer;
035    import javax.management.ObjectName;
036    
037    import net.dpml.transit.Artifact;
038    import net.dpml.transit.ContentHandler;
039    import net.dpml.lang.DecodingException;
040    import net.dpml.util.Logger;
041    import net.dpml.util.Resolver;
042    
043    import net.dpml.appliance.Appliance;
044    import net.dpml.appliance.ApplianceManager;
045    import net.dpml.appliance.ApplianceContentManager;
046    import net.dpml.appliance.ApplianceFactory;
047    
048    import dpml.station.info.ApplianceDescriptor;
049    
050    import org.w3c.dom.Element;
051    import org.w3c.dom.Document;
052    import org.w3c.dom.TypeInfo;
053    
054    /**
055     * Content handler for the <tt>appliance</tt> artifact type.
056     *
057     * @author <a href="http://www.dpml.net">Digital Product Management Library</a>
058     * @version 2.1.0
059     */
060    public class ApplianceContentHandler extends ContentHandler implements ApplianceContentManager, ApplianceFactory
061    {
062        static final String NAMESPACE = "dpml:station";
063        static final String TYPE = "appliance";
064        
065        private static final Logger LOGGER = new DefaultLogger( "dpml.metro.appliance" );
066        private static final DOM3DocumentBuilder DOCUMENT_BUILDER = 
067          new DOM3DocumentBuilder();
068          
069        private static final List<ApplianceManager> MANAGERS = 
070          new ArrayList<ApplianceManager>();
071          
072        private static final ApplianceContentHandler SINGLETON = newApplianceContentHandler();
073        
074        private static ApplianceContentHandler newApplianceContentHandler()
075        {
076            ApplianceContentHandler handler = new ApplianceContentHandler();
077            String flag = System.getProperty( "dpml.jmx.enabled", "false" );
078            if( "true".equals( flag ) )
079            {
080                try
081                {
082                    MBeanServer server = ManagementFactory.getPlatformMBeanServer();
083                    Hashtable<String,String> table = new Hashtable<String,String>();
084                    table.put( "type", "Appliances" );
085                    ObjectName name =
086                      ObjectName.getInstance( "net.dpml.transit", table );
087                    server.registerMBean( handler, name );
088                }
089                catch( Exception e )
090                {
091                    e.printStackTrace();
092                }
093            }
094            return handler;
095        }
096        
097       /**
098        * Creation of a new applicance content handler.
099        */
100        public ApplianceContentHandler()
101        {
102            this( LOGGER );
103        }
104        
105       /**
106        * Creation of a new applicance content handler.
107        * @param logger the assigned logging channel
108        */
109        protected ApplianceContentHandler( Logger logger )
110        {
111            logger.debug( "instantiating" );
112        }
113        
114        /*
115        protected MBeanServerConnection getMBeanServerConnection() throws MalformedURLException, IOException
116        {
117            String spec = System.getProperty( Station.STATION_JMX_URI_KEY );
118            if( null != spec )
119            {
120                JMXServiceURL url = new JMXServiceURL( spec );
121                JMXConnector connector = JMXConnectorFactory.connect( url, null ); 
122                return connector.getMBeanServerConnection();
123            }
124            else
125            {
126                return ManagementFactory.getPlatformMBeanServer();
127            }
128        }
129        */
130        
131       /**
132        * Return the set of applicance managers established by the handler.
133        * @return the appliance managers
134        */
135        public ApplianceManager[] getApplianceManagers()
136        {
137            return MANAGERS.toArray( new ApplianceManager[0] );
138        }
139        
140       /**
141        * Returns the type thar the content handler supports.
142        * @return the content type name
143        */
144        public String getType()
145        {
146            return TYPE;
147        }
148        
149       /**
150        * Returns the content in the form of a {@link net.dpml.appliance.Appliance}.
151        * @param connection the url connection
152        * @return the application handler
153        * @exception IOException if an IO error occurs
154        */
155        public Object getContent( URLConnection connection ) throws IOException
156        {
157            return newAppliance( connection, null );
158        }
159        
160       /**
161        * Returns the content assignable to the first recognized class in the list
162        * of suppied classes.  If the class array is empty the application handler is returned.
163        * If none of the classes are recognized, null is returned.
164        * @param connection the url connection
165        * @param classes the selection class array
166        * @return the resolved instance
167        * @exception IOException if an IO error occurs
168        */
169        public Object getContent( URLConnection connection, Class[] classes ) throws IOException
170        {
171            ApplianceDescriptor descriptor = getApplianceDescriptor( connection );
172            return getContentForClasses( descriptor, classes );
173        }
174        
175       /**
176        * Create a new appliance using the supplied connection object.
177        * @param connection the URL connection
178        * @param partition an optional partition name
179        * @return the appliance
180        * @exception IOException if an IO error occurs
181        */
182        public Appliance newAppliance( URLConnection connection, String partition ) throws IOException
183        {
184            ApplianceDescriptor descriptor = getApplianceDescriptor( connection );
185            return newAppliance( partition, descriptor );
186        }
187    
188        static <T>T getContentForClass( URLConnection connection, Class<T> type ) throws IOException
189        {
190            ApplianceDescriptor descriptor = getApplianceDescriptor( connection );
191            return getContentForClass( descriptor, type );
192        }
193        
194        private static Object getContentForClasses( ApplianceDescriptor descriptor, Class<?>[] classes ) throws IOException
195        {
196            for( Class<?> c : classes )
197            {
198                Object value = getContentForClass( descriptor, c );
199                if( null != value )
200                {
201                    return value;
202                }
203            }
204            return null;
205        }
206        
207        private static <T>T getContentForClass( ApplianceDescriptor descriptor, Class<T> type ) throws IOException
208        {
209            if( ApplianceDescriptor.class == type )
210            {
211                return type.cast( descriptor );
212            }
213            else if( Appliance.class == type )
214            {
215                String key = "" + System.identityHashCode( descriptor );
216                Appliance appliance = newAppliance( key, descriptor );
217                return type.cast( appliance );
218            }
219            else
220            {
221                return null;
222            }
223        }
224        
225        static final Appliance newAppliance( String key, ApplianceDescriptor descriptor ) throws IOException
226        {
227            if( null == key )
228            {
229                throw new NullPointerException( "key" );
230            }
231            Logger logger = getLogger();
232            Application application = new Application( logger, key, descriptor );
233            Appliance appliance = application.getAppliance();
234            register( key, appliance );
235            return appliance;
236        }
237        
238        static void register( String id, Appliance appliance )
239        {
240            if( appliance instanceof ApplianceManager )
241            {
242                if( null == id )
243                {
244                    throw new NullPointerException( "id" );
245                }
246                ApplianceManager manager = (ApplianceManager) appliance;
247                if( !MANAGERS.contains( manager ) )
248                {
249                    try
250                    {
251                        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
252                        Hashtable<String, String> table = new Hashtable<String, String>();
253                        table.put( "type", "Appliances" );
254                        table.put( "name", id );
255                        ObjectName name =
256                          ObjectName.getInstance( "net.dpml.transit", table );
257                        server.registerMBean( manager, name );
258                        MANAGERS.add( manager );
259                    }
260                    catch( Exception e )
261                    {
262                        e.printStackTrace();
263                    }
264                }
265            }
266        }
267        
268        static void deregister( Appliance appliance )
269        {
270            if( appliance instanceof ApplianceManager )
271            {
272                ApplianceManager manager = (ApplianceManager) appliance;
273                if( MANAGERS.contains( manager ) )
274                {
275                    MANAGERS.remove( manager );
276                }
277            }
278        }
279        
280        static Appliance newAppliance( String key, URI uri ) throws IOException
281        {
282            URL url = Artifact.toURL( uri );
283            URLConnection connection = url.openConnection();
284            ApplianceDescriptor descriptor = getApplianceDescriptor( connection );
285            return newAppliance( key, descriptor );
286        }
287        
288        //static final Appliance newLocalAppliance( String partition, URI codebase ) throws IOException
289        //{
290        //    return new StandardAppliance( LOGGER, partition, codebase );
291        //}
292        
293        private static ApplianceDescriptor getApplianceDescriptor( URLConnection connection ) throws IOException
294        {
295            URL url = connection.getURL();
296            try
297            {
298                Document document = DOCUMENT_BUILDER.parse( url );
299                final Element element = document.getDocumentElement();
300                TypeInfo type = element.getSchemaTypeInfo();
301                String namespace = type.getTypeNamespace();
302                if( NAMESPACE.equals( namespace ) )
303                {
304                    URI uri = URI.create( url.toExternalForm() );
305                    Resolver resolver = new SimpleResolver();
306                    return new ApplianceDescriptor( element, resolver, uri );
307                }
308                else
309                {
310                    final String error = 
311                      "ApplianceDescriptor namespace not recognized."
312                      + "\nFound: " + namespace
313                      + "\nExpecting: " + NAMESPACE;
314                    throw new DecodingException( error, element );
315                }
316            }
317            catch( IOException e )
318            {
319                throw e;
320            }
321            catch( Exception e )
322            {
323                final String error = "Unexpected error while constructing application profile: " + url;
324                IOException ioe = new IOException();
325                ioe.initCause( e );
326                throw ioe;
327            }
328        }
329        
330        private static Logger getLogger()
331        {
332            return LOGGER;
333        }
334    }