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