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