001    /*
002     * Copyright 2007 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    
026    import dpml.station.info.EntryDescriptor;
027    import dpml.station.info.PlanDescriptor;
028    
029    import java.io.IOException;
030    import java.net.URI;
031    import java.net.URL;
032    import java.net.URLConnection;
033    import java.lang.management.ManagementFactory;
034    import java.util.Hashtable;
035    
036    import javax.management.MBeanServer;
037    import javax.management.ObjectName;
038    import javax.management.MBeanException;
039    import javax.management.InstanceAlreadyExistsException;
040    
041    import net.dpml.appliance.Appliance;
042    import net.dpml.appliance.ApplianceFactory;
043    
044    import net.dpml.lang.DecodingException;
045    
046    import net.dpml.transit.Artifact;
047    import net.dpml.transit.ContentHandler;
048    
049    import net.dpml.util.Resolver;
050    import net.dpml.util.Logger;
051    
052    import org.w3c.dom.Element;
053    import org.w3c.dom.Document;
054    import org.w3c.dom.TypeInfo;
055    
056    /**
057     * Content handler for the <tt>plan</tt> artifact type.
058     *
059     * @author <a href="http://www.dpml.net">Digital Product Management Laboratory</a>
060     * @version 2.0.1
061     */
062    public class PlanContentHandler extends ApplianceContentHandler //implements ApplianceFactory
063    {
064        private static final Logger LOGGER = new DefaultLogger( "dpml.station.plan" );
065        static final String NAMESPACE = "dpml:station";
066        static final String TYPE = "plan";
067        
068        private static final DOM3DocumentBuilder DOCUMENT_BUILDER = 
069          new DOM3DocumentBuilder();
070        
071        public PlanContentHandler()
072        {
073            super( LOGGER );
074        }
075        
076       /**
077        * Returns the type thar the content handler supports.
078        * @return the content type name
079        */
080        public String getType()
081        {
082            return TYPE;
083        }
084        
085       /**
086        * Returns the content in the form of a {@link net.dpml.appliance.Appliance}.
087        * @param connection the url connection
088        * @return the application handler
089        * @exception IOException if an IO error occurs
090        */
091        public Object getContent( URLConnection connection ) throws IOException
092        {
093            return getContentForClass( connection, Appliance.class );
094        }
095        
096       /**
097        * Returns the content assignable to the first recognized class in the list
098        * of suppied classes.  If the class array is empty the application handler is returned.
099        * If none of the classes are recognized, null is returned.
100        * @param connection the url connection
101        * @param classes the selection class array
102        * @return the resolved instance
103        * @exception IOException if an IO error occurs
104        */
105        public Object getContent( URLConnection connection, Class[] classes ) throws IOException
106        {
107            PlanDescriptor descriptor = getPlanDescriptor( connection );
108            return getContentForClasses( descriptor, classes );
109        }
110        
111       /**
112        * Create a new appliance using the supplied connection object.
113        * @param connection the URL connection
114        * @param partition an optional partition name
115        * @return the appliance
116        * @exception IOException if an IO error occurs
117        */
118        public Appliance newAppliance( URLConnection connection, String partition ) throws IOException
119        {
120            PlanDescriptor descriptor = getPlanDescriptor( connection );
121            String name = descriptor.getName();
122            String path = getQualifiedName( partition, name );
123            Appliance appliance = new CompositeAppliance( LOGGER, path, descriptor );
124            register( path, appliance );
125            return appliance;
126        }
127        
128        private static String getQualifiedName( String partition, String name )
129        {
130            if( null == partition )
131            {
132                return name;
133            }
134            else
135            {
136                return partition + "." + name;
137            }
138        }
139        
140        static <T>T getContentForClass( URLConnection connection, Class<T> type ) throws IOException
141        {
142            PlanDescriptor descriptor = getPlanDescriptor( connection );
143            return getContentForClass( descriptor, type );
144        }
145        
146        private static Object getContentForClasses( 
147          PlanDescriptor descriptor, Class<?>[] classes ) throws IOException
148        {
149            for( Class<?> c : classes )
150            {
151                Object value = getContentForClass( descriptor, c );
152                if( null != value )
153                {
154                    return value;
155                }
156            }
157            return null;
158        }
159        
160        private static <T>T getContentForClass( 
161          PlanDescriptor descriptor, Class<T> type ) throws IOException
162        {
163            if( PlanDescriptor.class == type )
164            {
165                return type.cast( descriptor );
166            }
167            else if( Appliance.class == type )
168            {
169                String name = descriptor.getName();
170                CompositeAppliance appliance = new CompositeAppliance( LOGGER, name, descriptor );
171                register( name, appliance );
172                return type.cast( appliance );
173            }
174            else
175            {
176                return null;
177            }
178        }
179        
180        public static Appliance newAppliance( String key, URI uri ) throws IOException
181        {
182            URL url = Artifact.toURL( uri );
183            URLConnection connection = url.openConnection();
184            PlanDescriptor descriptor = getPlanDescriptor( connection );
185            Appliance appliance = new CompositeAppliance( LOGGER, key, descriptor );
186            register( key, appliance );
187            return appliance;
188        }
189        
190        private static PlanDescriptor getPlanDescriptor( URLConnection connection ) throws IOException
191        {
192            URL url = connection.getURL();
193            try
194            {
195                Document document = DOCUMENT_BUILDER.parse( url );
196                final Element element = document.getDocumentElement();
197                TypeInfo type = element.getSchemaTypeInfo();
198                String namespace = type.getTypeNamespace();
199                if( NAMESPACE.equals( namespace ) )
200                {
201                    URI codebase = url.toURI();
202                    Resolver resolver = new SimpleResolver();
203                    return new PlanDescriptor( element, resolver, codebase );
204                }
205                else
206                {
207                    final String error = 
208                      "Document namespace not recognized."
209                      + "\nFound: " + namespace
210                      + "\nExpecting: " + NAMESPACE;
211                    throw new DecodingException( error, element );
212                }
213            }
214            catch( IOException e )
215            {
216                throw e;
217            }
218            catch( Exception e )
219            {
220                final String error = "Unexpected error while constructing scenario: " + url;
221                IOException ioe = new IOException();
222                ioe.initCause( e );
223                throw ioe;
224            }
225        }
226    }