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 Library</a>
058 * @version 2.1.0
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 }