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