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.transit.console;
020    
021    import java.io.IOException;
022    import java.io.OutputStream;
023    import java.io.File;
024    import java.io.FileNotFoundException;
025    import java.lang.reflect.InvocationTargetException;
026    import java.net.URI;
027    import java.net.URL;
028    import java.util.List;
029    import java.util.Set;
030    import java.util.HashSet;
031    
032    import net.dpml.cli.Option;
033    import net.dpml.cli.Group;
034    import net.dpml.cli.CommandLine;
035    import net.dpml.cli.commandline.Parser;
036    import net.dpml.cli.util.HelpFormatter;
037    import net.dpml.cli.OptionException;
038    import net.dpml.cli.DisplaySetting;
039    import net.dpml.cli.builder.ArgumentBuilder;
040    import net.dpml.cli.builder.GroupBuilder;
041    import net.dpml.cli.builder.DefaultOptionBuilder;
042    import net.dpml.cli.builder.CommandBuilder;
043    import net.dpml.cli.validation.URIValidator;
044    import net.dpml.cli.validation.URLValidator;
045    import net.dpml.cli.validation.NumberValidator;
046    
047    import net.dpml.util.Logger;
048    import net.dpml.lang.UnknownKeyException;
049    import net.dpml.lang.ValueDirective;
050    
051    import net.dpml.lang.Part;
052    import net.dpml.lang.PartException;
053    
054    import net.dpml.transit.Artifact;
055    import net.dpml.transit.Transit;
056    import net.dpml.transit.TransitBuilder;
057    import net.dpml.transit.DefaultTransitModel;
058    import net.dpml.transit.info.TransitDirective;
059    import net.dpml.transit.info.ProxyDirective;
060    import net.dpml.transit.info.CacheDirective;
061    import net.dpml.transit.info.HostDirective;
062    import net.dpml.transit.info.LayoutDirective;
063    import net.dpml.util.ExceptionHelper;
064    
065    
066    /**
067     * Transit Plugin that provides support for the configuration of the Transit subsystem.
068     *
069     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
070     * @version 1.0.4
071     */
072    public class TransitConsoleHandler 
073    {
074        // ------------------------------------------------------------------------
075        // state
076        // ------------------------------------------------------------------------
077    
078        private final Logger m_logger;
079        private final TransitDirective m_directive;
080        private final CommandLine m_line;
081        private final TransitDirectiveBuilder m_builder;
082        
083        // ------------------------------------------------------------------------
084        // constructor
085        // ------------------------------------------------------------------------
086    
087       /**
088        * Creation of a new Transit CLI handler.
089        * 
090        * @param logger the assigned logging channel
091        * @param args command line arguments
092        * @exception Exception if an error occurs during plugin establishment
093        */
094        public TransitConsoleHandler( Logger logger, String[] args ) throws Exception
095        {
096            this( logger, getCommandLine( logger, args ) );
097        }
098        
099       /**
100        * Creation of a new Transit CLI handler.
101        *
102        * @param logger the assigned logging channel
103        * @param line the command line
104        * @exception Exception if an error occurs during plugin establishment
105        */
106        private TransitConsoleHandler( final Logger logger, final CommandLine line ) throws Exception
107        {
108            if( null == line ) 
109            {
110                m_line = null;
111                m_directive = null;
112                m_builder = null;
113                m_logger = null;
114                System.exit( -1 );
115            }
116            else
117            {
118                m_logger = logger;
119                m_line = line;
120                m_directive = loadTransitDirective( line );
121                m_builder = new TransitDirectiveBuilder( m_directive );
122                if( null == m_directive )
123                {
124                    System.exit( -1 );
125                }
126            }
127            
128            // handle command
129            
130            if( line.hasOption( INFO_COMMAND ) )
131            {
132                processInfo( line );
133            }
134            else if( line.hasOption( ADD_COMMAND ) )
135            {
136                TransitDirective directive = processAdd( line );
137                export( directive );
138            }
139            else if( line.hasOption( SET_COMMAND ) )
140            {
141                TransitDirective directive = processSet( line );
142                export( directive );
143            }
144            else if( line.hasOption( REMOVE_COMMAND ) )
145            {
146                TransitDirective directive = processRemove( line );
147                export( directive );
148            }
149            else if( line.hasOption( REVERT_COMMAND ) )
150            {
151                export( TransitDirective.CLASSIC_PROFILE );
152            }
153            else if( line.hasOption( LOAD_COMMAND ) )
154            {
155                processLoad( line );
156            }
157            else
158            {
159                processHelp();
160            }
161        }
162        
163        // ------------------------------------------------------------------------
164        // command handling
165        // ------------------------------------------------------------------------
166        
167        private void processLoad( CommandLine line )
168        {
169            try
170            {
171                URI uri = (URI) line.getValue( LOAD_COMMAND, null );
172                List list = line.getValues( ARGUMENTS );
173                String[] args = (String[]) list.toArray( new String[ list.size() ] );
174                Part part = Part.load( uri );
175                Object instance = part.instantiate( new Object[]{args, m_logger} );
176                if( instance instanceof Runnable )
177                {
178                    Runnable runnable = (Runnable) instance;
179                    runnable.run();
180                }
181            }
182            catch( PartException e )
183            {
184                boolean stack = isDebugEnabled();
185                String message = ExceptionHelper.packException( e, stack ); 
186                System.out.println( message );
187            }
188            catch( Throwable e )
189            {
190                processException( e );
191            }
192        }
193        
194        private void processException( Throwable e )
195        {
196            if( e instanceof InvocationTargetException )
197            {
198                InvocationTargetException ite = (InvocationTargetException) e;
199                Throwable cause = ite.getCause();
200                processException( cause );
201            }
202            else
203            {
204                final String error = 
205                  "Part exception.";
206                String message = ExceptionHelper.packException( error, e, true ); 
207                System.out.println( message );
208            }
209        }
210        
211        private boolean isDebugEnabled()
212        {
213            return "true".equals( System.getProperty( "dpml.debug", "false" ) );
214        }
215        
216        private void processInfo( CommandLine line )
217        {
218            StringBuffer buffer = new StringBuffer();
219            
220            buffer.append( "\n\n  Transit Console Version 1.0.4" );
221            buffer.append( "\n  Transit Runtime Version " + Transit.VERSION );
222            buffer.append( "\n\n  Environment\n" );
223            buffer.append( "\n    ${dpml.home} \t" + Transit.DPML_HOME );
224            buffer.append( "\n    ${dpml.data} \t" + Transit.DPML_DATA );
225            buffer.append( "\n    ${dpml.prefs} \t" + Transit.DPML_PREFS );
226            buffer.append( "\n    ${dpml.system} \t" + Transit.DPML_SYSTEM );
227            
228            ProxyDirective proxy = m_directive.getProxyDirective();
229            if( null != proxy )
230            {
231                buffer.append( "\n\n  Proxy Settings" );
232                buffer.append( "\n\n    Host: \t" + proxy.getHost() );
233                if( proxy.getUsername() != null )
234                {
235                    buffer.append( "\n    Username: \t" + proxy.getUsername() );
236                }
237                char[] pswd = proxy.getPassword();
238                if( null != pswd )
239                {
240                    buffer.append( "\n    Password: \t" );
241                    for( int i=0; i<pswd.length; i++ )
242                    {
243                        buffer.append( "*" );
244                    }
245                }
246                String[] excludes = proxy.getExcludes();
247                if( excludes.length > 0 )
248                {
249                    buffer.append( "\n    Excludes: \t" );
250                    for( int i=0; i<excludes.length; i++ )
251                    {
252                        String exclude = excludes[i];
253                        buffer.append( exclude );
254                        if( i < ( excludes.length-1 ) )
255                        {
256                            buffer.append( ", " );
257                        }
258                    }
259                }
260            }
261            
262            CacheDirective cache = m_directive.getCacheDirective();
263            
264            buffer.append( "\n\n  Cache and System Settings\n" );
265            buffer.append( "\n    Cache Layout: \t" + cache.getCacheLayout() );
266            buffer.append( "\n    Cache Directory: \t" + cache.getCache() );
267            buffer.append( "\n    System Directory: \t" + cache.getLocal() );
268            buffer.append( "\n    System Layout: \t" + cache.getLocalLayout() );
269            
270            HostDirective[] hosts = cache.getHostDirectives();
271            if( hosts.length > 0 )
272            {
273                buffer.append( "\n\n  Host Settings" );
274                for( int i=0; i<hosts.length; i++ )
275                {
276                    HostDirective host = hosts[i];
277                    buffer.append( "\n\n    " + host.getID() + " (" + ( i+1 ) + ")" );
278                    buffer.append( "\n\n      URL\t" + host.getHost() );
279                    buffer.append( "\n      Priority:\t" + host.getPriority() );
280                    if( host.getIndex() != null )
281                    {
282                        buffer.append( "\n      Index: \t" + host.getIndex() );
283                    }
284                    buffer.append( "\n      Enabled: \t" + host.getEnabled() );
285                    buffer.append( "\n      Trusted: \t" + host.getTrusted() );
286                    buffer.append( "\n      Layout: \t" + host.getLayout() );
287                    if( host.getUsername() != null )
288                    {
289                        buffer.append( "\n      Username: \t" + host.getUsername() );
290                    }
291                    buffer.append( "\n      Password: \t" );
292                    char[] pswd = host.getPassword();
293                    if( null != pswd )
294                    {
295                        for( int j=0; j<pswd.length; j++ )
296                        {
297                            buffer.append( "*" );
298                        }
299                    }
300                    buffer.append( "\n      Prompt: \t" + host.getPrompt() );
301                    buffer.append( "\n      Scheme: \t" + host.getScheme() );
302                }
303            }
304            else
305            {
306                buffer.append( "\n\n  No hosts." );
307            }
308            
309            LayoutDirective[] layouts = cache.getLayoutDirectives();
310            if( layouts.length > 0 )
311            {
312                buffer.append( "\n\n  Layout Settings" );
313                for( int i=0; i<layouts.length; i++ )
314                {
315                    LayoutDirective layout = layouts[i];
316                    buffer.append( "\n\n    " + layout.getID() + " (" + ( i+1 ) + ")" );
317                    buffer.append( "\n\n      Codebase:\t" + layout.getCodeBaseURI() );
318                    buffer.append( "\n      Title:\t" + layout.getTitle() );
319                }
320            }
321            
322            System.out.println( buffer.toString() );
323        }
324        
325        private TransitDirective processAdd( CommandLine line ) throws Exception
326        {
327            if( line.hasOption( ADD_HOST_COMMAND ) )
328            {
329                String key = (String) line.getValue( ADD_HOST_COMMAND, null );
330                CacheDirective cache = m_directive.getCacheDirective();
331                HostDirective[] hosts = cache.getHostDirectives();
332                for( int i=0; i<hosts.length; i++ )
333                {
334                    HostDirective h = hosts[i];
335                    if( h.getID().equals( key ) )
336                    {
337                        System.out.println( "ERROR: Host id '" + key + "' already assigned." );
338                        return null;
339                    }
340                }
341                
342                System.out.println( "Adding host: " + key );
343                URL url = (URL) line.getValue( REQUIRED_HOST_OPTION, null );
344                String username = (String) line.getValue( USERNAME_OPTION, null );
345                String password = (String) line.getValue( PASSWORD_OPTION, null );
346                Number priority = (Number) line.getValue( HOST_PRIORITY_OPTION, new Integer( 100 ) );
347                String index = (String) line.getValue( HOST_INDEX_OPTION, null );
348                boolean enabled = !line.hasOption( DISABLED_OPTION );
349                boolean trusted = line.hasOption( TRUSTED_OPTION );
350                String layout = (String) line.getValue( LAYOUT_OPTION, "classic" );
351                String scheme = (String) line.getValue( HOST_SCHEME_OPTION, "" );
352                String prompt = (String) line.getValue( HOST_PROMPT_OPTION, "" );
353                
354                HostDirective host = 
355                  new HostDirective( 
356                    key, priority.intValue(), url.toString(), index, username, 
357                    toCharArray( password ), enabled, trusted, layout, 
358                    scheme, prompt );
359                    
360                HostDirective[] newHosts = new HostDirective[ hosts.length + 1 ];
361                for( int i=0; i<hosts.length; i++ )
362                {
363                    newHosts[i] = hosts[i];
364                }
365                newHosts[ hosts.length ] = host;
366                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
367                CacheDirective newCache = builder.create( newHosts );
368                return m_builder.create( newCache );
369            }
370            else if( line.hasOption( ADD_LAYOUT_COMMAND ) )
371            {
372                String key = (String) line.getValue( ADD_LAYOUT_COMMAND, null );
373                CacheDirective cache = m_directive.getCacheDirective();
374                LayoutDirective[] handlers = cache.getLayoutDirectives();
375                for( int i=0; i<handlers.length; i++ )
376                {
377                    LayoutDirective c = handlers[i];
378                    if( c.getID().equals( key ) )
379                    {
380                        System.out.println( "ERROR: Layout id '" + key + "' already assigned." );
381                        return null;
382                    }
383                }
384                
385                System.out.println( "Adding layout: " + key );
386                URI uri = (URI) line.getValue( REQUIRED_CODEBASE_OPTION, null );
387                String title = (String) line.getValue( TITLE_OPTION, null );
388                
389                LayoutDirective handler = 
390                  new LayoutDirective( 
391                    key, title, uri, new ValueDirective[0] );
392                
393                LayoutDirective[] newHandlers = new LayoutDirective[ handlers.length + 1 ];
394                for( int i=0; i<handlers.length; i++ )
395                {
396                    newHandlers[i] = handlers[i];
397                }
398                newHandlers[ handlers.length ] = handler;
399                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
400                CacheDirective newCache = builder.create( newHandlers );
401                return m_builder.create( newCache );
402            }
403            else
404            {
405                throw new IllegalStateException( "Unqualified add." );
406            }
407        }
408        
409        private TransitDirective processSet( CommandLine line ) throws Exception
410        {
411            if( line.hasOption( SET_CACHE_COMMAND ) )
412            {
413                return setCache( line );
414            }
415            else if( line.hasOption( SET_SYSTEM_COMMAND ) )
416            {
417                return setSystem( line );
418            }
419            else if( line.hasOption( PROXY_COMMAND ) )
420            {
421                return setProxy( line );
422            }
423            else if( line.hasOption( SET_HOST_COMMAND ) )
424            {
425                return setHost( line );
426            }
427            else if( line.hasOption( SET_LAYOUT_COMMAND ) )
428            {
429                return setLayout( line );
430            }
431            else
432            {
433                throw new IllegalStateException( "Unqualified set command." );
434            }
435        }
436        
437        private TransitDirective setCache( CommandLine line ) throws Exception
438        {
439            String cache = (String) line.getValue( DIRECTORY_OPTION );
440            String layout = (String) line.getValue( LAYOUT_OPTION );
441            CacheDirective directive = m_directive.getCacheDirective();
442            CacheDirectiveBuilder builder = new CacheDirectiveBuilder( directive );
443            CacheDirective newCache = builder.create( cache, layout, null, null );
444            return m_builder.create( newCache );
445        }
446        
447        private TransitDirective setSystem( CommandLine line ) throws Exception
448        {
449            String system = (String) line.getValue( DIRECTORY_OPTION );
450            String layout = (String) line.getValue( LAYOUT_OPTION );
451            CacheDirective directive = m_directive.getCacheDirective();
452            CacheDirectiveBuilder builder = new CacheDirectiveBuilder( directive );
453            CacheDirective newCache = builder.create( null, null, system, layout );
454            return m_builder.create( newCache );
455        }
456        
457        private TransitDirective setProxy( CommandLine line ) throws Exception
458        {
459            System.out.println( "Updating proxy configuration." );
460            ProxyDirective proxy = m_directive.getProxyDirective();
461            
462            URL url = (URL) line.getValue( HOST_OPTION, null );
463            String username = (String) line.getValue( USERNAME_OPTION, null );
464            String password = (String) line.getValue( PASSWORD_OPTION, null );
465            List values = line.getValues( PROXY_EXCLUDE_OPTION );
466            String[] excludes = (String[]) values.toArray( new String[0] );
467            char[] pswd = toCharArray( password );
468            if( null == proxy )
469            {
470                if( null == url )
471                {
472                    System.out.println( "ERROR: Missing proxy host option." );
473                    return null;
474                }
475                ProxyDirective p = new ProxyDirective( 
476                  url.toString(), excludes, username, pswd );
477                return m_builder.create( p );
478            }
479            else
480            {
481                ProxyDirectiveBuilder builder = new ProxyDirectiveBuilder( proxy );
482                ProxyDirective p = builder.create( url, excludes, username, pswd );
483                return m_builder.create( p );
484            }
485        }
486        
487        private TransitDirective setHost( CommandLine line ) throws Exception
488        {
489            String key = (String) line.getValue( SET_HOST_COMMAND, null );
490            CacheDirective cache = m_directive.getCacheDirective();
491            HostDirective[] hosts = cache.getHostDirectives();
492            HostDirective host = null;
493            for( int i=0; i<hosts.length; i++ )
494            {
495                HostDirective h = hosts[i];
496                if( h.getID().equals( key ) )
497                {
498                    host = h;
499                }
500            }
501            
502            if( null == host )
503            {
504                System.out.println( "ERROR: Host id '" + key + "' not recognized." );
505                return null;
506            }
507            
508            System.out.println( "Updating host: " + key );
509            URL url = (URL) line.getValue( HOST_OPTION, null );
510            int priority = getPriorityValue( line );
511            String index = (String) line.getValue( HOST_INDEX_OPTION, null );
512            String username = (String) line.getValue( USERNAME_OPTION, null );
513            String password = (String) line.getValue( PASSWORD_OPTION, null );
514            boolean enabled = getEnabledFlag( line, host );
515            boolean trusted = getTrustedFlag( line, host );
516            String layout = (String) line.getValue( LAYOUT_OPTION, null );
517            String scheme = (String) line.getValue( HOST_SCHEME_OPTION, null );
518            String prompt = (String) line.getValue( HOST_PROMPT_OPTION, null );
519            
520            HostDirectiveBuilder builder = new HostDirectiveBuilder( host );
521            HostDirective newHost = 
522              builder.create( 
523                priority, url, index, username, toCharArray( password ),
524                enabled, trusted, layout, scheme, prompt );
525            
526            HostDirective[] newHosts = new HostDirective[ hosts.length ];
527            for( int i=0; i<hosts.length; i++ )
528            {   
529                HostDirective h = hosts[i];
530                if( h.getID().equals( key ) )
531                {
532                    newHosts[i] = newHost;
533                }
534                else
535                {
536                    newHosts[i] = h;
537                }
538            }
539            CacheDirectiveBuilder cacheBuilder = new CacheDirectiveBuilder( cache );
540            CacheDirective newCache = cacheBuilder.create( newHosts );
541            return m_builder.create( newCache );
542        }
543        
544        private TransitDirective setLayout( CommandLine line ) throws Exception
545        {
546            String key = (String) line.getValue( SET_LAYOUT_COMMAND, null );
547            CacheDirective cache = m_directive.getCacheDirective();
548            LayoutDirective[] handlers = cache.getLayoutDirectives();
549            LayoutDirective handler = null;
550            for( int i=0; i<handlers.length; i++ )
551            {
552                LayoutDirective c = handlers[i];
553                if( c.getID().equals( key ) )
554                {
555                    handler = c;
556                }
557            }
558    
559            if( null == handler )
560            {
561                System.out.println( "ERROR: Layout id '" + key + "' not recognized." );
562                return null;
563            }
564                
565            System.out.println( "Updating layout: " + key );
566            URI uri = (URI) line.getValue( CODEBASE_OPTION, null );
567            String title = (String) line.getValue( TITLE_OPTION, null );
568                
569            LayoutDirectiveBuilder builder = new LayoutDirectiveBuilder( handler );
570            LayoutDirective newDirective = 
571              builder.create( title, uri, null );
572                
573            LayoutDirective[] newDirectives = new LayoutDirective[ handlers.length ];
574            for( int i=0; i<handlers.length; i++ )
575            {   
576                LayoutDirective d = handlers[i];
577                if( d.getID().equals( key ) )
578                {
579                    newDirectives[i] = newDirective;
580                }
581                else
582                {
583                    newDirectives[i] = d;
584                }
585            }
586            CacheDirectiveBuilder cacheBuilder = new CacheDirectiveBuilder( cache );
587            CacheDirective newCache = cacheBuilder.create( newDirectives );
588            return m_builder.create( newCache );
589        }
590        
591        private TransitDirective processRemove( CommandLine line )
592        {
593            if( line.hasOption( SELECT_HOST_COMMAND ) )
594            {
595                String key = (String) line.getValue( SELECT_HOST_COMMAND, null );
596                System.out.println( "Removing resource host: " + key );
597                CacheDirective cache = m_directive.getCacheDirective();
598                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
599                try
600                {
601                    CacheDirective newCache = builder.removeHostDirective( key );
602                    return m_builder.create( newCache );
603                }
604                catch( UnknownKeyException e )
605                {
606                    System.out.println( "Unknown host '" + key + "'." );
607                    return null;
608                }
609            }
610            else if( line.hasOption( SELECT_LAYOUT_COMMAND ) )
611            {
612                String key = (String) line.getValue( SELECT_LAYOUT_COMMAND, null );
613                System.out.println( "Removing layout: " + key );
614                CacheDirective cache = m_directive.getCacheDirective();
615                CacheDirectiveBuilder builder = new CacheDirectiveBuilder( cache );
616                try
617                {
618                    CacheDirective newCache = builder.removeLayoutDirective( key );
619                    return m_builder.create( newCache );
620                }
621                catch( UnknownKeyException e )
622                {
623                    System.out.println( "Unknown layout '" + key + "'." );
624                    return null;
625                }
626            }
627            else if( line.hasOption( REMOVE_PROXY_COMMAND ) )
628            {
629                if( null == m_directive.getProxyDirective() )
630                {
631                    System.out.println( "Nothing to remove." );
632                    return null;
633                }
634                else
635                {
636                    System.out.println( "Removing all proxy settings." );
637                    CacheDirective cache = m_directive.getCacheDirective();
638                    return new TransitDirective( null, cache );
639                }
640            }
641            else
642            {
643                throw new IllegalStateException( "Unqualified remove command." );
644            }
645        }
646        
647        private void export( TransitDirective directive )
648        {
649            if( null == directive )
650            {
651                return;
652            }
653            else if( m_directive.equals( directive ) )
654            {
655                System.out.println( "# no change" );
656            }
657            else
658            {
659                URI store = DefaultTransitModel.DEFAULT_PROFILE_URI;
660                URI uri = (URI) m_line.getValue( PROFILE_URI_OPTION, store );
661                System.out.println( "Saving to: " + uri );
662                OutputStream output = null;
663                try
664                {
665                    URL url = buildURL( uri );
666                    output = url.openConnection().getOutputStream();
667                    TransitBuilder builder = new TransitBuilder( m_logger );
668                    builder.write( directive, output );
669                }
670                catch( Exception e )
671                {
672                    final String error = 
673                      "Internal error while attempting to write to the uri: "
674                      + uri;
675                    getLogger().error( error, e );
676                }
677                finally
678                {
679                    if( null != output )
680                    {
681                        try
682                        {
683                            output.close();
684                        }
685                        catch( Exception e )
686                        {
687                            e.printStackTrace();
688                        }
689                    }
690                }
691            }
692        }
693        
694        // ------------------------------------------------------------------------
695        // internal utilities
696        // ------------------------------------------------------------------------
697        
698        private Logger getLogger()
699        {
700            return m_logger;
701        }
702        
703        private char[] toCharArray( String value )
704        {
705            if( null == value )
706            {
707                return null;
708            }
709            else
710            {
711                return value.toCharArray();
712            }
713        }
714        
715        private TransitDirective loadTransitDirective( CommandLine line )
716        {
717            if( line.hasOption( PROFILE_URI_OPTION ) )
718            {
719                URI uri = (URI) line.getValue( PROFILE_URI_OPTION, null );
720                try
721                {
722                    URL url = buildURL( uri );
723                    TransitBuilder builder = new TransitBuilder( m_logger );
724                    return builder.load( url );
725                }
726                catch( FileNotFoundException e )
727                {
728                    final String error = 
729                      "Resource not found: "
730                    + uri;
731                    getLogger().warn( error );
732                    return null;
733                }
734                catch( Exception e )
735                {
736                    final String error = 
737                      "An error occured while attempting to load the transit profile: "
738                      + uri;
739                    getLogger().error( error, e  );
740                    return null;
741                }
742            }
743            else
744            {
745                File prefs = Transit.DPML_PREFS;
746                File config = new File( prefs, "dpml/transit/xmls/standard.xml" );
747                if( config.exists() )
748                {
749                    try
750                    {
751                        URI uri = config.toURI();
752                        URL url = uri.toURL();
753                        TransitBuilder builder = new TransitBuilder( m_logger );
754                        return builder.load( url );
755                    }
756                    catch( Exception e )
757                    {
758                        final String error = 
759                          "An error occured while attempting to load the transit profile: "
760                        + config;
761                        getLogger().error( error, e  );
762                        return null;
763                    }
764                }
765                else
766                {
767                    //
768                    // load default profile
769                    //
770                    
771                    return TransitDirective.CLASSIC_PROFILE;
772                }
773            }
774        }
775        
776        private URL buildURL( URI uri ) throws IOException
777        {
778            if( Artifact.isRecognized( uri ) )
779            {
780                return Artifact.createArtifact( uri ).toURL();
781            }
782            else
783            {
784                return uri.toURL();
785            }
786          
787        }
788        
789        private boolean getEnabledFlag( CommandLine line, HostDirective host )
790        {
791            if( line.hasOption( DISABLED_OPTION ) )
792            {
793                return false;
794            }
795            else if( line.hasOption( ENABLED_OPTION ) )
796            {
797                return true;
798            }
799            else
800            {
801                return host.getEnabled();
802            }
803        }
804        
805        private boolean getTrustedFlag( CommandLine line, HostDirective host )
806        {
807            if( line.hasOption( TRUSTED_OPTION ) )
808            {
809                return true;
810            }
811            else if( line.hasOption( UNTRUSTED_OPTION ) )
812            {
813                return false;
814            }
815            else
816            {
817                return host.getTrusted();
818            }
819        }
820        
821        private int getPriorityValue( CommandLine line )
822        {
823            if( line.hasOption( HOST_PRIORITY_OPTION ) )
824            {
825                Number priority = (Number) line.getValue( HOST_PRIORITY_OPTION, new Integer( -1 ) );
826                return priority.intValue();
827            }
828            else
829            {
830                return -1;
831            }
832        }
833        
834        // ------------------------------------------------------------------------
835        // static utilities
836        // ------------------------------------------------------------------------
837        
838        private static final DefaultOptionBuilder OPTION_BUILDER = new DefaultOptionBuilder();
839        private static final ArgumentBuilder ARGUMENT_BUILDER = new ArgumentBuilder();
840        private static final CommandBuilder COMMAND_BUILDER = new CommandBuilder();
841        private static final GroupBuilder GROUP_BUILDER = new GroupBuilder();
842        private static final NumberValidator INTERGER_VALIDATOR = NumberValidator.getIntegerInstance();
843        private static final Set HELP_TOPICS = new HashSet();
844        
845        static
846        {
847            HELP_TOPICS.add( "add" );
848            HELP_TOPICS.add( "set" );
849            HELP_TOPICS.add( "remove" );
850        }
851        
852        private static final Option DIRECTORY_OPTION = 
853            OPTION_BUILDER
854              .withShortName( "dir" )
855              .withDescription( "Directory." )
856              .withRequired( false )
857              .withArgument(
858                ARGUMENT_BUILDER 
859                  .withDescription( "Directory." )
860                  .withName( "path" )
861                  .withMinimum( 1 )
862                  .withMaximum( 1 )
863                  .create() )
864              .create();
865        
866        private static final Option SYSTEM_LIBRARY_OPTION = 
867            OPTION_BUILDER
868              .withShortName( "system" )
869              .withDescription( "Local system repository." )
870              .withRequired( false )
871              .withArgument(
872                ARGUMENT_BUILDER 
873                  .withDescription( "Directory." )
874                  .withName( "dir" )
875                  .withMinimum( 1 )
876                  .withMaximum( 1 )
877                  .create() )
878              .create();
879              
880        private static final Option PROFILE_URI_OPTION = 
881            OPTION_BUILDER
882              .withShortName( "profile" )
883              .withDescription( "Configuration profile uri." )
884              .withRequired( false )
885              .withArgument(
886                ARGUMENT_BUILDER 
887                  .withDescription( "URI path." )
888                  .withName( "uri" )
889                  .withMinimum( 1 )
890                  .withMaximum( 1 )
891                  .withValidator( new URIValidator() )
892                  .create() )
893              .create();
894        
895        private static final Option HELP_COMMAND =
896          COMMAND_BUILDER
897            .withName( "help" )
898            .withDescription( "Print command help." )
899            .create();
900            
901        private static final Option HOST_OPTION = 
902          OPTION_BUILDER
903            .withShortName( "url" )
904            .withDescription( "Host URL." )
905            .withRequired( false )
906            .withArgument(
907              ARGUMENT_BUILDER 
908                .withName( "url" )
909                .withMinimum( 1 )
910                .withMaximum( 1 )
911                .withValidator( new URLValidator() )
912                .create() )
913            .create();
914        
915        private static final Option REQUIRED_HOST_OPTION = 
916          OPTION_BUILDER
917            .withShortName( "url" )
918            .withDescription( "Host URL (required)." )
919            .withRequired( true )
920            .withArgument(
921              ARGUMENT_BUILDER 
922                .withName( "url" )
923                .withMinimum( 1 )
924                .withMaximum( 1 )
925                .withValidator( new URLValidator() )
926                .create() )
927            .create();
928        
929        private static final Option REQUIRED_CODEBASE_OPTION = 
930          OPTION_BUILDER
931            .withShortName( "uri" )
932            .withDescription( "Codebase URI (required)." )
933            .withRequired( true )
934            .withArgument(
935              ARGUMENT_BUILDER 
936                .withName( "uri" )
937                .withMinimum( 1 )
938                .withMaximum( 1 )
939                .withValidator( new URIValidator() )
940                .create() )
941            .create();
942        
943        private static final Option CODEBASE_OPTION = 
944          OPTION_BUILDER
945            .withShortName( "uri" )
946            .withDescription( "Codebase URI." )
947            .withRequired( false )
948            .withArgument(
949              ARGUMENT_BUILDER 
950                .withName( "uri" )
951                .withMinimum( 1 )
952                .withMaximum( 1 )
953                .withValidator( new URIValidator() )
954                .create() )
955            .create();
956        
957        private static final Option TITLE_OPTION = 
958          OPTION_BUILDER
959            .withShortName( "title" )
960            .withDescription( "Title." )
961            .withRequired( false )
962            .withArgument(
963              ARGUMENT_BUILDER 
964                .withName( "name" )
965                .withMinimum( 1 )
966                .withMaximum( 1 )
967                .create() )
968            .create();
969        
970        private static final Option USERNAME_OPTION = 
971          OPTION_BUILDER
972            .withShortName( "username" )
973            .withDescription( "Username." )
974            .withRequired( false )
975            .withArgument(
976              ARGUMENT_BUILDER 
977                .withName( "username" )
978                .withMinimum( 1 )
979                .withMaximum( 1 )
980                .create() )
981            .create();
982        
983        private static final Option PASSWORD_OPTION = 
984          OPTION_BUILDER
985            .withShortName( "password" )
986            .withDescription( "Password." )
987            .withRequired( false )
988            .withArgument(
989              ARGUMENT_BUILDER 
990                .withName( "password" )
991                .withMinimum( 1 )
992                .withMaximum( 1 )
993                .create() )
994            .create();
995    
996        private static final Option HOST_PRIORITY_OPTION = 
997          OPTION_BUILDER
998            .withShortName( "priority" )
999            .withDescription( "Host priority." )
1000            .withRequired( false )
1001            .withArgument(
1002              ARGUMENT_BUILDER 
1003                .withName( "int" )
1004                .withMinimum( 1 )
1005                .withMaximum( 1 )
1006                .withValidator( INTERGER_VALIDATOR )
1007                .create() )
1008            .create();
1009    
1010        private static final Option HOST_INDEX_OPTION = 
1011          OPTION_BUILDER
1012            .withShortName( "index" )
1013            .withDescription( "Host index path." )
1014            .withRequired( false )
1015            .withArgument(
1016              ARGUMENT_BUILDER 
1017                .withName( "resource" )
1018                .withMinimum( 1 )
1019                .withMaximum( 1 )
1020                .create() )
1021            .create();
1022            
1023        private static final Option LAYOUT_OPTION = 
1024          OPTION_BUILDER
1025            .withShortName( "layout" )
1026            .withDescription( "Host layout strategy." )
1027            .withRequired( false )
1028            .withArgument(
1029              ARGUMENT_BUILDER 
1030                .withName( "id" )
1031                .withMinimum( 1 )
1032                .withMaximum( 1 )
1033                .create() )
1034            .create();
1035            
1036        private static final Option HOST_SCHEME_OPTION = 
1037          OPTION_BUILDER
1038            .withShortName( "scheme" )
1039            .withDescription( "Host authentication scheme." )
1040            .withRequired( false )
1041            .withArgument(
1042              ARGUMENT_BUILDER 
1043                .withName( "scheme" )
1044                .withMinimum( 1 )
1045                .withMaximum( 1 )
1046                .create() )
1047            .create();
1048            
1049        private static final Option HOST_PROMPT_OPTION = 
1050          OPTION_BUILDER
1051            .withShortName( "prompt" )
1052            .withDescription( "Host authentication prompt." )
1053            .withRequired( false )
1054            .withArgument(
1055              ARGUMENT_BUILDER 
1056                .withName( "prompt" )
1057                .withMinimum( 1 )
1058                .withMaximum( 1 )
1059                .create() )
1060            .create();
1061        
1062        private static final Option TRUSTED_OPTION = 
1063          OPTION_BUILDER
1064            .withShortName( "trusted" )
1065            .withDescription( "Assert as trusted host." )
1066            .withRequired( false )
1067            .create();
1068            
1069        private static final Option UNTRUSTED_OPTION = 
1070          OPTION_BUILDER
1071            .withShortName( "untrusted" )
1072            .withDescription( "Assert as untrusted host." )
1073            .withRequired( false )
1074            .create();
1075        
1076        private static final Group TRUST_GROUP =
1077          GROUP_BUILDER
1078            .withMinimum( 0 )
1079            .withMaximum( 1 )
1080            .withOption( TRUSTED_OPTION )
1081            .withOption( UNTRUSTED_OPTION )
1082            .create();
1083        
1084        private static final Option ENABLED_OPTION = 
1085          OPTION_BUILDER
1086            .withShortName( "enabled" )
1087            .withDescription( "Enable the host." )
1088            .withRequired( false )
1089            .create();
1090            
1091        private static final Option DISABLED_OPTION = 
1092          OPTION_BUILDER
1093            .withShortName( "disabled" )
1094            .withDescription( "Disable the host." )
1095            .withRequired( false )
1096            .create();
1097            
1098        private static final Group ENABLED_GROUP =
1099          GROUP_BUILDER
1100            .withMinimum( 0 )
1101            .withMaximum( 1 )
1102            .withOption( ENABLED_OPTION )
1103            .withOption( DISABLED_OPTION )
1104            .create();
1105        
1106        private static final Option PROXY_EXCLUDE_OPTION = 
1107          OPTION_BUILDER
1108            .withShortName( "excludes" )
1109            .withDescription( "Proxy excludes." )
1110            .withRequired( false )
1111            .withArgument(
1112              ARGUMENT_BUILDER 
1113                .withName( "hosts" )
1114                .withMinimum( 1 )
1115                .withInitialSeparator( ' ' )
1116                .withSubsequentSeparator( ',' )
1117                .create() )
1118            .create();
1119            
1120        private static final Group SET_HANDLER_OPTIONS_GROUP =
1121          GROUP_BUILDER
1122            .withMinimum( 0 )
1123            .withOption( CODEBASE_OPTION )
1124            .withOption( TITLE_OPTION )
1125            .create();
1126        
1127        private static final Group ADD_HANDLER_OPTIONS_GROUP =
1128          GROUP_BUILDER
1129            .withOption( REQUIRED_CODEBASE_OPTION )
1130            .withOption( TITLE_OPTION )
1131            .create();
1132            
1133        private static final Group SET_HOST_OPTIONS_GROUP =
1134          GROUP_BUILDER
1135            .withMinimum( 0 )
1136            .withOption( HOST_OPTION )
1137            .withOption( USERNAME_OPTION )
1138            .withOption( PASSWORD_OPTION )
1139            .withOption( HOST_PRIORITY_OPTION )
1140            .withOption( HOST_INDEX_OPTION )
1141            .withOption( ENABLED_GROUP )
1142            .withOption( TRUST_GROUP )
1143            .withOption( LAYOUT_OPTION )
1144            .withOption( HOST_SCHEME_OPTION )
1145            .withOption( HOST_PROMPT_OPTION )
1146            .create();
1147        
1148        private static final Group ADD_HOST_OPTIONS_GROUP =
1149          GROUP_BUILDER
1150            .withMinimum( 0 )
1151            .withOption( REQUIRED_HOST_OPTION )
1152            .withOption( USERNAME_OPTION )
1153            .withOption( PASSWORD_OPTION )
1154            .withOption( HOST_PRIORITY_OPTION )
1155            .withOption( HOST_INDEX_OPTION )
1156            .withOption( DISABLED_OPTION )
1157            .withOption( TRUSTED_OPTION )
1158            .withOption( LAYOUT_OPTION )
1159            .withOption( HOST_SCHEME_OPTION )
1160            .withOption( HOST_PROMPT_OPTION )
1161            .create();
1162        
1163        private static final Group PROXY_OPTIONS_GROUP =
1164          GROUP_BUILDER
1165            .withMinimum( 0 )
1166            .withOption( HOST_OPTION )
1167            .withOption( USERNAME_OPTION )
1168            .withOption( PASSWORD_OPTION )
1169            .withOption( PROXY_EXCLUDE_OPTION )
1170            .create();
1171        
1172        private static final Group CACHE_OPTIONS_GROUP =
1173          GROUP_BUILDER
1174            .withMinimum( 0 )
1175            .withOption( DIRECTORY_OPTION )
1176            .withOption( LAYOUT_OPTION )
1177            .create();
1178        
1179        private static final Group SYSTEM_OPTIONS_GROUP =
1180          GROUP_BUILDER
1181            .withMinimum( 0 )
1182            .withOption( DIRECTORY_OPTION )
1183            .withOption( LAYOUT_OPTION )
1184            .create();
1185        
1186        private static final Option PROXY_COMMAND =
1187          COMMAND_BUILDER
1188            .withName( "proxy" )
1189            .withDescription( "Select proxy settings." )
1190            .withChildren( PROXY_OPTIONS_GROUP )
1191            .create();
1192        
1193        private static final Option SET_CACHE_COMMAND =
1194          COMMAND_BUILDER
1195            .withName( "cache" )
1196            .withDescription( "Select cache settings." )
1197            .withChildren( CACHE_OPTIONS_GROUP )
1198            .create();
1199        
1200        private static final Option SET_SYSTEM_COMMAND =
1201          COMMAND_BUILDER
1202            .withName( "system" )
1203            .withDescription( "Select system repository settings." )
1204            .withChildren( SYSTEM_OPTIONS_GROUP )
1205            .create();
1206        
1207        private static final Option ADD_HOST_COMMAND =
1208          COMMAND_BUILDER
1209            .withName( "host" )
1210            .withDescription( "Add a new resource host." )
1211            .withChildren( ADD_HOST_OPTIONS_GROUP )
1212            .withArgument(
1213              ARGUMENT_BUILDER 
1214                .withName( "id" )
1215                .withMinimum( 1 )
1216                .withMaximum( 1 )
1217                .create() )
1218            .create();
1219    
1220        private static final Option SET_HOST_COMMAND =
1221          COMMAND_BUILDER
1222            .withName( "host" )
1223            .withDescription( "Resource host selection." )
1224            .withChildren( SET_HOST_OPTIONS_GROUP )
1225            .withArgument(
1226              ARGUMENT_BUILDER 
1227                .withName( "id" )
1228                .withMinimum( 1 )
1229                .withMaximum( 1 )
1230                .create() )
1231            .create();
1232    
1233        private static final Option SET_LAYOUT_COMMAND =
1234          COMMAND_BUILDER
1235            .withName( "layout" )
1236            .withDescription( "Layout scheme selection." )
1237            .withChildren( SET_HANDLER_OPTIONS_GROUP )
1238            .withArgument(
1239              ARGUMENT_BUILDER 
1240                .withName( "id" )
1241                .withMinimum( 1 )
1242                .withMaximum( 1 )
1243                .create() )
1244            .create();
1245    
1246        private static final Option SELECT_HOST_COMMAND =
1247          COMMAND_BUILDER
1248            .withName( "host" )
1249            .withDescription( "Resource host selection." )
1250            .withArgument(
1251              ARGUMENT_BUILDER 
1252                .withName( "id" )
1253                .withMinimum( 1 )
1254                .withMaximum( 1 )
1255                .create() )
1256            .create();
1257    
1258        private static final Option SELECT_LAYOUT_COMMAND =
1259          COMMAND_BUILDER
1260            .withName( "layout" )
1261            .withDescription( "Layout scheme selection." )
1262            .withArgument(
1263              ARGUMENT_BUILDER 
1264                .withName( "id" )
1265                .withMinimum( 1 )
1266                .withMaximum( 1 )
1267                .create() )
1268            .create();
1269    
1270        private static final Option ADD_LAYOUT_COMMAND =
1271          COMMAND_BUILDER
1272            .withName( "layout" )
1273            .withDescription( "Add a new layout." )
1274            .withChildren( ADD_HANDLER_OPTIONS_GROUP )
1275            .withArgument(
1276              ARGUMENT_BUILDER 
1277                .withName( "id" )
1278                .withMinimum( 1 )
1279                .withMaximum( 1 )
1280                .create() )
1281            .create();
1282            
1283        private static final Option REMOVE_PROXY_COMMAND =
1284          COMMAND_BUILDER
1285            .withName( "proxy" )
1286            .withDescription( "Delete proxy settings." )
1287            .create();
1288            
1289        private static final Group ADD_OPTIONS_GROUP =
1290          GROUP_BUILDER
1291            .withMinimum( 1 )
1292            .withMinimum( 1 )
1293            .withOption( ADD_HOST_COMMAND )
1294            .withOption( ADD_LAYOUT_COMMAND )
1295            .create();
1296    
1297        private static final Group SET_OPTIONS_GROUP =
1298          GROUP_BUILDER
1299            .withMinimum( 1 )
1300            .withMinimum( 1 )
1301            .withOption( PROXY_COMMAND )
1302            .withOption( SET_CACHE_COMMAND )
1303            .withOption( SET_SYSTEM_COMMAND )
1304            .withOption( SET_HOST_COMMAND )
1305            .withOption( SET_LAYOUT_COMMAND )
1306            .create();
1307        
1308        private static final Group REMOVE_OPTIONS_GROUP =
1309          GROUP_BUILDER
1310            .withMinimum( 1 )
1311            .withMinimum( 1 )
1312            .withOption( SELECT_HOST_COMMAND )
1313            .withOption( SELECT_LAYOUT_COMMAND )
1314            .withOption( REMOVE_PROXY_COMMAND )
1315            .create();
1316        
1317        private static final Option ADD_COMMAND =
1318          COMMAND_BUILDER
1319            .withName( "add" )
1320            .withChildren( ADD_OPTIONS_GROUP )
1321            .create();
1322        
1323        private static final Option SET_COMMAND =
1324          COMMAND_BUILDER
1325            .withName( "set" )
1326            .withDescription( "Set an configuration aspect." )
1327            .withChildren( SET_OPTIONS_GROUP )
1328            .create();
1329        
1330        private static final Option REMOVE_COMMAND =
1331          COMMAND_BUILDER
1332            .withName( "remove" )
1333            .withChildren( REMOVE_OPTIONS_GROUP )
1334            .create();
1335        
1336        private static final Option INFO_COMMAND =
1337          COMMAND_BUILDER
1338            .withName( "info" )
1339            .withDescription( "List configuration." )
1340            .create();
1341    
1342        private static final Option REVERT_COMMAND =
1343          COMMAND_BUILDER
1344            .withName( "revert" )
1345            .withDescription( "Set configuration to default." )
1346            .create();
1347    
1348        private static final Option LOAD_COMMAND =
1349          COMMAND_BUILDER
1350            .withName( "load" )
1351            .withDescription( "Load a transit plugin." )
1352            .withArgument(
1353              ARGUMENT_BUILDER 
1354                .withName( "uri" )
1355                .withMinimum( 1 )
1356                .withMaximum( 1 )
1357                .withValidator( new URIValidator() )
1358                .create() )
1359            .create();
1360        
1361        private static final Option ARGUMENTS = 
1362            ARGUMENT_BUILDER
1363              .withName( "args" )
1364              .create();
1365    
1366        private static final Group COMMAND_GROUP =
1367          GROUP_BUILDER
1368            .withOption( INFO_COMMAND )
1369            .withOption( ADD_COMMAND )
1370            .withOption( SET_COMMAND )
1371            .withOption( REMOVE_COMMAND )
1372            .withOption( REVERT_COMMAND )
1373            .withOption( LOAD_COMMAND )
1374            .withOption( HELP_COMMAND )
1375            .withMinimum( 1 )
1376            .withMaximum( 1 )
1377            .create();
1378        
1379        private static final Group OPTIONS_GROUP =
1380          GROUP_BUILDER
1381            .withName( "command" )
1382            .withOption( PROFILE_URI_OPTION )
1383            .withOption( COMMAND_GROUP )
1384            .withOption( ARGUMENTS )
1385            .withMinimum( 0 )
1386            .create();
1387    
1388        private static CommandLine getCommandLine( Logger logger, String[] args )
1389        {
1390            try
1391            {
1392                // parse the command line arguments
1393            
1394                Parser parser = new Parser();
1395                parser.setGroup( OPTIONS_GROUP );
1396                return parser.parse( args );
1397            }
1398            catch( OptionException e )
1399            {
1400                logger.error( e.getMessage() );
1401                return null;
1402            }
1403        }
1404        
1405        private static void processHelp() throws IOException
1406        {
1407            processGeneralHelp( OPTIONS_GROUP );
1408        }
1409        
1410        private static void processGeneralHelp( Group group ) throws IOException
1411        {
1412            HelpFormatter formatter = new HelpFormatter( 
1413              HelpFormatter.DEFAULT_GUTTER_LEFT, 
1414              HelpFormatter.DEFAULT_GUTTER_CENTER, 
1415              HelpFormatter.DEFAULT_GUTTER_RIGHT, 
1416              100, 50 );
1417            
1418            formatter.getDisplaySettings().add( DisplaySetting.DISPLAY_GROUP_OUTER );
1419            formatter.getDisplaySettings().add( DisplaySetting.DISPLAY_PROPERTY_OPTION );
1420            formatter.getDisplaySettings().add( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
1421            
1422            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_OPTIONAL );
1423            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_GROUP_OUTER );
1424            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_PROPERTY_OPTION );
1425            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_OPTIONAL );
1426            formatter.getFullUsageSettings().add( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
1427            formatter.getFullUsageSettings().remove( DisplaySetting.DISPLAY_PARENT_CHILDREN );
1428            
1429            formatter.getLineUsageSettings().add( DisplaySetting.DISPLAY_PROPERTY_OPTION );
1430            formatter.getLineUsageSettings().add( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
1431            formatter.getLineUsageSettings().remove( DisplaySetting.DISPLAY_PARENT_CHILDREN );
1432            formatter.getLineUsageSettings().remove( DisplaySetting.DISPLAY_GROUP_EXPANDED );
1433            
1434            formatter.setGroup( group );
1435            formatter.setShellCommand( "transit" );
1436            formatter.print();
1437        }
1438    }
1439