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