001    /*
002     * Copyright 2005 Stephen J. 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.server; 
020    
021    import java.net.URI;
022    import java.net.URL;
023    import java.rmi.registry.Registry;
024    
025    import javax.management.NotCompliantMBeanException;
026    
027    import net.dpml.cli.Option;
028    import net.dpml.cli.Group;
029    import net.dpml.cli.CommandLine;
030    import net.dpml.cli.OptionException;
031    import net.dpml.cli.commandline.Parser;
032    import net.dpml.cli.builder.ArgumentBuilder;
033    import net.dpml.cli.builder.GroupBuilder;
034    import net.dpml.cli.builder.DefaultOptionBuilder;
035    import net.dpml.cli.option.PropertyOption;
036    import net.dpml.cli.validation.URIValidator;
037    import net.dpml.cli.validation.NumberValidator;
038    
039    import net.dpml.station.ApplicationRegistry;
040    
041    import net.dpml.transit.Artifact;
042    import net.dpml.transit.model.TransitModel;
043    import net.dpml.transit.management.TransitController;
044    
045    import net.dpml.util.Logger;
046    
047    /**
048     * The RemoteStation is responsible for the establishment of 
049     * callback monitors to external processes established by the 
050     * station manager.
051     *
052     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
053     * @version 1.2.0
054     */
055    public class StationServerPlugin implements Runnable
056    {
057        private final Logger m_logger;
058        private final CommandLine m_line;
059        private final TransitModel m_model;
060        
061        private RemoteStation m_station;
062        
063       /**
064        * Creation of a new station server plugin for station commandline
065        * handling.
066        * @param logger the assigned logging channel
067        * @param model the transit model
068        * @param args the command line arguments array
069        * @exception Exception if an error occurs
070        */
071        public StationServerPlugin( Logger logger, TransitModel model, String[] args ) throws Exception
072        {
073            m_logger = logger;
074            m_model = model;
075            
076            // parse the command group model
077            
078            Parser parser = new Parser();
079            parser.setGroup( COMMAND_GROUP );
080            try
081            {
082                m_line = parser.parse( args );
083            }
084            catch( OptionException e )
085            {
086                final String message = "Server commandline processing error.";
087                StringBuffer buffer = new StringBuffer( message );
088                buffer.append( "\nArgument count: " + args.length );
089                for( int i=0; i<args.length; i++ )
090                {
091                    buffer.append( "\n  arg (" + i + "): [" + args[i] + "]" );
092                }
093                String error = buffer.toString();
094                throw new Exception( error, e );
095            }
096        }
097        
098       /**
099        * Start the thread.
100        */
101        public void run()
102        {
103            try
104            {
105                // register MBeans
106                
107                String jmxEnabled = System.getProperty( "dpml.transit.jmx.enabled" );
108                if( "true".equals( jmxEnabled ) )
109                {
110                    try
111                    {
112                        new TransitController( m_logger, m_model );
113                        m_logger.info( "Transit controller established." );
114                    }
115                    catch( NotCompliantMBeanException ncdfe )
116                    {
117                        if( m_logger.isWarnEnabled() )
118                        {
119                            m_logger.warn( "JMX support requires SE6." );
120                        }
121                    }
122                    catch( NoClassDefFoundError ncdfe )
123                    {
124                        if( m_logger.isWarnEnabled() )
125                        {
126                            m_logger.warn( "JMX resources unavailable." );
127                        }
128                    }
129                    catch( ClassNotFoundException cnfe )
130                    {
131                        if( m_logger.isWarnEnabled() )
132                        {
133                            m_logger.warn( "JMX resources unavailable." );
134                        }
135                    }
136                }
137                
138                int port = getPortValue( m_line, Registry.REGISTRY_PORT );
139                URI uri = (URI) m_line.getValue( URI_OPTION, null );
140                if( null == uri )
141                {
142                    URL url = ApplicationRegistry.DEFAULT_STORAGE_URI.toURL();
143                    m_logger.info( "starting station on port: " + port );
144                    m_station = new RemoteStation( m_logger, m_model, port, url );
145                    Thread.currentThread().setContextClassLoader( ApplicationRegistry.class.getClassLoader() );
146                    setShutdownHook( m_station );
147                }
148                else
149                {
150                    if( Artifact.isRecognized( uri ) )
151                    {
152                        URL url = Artifact.createArtifact( uri ).toURL();
153                        m_logger.info( 
154                        "starting station on port: " 
155                        + port 
156                        + " with registry " 
157                        + url );
158                        m_station = new RemoteStation( m_logger, m_model, port, url );
159                    }
160                    else
161                    {
162                        URL url = uri.toURL();
163                        m_logger.info( 
164                          "starting station on port: " 
165                          + port 
166                          + " with registry " 
167                          + url );
168                        m_station = new RemoteStation( m_logger, m_model, port, url );
169                    }
170                }
171            }
172            catch( Exception e )
173            {
174                final String error = 
175                  "Station startup failure.";
176                m_logger.error( error, e );
177            }
178        }
179        
180        private int getPortValue( CommandLine line, int defaultPort )
181        {
182            if( line.hasOption( PORT_OPTION ) )
183            {
184                Number number = (Number) line.getValue( PORT_OPTION, null );
185                return number.intValue();
186            }
187            else
188            {
189                return defaultPort;
190            }
191        }
192        
193        private URI getRegistryURI( CommandLine line, URI uri )
194        {
195            if( line.hasOption( URI_OPTION ) )
196            {
197                return (URI) line.getValue( URI_OPTION, null );
198            }
199            else
200            {
201                return uri;
202            }
203        }
204    
205       /**
206        * Create a shutdown hook that will trigger shutdown of the station.
207        * @param station the station
208        */
209        public static void setShutdownHook( final RemoteStation station )
210        {
211            Runtime.getRuntime().addShutdownHook( new ShutdownHandler( station ) );
212        }
213        
214        private static final DefaultOptionBuilder OPTION_BUILDER = new DefaultOptionBuilder();
215        private static final ArgumentBuilder ARGUMENT_BUILDER = new ArgumentBuilder();
216        private static final GroupBuilder GROUP_BUILDER = new GroupBuilder();
217    
218        private static final PropertyOption PROPERTY_OPTION = new PropertyOption();
219        private static final NumberValidator NUMBER_VALIDATOR = NumberValidator.getIntegerInstance();
220          
221        private static final Option PORT_OPTION = 
222          ARGUMENT_BUILDER 
223            .withDescription( "Port number." )
224            .withName( "port" )
225            .withMinimum( 0 )
226            .withMaximum( 1 )
227            .withValidator( NUMBER_VALIDATOR )
228            .create();
229            
230        private static final Option URI_OPTION = 
231            OPTION_BUILDER
232              .withShortName( "registry" )
233              .withDescription( "Application registry store." )
234              .withRequired( false )
235              .withArgument(
236                ARGUMENT_BUILDER 
237                  .withDescription( "Local or remote artifact reference." )
238                  .withName( "artifact" )
239                  .withMinimum( 1 )
240                  .withMaximum( 1 )
241                  .withValidator( new URIValidator() )
242                  .create() )
243              .create();
244            
245        private static final Group COMMAND_GROUP =
246          GROUP_BUILDER
247            .withName( "options" )
248            .withOption( PORT_OPTION )
249            .withOption( URI_OPTION )
250            .withOption( PROPERTY_OPTION )
251            .create();
252    
253       /**
254        * Shutdown handler implementation.
255        */
256        private static class ShutdownHandler extends Thread
257        {
258            private final RemoteStation m_station;
259            
260           /**
261            * Creation of a new shutdown handler.
262            * @param station the station to shutdown
263            */
264            ShutdownHandler( final RemoteStation station )
265            {
266                m_station = station;
267            }
268            
269            public void run()
270            {
271                m_station.shutdown();
272            }
273        }
274       
275    }