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