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.1
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 }