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 }