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