View Javadoc

    1   // ========================================================================
    2   // Copyright 2003-2005 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14  package org.mortbay.start;
    15
    16  import java.io.BufferedReader;
    17  import java.io.File;
    18  import java.io.FileInputStream;
    19  import java.io.IOException;
    20  import java.io.InputStream;
    21  import java.io.InputStreamReader;
    22  import java.io.OutputStream;
    23  import java.lang.reflect.InvocationTargetException;
    24  import java.lang.reflect.Method;
    25  import java.net.ConnectException;
    26  import java.net.InetAddress;
    27  import java.net.Socket;
    28  import java.security.Policy;
    29  import java.util.ArrayList;
    30  import java.util.Hashtable;
    31  import java.util.StringTokenizer;
    32
    33  /*-------------------------------------------*/
    34  /**
    35   * Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the
    36   * start.jar archive. It allows an application to be started with the command "java -jar
    37   * start.jar". The behaviour of Main is controlled by the "org/mortbay/start/start.config" file
    38   * obtained as a resource or file. This can be overridden with the START system property. The
    39   * format of each line in this file is:
    40   *
    41   * <PRE>
    42   *
    43   * SUBJECT [ [!] CONDITION [AND|OR] ]*
    44   *
    45   * </PRE>
    46   *
    47   * where SUBJECT:
    48   *
    49   * <PRE>
    50   * ends with ".class" is the Main class to run.
    51   * ends with ".xml" is a configuration file for the command line
    52   * ends with "/" is a directory from which add all jar and zip files from.
    53   * ends with "/*" is a directory from which add all unconsidered jar and zip files from.
    54   * Containing = are used to assign system properties.
    55   * all other subjects are treated as files to be added to the classpath.
    56   * </PRE>
    57   *
    58   * Subjects may include system properties with $(propertyname) syntax. File subjects starting with
    59   * "/" are considered absolute, all others are relative to the home directory.
    60   * <P>
    61   * CONDITION is one of:
    62   *
    63   * <PRE>
    64   *
    65   * always
    66   * never
    67   * available package.class
    68   * java OPERATOR n.n
    69   * nargs OPERATOR n
    70   * OPERATOR := one of "<",">"," <=",">=","==","!="
    71   *
    72   * </PRE>
    73   *
    74   * CONTITIONS can be combined with AND OR or !, with AND being the assume operator for a list of
    75   * CONDITIONS. Classpath operations are evaluated on the fly, so once a class or jar is added to
    76   * the classpath, subsequent available conditions will see that class. The system parameter
    77   * CLASSPATH, if set is given to the start classloader before any paths from the configuration
    78   * file. Programs started with start.jar may be stopped with the stop.jar, which connects via a
    79   * local port to stop the server. The default port can be set with the STOP.PORT system property (a
    80   * port of < 0 disables the stop mechanism). If the STOP.KEY system property is set, then a random
    81   * key is generated and written to stdout. This key must be passed to the stop.jar.
    82   *
    83   * @author Jan Hlavaty (hlavac@code.cz)
    84   * @author Greg Wilkins
    85   */
    86  public class Main
    87  {
    88      static boolean _debug=System.getProperty("DEBUG",null)!=null;
    89      private String _classname=null;
    90      private Classpath _classpath=new Classpath();
    91      private String _config=System.getProperty("START","org/mortbay/start/start.config");
    92      private ArrayList _xml=new ArrayList();
    93      private boolean _version=false;
    94
    95      public static void main(String[] args)
    96      {
    97          try
    98          {
    99              if (args.length>0&&args[0].equalsIgnoreCase("--help"))
    100             {
    101                 System.err
    102                         .println("Usage: java [-DDEBUG] [-DSTART=start.config] [-Dmain.class=org.MyMain] -jar start.jar [--help|--stop|--version] [config ...]");
    103                 System.exit(1);
    104             }
    105             else if (args.length>0&&args[0].equalsIgnoreCase("--stop"))
    106             {
    107                 new Main().stop();
    108             }
    109             else if (args.length>0&&args[0].equalsIgnoreCase("--version"))
    110             {
    111                 String[] nargs=new String[args.length-1];
    112                 System.arraycopy(args,1,nargs,0,nargs.length);
    113                 Main main=new Main();
    114                 main._version=true;
    115                 main.start(nargs);
    116             }
    117             else
    118             {
    119                 new Main().start(args);
    120             }
    121         }
    122         catch (Exception e)
    123         {
    124             e.printStackTrace();
    125         }
    126     }
    127
    128     static File getDirectory(String name)
    129     {
    130         try
    131         {
    132             if (name!=null)
    133             {
    134                 File dir=new File(name).getCanonicalFile();
    135                 if (dir.isDirectory())
    136                 {
    137                     return dir;
    138                 }
    139             }
    140         }
    141         catch (IOException e)
    142         {
    143         }
    144         return null;
    145     }
    146
    147     boolean isAvailable(String classname)
    148     {
    149         try
    150         {
    151             Class.forName(classname);
    152             return true;
    153         }
    154         catch (NoClassDefFoundError e)
    155         {
    156         }
    157         catch (ClassNotFoundException e)
    158         {
    159         }
    160         ClassLoader loader=_classpath.getClassLoader();
    161         try
    162         {
    163             loader.loadClass(classname);
    164             return true;
    165         }
    166         catch (NoClassDefFoundError e)
    167         {
    168         }
    169         catch (ClassNotFoundException e)
    170         {
    171         }
    172         return false;
    173     }
    174
    175     public void invokeMain(ClassLoader classloader, String classname, String[] args) throws IllegalAccessException, InvocationTargetException,
    176             NoSuchMethodException, ClassNotFoundException
    177     {
    178         Class invoked_class=null;
    179         invoked_class=classloader.loadClass(classname);
    180
    181         if (_version)
    182         {
    183             System.err.println(invoked_class.getPackage().getImplementationTitle()+" "+invoked_class.getPackage().getImplementationVersion());
    184             System.exit(0);
    185         }
    186
    187         Class[] method_param_types=new Class[1];
    188         method_param_types[0]=args.getClass();
    189         Method main=null;
    190         main=invoked_class.getDeclaredMethod("main",method_param_types);
    191         Object[] method_params=new Object[1];
    192         method_params[0]=args;
    193
    194         main.invoke(null,method_params);
    195     }
    196
    197     /* ------------------------------------------------------------ */
    198     String expand(String s)
    199     {
    200         int i1=0;
    201         int i2=0;
    202         while (s!=null)
    203         {
    204             i1=s.indexOf("$(",i2);
    205             if (i1<0)
    206                 break;
    207             i2=s.indexOf(")",i1+2);
    208             if (i2<0)
    209                 break;
    210             String property=System.getProperty(s.substring(i1+2,i2),"");
    211             s=s.substring(0,i1)+property+s.substring(i2+1);
    212         }
    213         return s;
    214     }
    215
    216     /* ------------------------------------------------------------ */
    217     void configure(InputStream config, int nargs) throws Exception
    218     {
    219         BufferedReader cfg=new BufferedReader(new InputStreamReader(config,"ISO-8859-1"));
    220         Version java_version=new Version(System.getProperty("java.version"));
    221         Version ver=new Version();
    222         // JAR's already processed
    223         java.util.Hashtable done=new Hashtable();
    224         // Initial classpath
    225         String classpath=System.getProperty("CLASSPATH");
    226         if (classpath!=null)
    227         {
    228             StringTokenizer tok=new StringTokenizer(classpath,File.pathSeparator);
    229             while (tok.hasMoreTokens())
    230                 _classpath.addComponent(tok.nextToken());
    231         }
    232         // Handle line by line
    233         String line=null;
    234         while (true)
    235         {
    236             line=cfg.readLine();
    237             if (line==null)
    238                 break;
    239             if (line.length()==0||line.startsWith("#"))
    240                 continue;
    241             try
    242             {
    243                 StringTokenizer st=new StringTokenizer(line);
    244                 String subject=st.nextToken();
    245                 boolean expression=true;
    246                 boolean not=false;
    247                 String condition=null;
    248                 // Evaluate all conditions
    249                 while (st.hasMoreTokens())
    250                 {
    251                     condition=st.nextToken();
    252                     if (condition.equalsIgnoreCase("!"))
    253                     {
    254                         not=true;
    255                         continue;
    256                     }
    257                     if (condition.equalsIgnoreCase("OR"))
    258                     {
    259                         if (expression)
    260                             break;
    261                         expression=true;
    262                         continue;
    263                     }
    264                     if (condition.equalsIgnoreCase("AND"))
    265                     {
    266                         if (!expression)
    267                             break;
    268                         continue;
    269                     }
    270                     boolean eval=true;
    271                     if (condition.equals("true")||condition.equals("always"))
    272                     {
    273                         eval=true;
    274                     }
    275                     else if (condition.equals("false")||condition.equals("never"))
    276                     {
    277                         eval=false;
    278                     }
    279                     else if (condition.equals("available"))
    280                     {
    281                         String class_to_check=st.nextToken();
    282                         eval=isAvailable(class_to_check);
    283                     }
    284                     else if (condition.equals("exists"))
    285                     {
    286                         try
    287                         {
    288                             eval=false;
    289                             File file=new File(expand(st.nextToken()));
    290                             eval=file.exists();
    291                         }
    292                         catch (Exception e)
    293                         {
    294                             if (_debug)
    295                                 e.printStackTrace();
    296                         }
    297                     }
    298                     else if (condition.equals("property"))
    299                     {
    300                         String property=System.getProperty(st.nextToken());
    301                         eval=property!=null&&property.length()>0;
    302                     }
    303                     else if (condition.equals("java"))
    304                     {
    305                         String operator=st.nextToken();
    306                         String version=st.nextToken();
    307                         ver.parse(version);
    308                         eval=(operator.equals("<")&&java_version.compare(ver)<0)||(operator.equals(">")&&java_version.compare(ver)>0)
    309                                 ||(operator.equals("<=")&&java_version.compare(ver)<=0)||(operator.equals("=<")&&java_version.compare(ver)<=0)
    310                                 ||(operator.equals("=>")&&java_version.compare(ver)>=0)||(operator.equals(">=")&&java_version.compare(ver)>=0)
    311                                 ||(operator.equals("==")&&java_version.compare(ver)==0)||(operator.equals("!=")&&java_version.compare(ver)!=0);
    312                     }
    313                     else if (condition.equals("nargs"))
    314                     {
    315                         String operator=st.nextToken();
    316                         int number=Integer.parseInt(st.nextToken());
    317                         eval=(operator.equals("<")&&nargs<number)||(operator.equals(">")&&nargs>number)||(operator.equals("<=")&&nargs<=number)
    318                                 ||(operator.equals("=<")&&nargs<=number)||(operator.equals("=>")&&nargs>=number)||(operator.equals(">=")&&nargs>=number)
    319                                 ||(operator.equals("==")&&nargs==number)||(operator.equals("!=")&&nargs!=number);
    320                     }
    321                     else
    322                     {
    323                         System.err.println("ERROR: Unknown condition: "+condition);
    324                         eval=false;
    325                     }
    326                     expression&=not?!eval:eval;
    327                     not=false;
    328                 }
    329                 String file=expand(subject).replace('/',File.separatorChar);
    330                 if (_debug)
    331                     System.err.println((expression?"T ":"F ")+line);
    332                 if (!expression)
    333                 {
    334                     done.put(file,file);
    335                     continue;
    336                 }
    337                 // Handle the subject
    338                 if (subject.indexOf("=")>0)
    339                 {
    340                     int i=file.indexOf("=");
    341                     String property=file.substring(0,i);
    342                     String value=file.substring(i+1);
    343                     if (_debug)
    344                         System.err.println("  "+property+"="+value);
    345                     System.setProperty(property,value);
    346                 }
    347                 else if (subject.endsWith("/*"))
    348                 {
    349                     // directory of JAR files - only add jars and zips
    350                     // within the directory
    351                     File dir=new File(file.substring(0,file.length()-1));
    352                     addJars(dir,done,false);
    353                 }
    354                 else if (subject.endsWith("/**"))
    355                 {
    356                     //directory hierarchy of jar files - recursively add all
    357                     //jars and zips in the hierarchy
    358                     File dir=new File(file.substring(0,file.length()-2));
    359                     addJars(dir,done,true);
    360                 }
    361                 else if (subject.endsWith("/"))
    362                 {
    363                     // class directory
    364                     File cd=new File(file);
    365                     String d=cd.getCanonicalPath();
    366                     if (!done.containsKey(d))
    367                     {
    368                         done.put(d,d);
    369                         boolean added=_classpath.addComponent(d);
    370                         if (_debug)
    371                             System.err.println((added?"  CLASSPATH+=":"  !")+d);
    372                     }
    373                 }
    374                 else if (subject.toLowerCase().endsWith(".xml"))
    375                 {
    376                     // Config file
    377                     File f=new File(file);
    378                     if (f.exists())
    379                         _xml.add(f.getCanonicalPath());
    380                     if (_debug)
    381                         System.err.println("  ARGS+="+f);
    382                 }
    383                 else if (subject.toLowerCase().endsWith(".class"))
    384                 {
    385                     // Class
    386                     String cn=expand(subject.substring(0,subject.length()-6));
    387                     if (cn!=null&&cn.length()>0)
    388                     {
    389                         if (_debug)
    390                             System.err.println("  CLASS="+cn);
    391                         _classname=cn;
    392                     }
    393                 }
    394                 else if (subject.toLowerCase().endsWith(".path"))
    395                 {
    396                     //classpath (jetty.class.path?) to add to runtime classpath
    397                     String cn=expand(subject.substring(0,subject.length()-5));
    398                     if (cn!=null&&cn.length()>0)
    399                     {
    400                         if (_debug)
    401                             System.err.println("  PATH="+cn);
    402                         _classpath.addClasspath(cn);
    403                     }
    404                 }
    405                 else
    406                 {
    407                     // single JAR file
    408                     File f=new File(file);
    409 		    if(f.exists())
    410 		    {
    411 			String d=f.getCanonicalPath();
    412 			if (!done.containsKey(d))
    413 			{
    414 			    done.put(d,d);
    415 			    boolean added=_classpath.addComponent(d);
    416 			    if (!added)
    417 			    {
    418 				added=_classpath.addClasspath(expand(subject));
    419 				if (_debug)
    420 				    System.err.println((added?"  CLASSPATH+=":"  !")+d);
    421 			    }
    422 			    else if (_debug)
    423 				System.err.println((added?"  CLASSPATH+=":"  !")+d);
    424 		        }
    425                     }
    426                 }
    427             }
    428             catch (Exception e)
    429             {
    430                 System.err.println("on line: '"+line+"'");
    431                 e.printStackTrace();
    432             }
    433         }
    434     }
    435
    436     /* ------------------------------------------------------------ */
    437     public void start(String[] args)
    438     {
    439         ArrayList al=new ArrayList();
    440         for (int i=0; i<args.length; i++)
    441         {
    442             if (args[i]==null)
    443                 continue;
    444             else
    445                 al.add(args[i]);
    446         }
    447         args=(String[])al.toArray(new String[al.size()]);
    448         // set up classpath:
    449         InputStream cpcfg=null;
    450         try
    451         {
    452             Monitor.monitor();
    453
    454             cpcfg=getClass().getClassLoader().getResourceAsStream(_config);
    455             if (_debug)
    456                 System.err.println("config="+_config);
    457             if (cpcfg==null)
    458                 cpcfg=new FileInputStream(_config);
    459             configure(cpcfg,args.length);
    460             File file=new File(System.getProperty("jetty.home"));
    461             String canonical=file.getCanonicalPath();
    462             System.setProperty("jetty.home",canonical);
    463         }
    464         catch (Exception e)
    465         {
    466             e.printStackTrace();
    467             System.exit(1);
    468         }
    469         finally
    470         {
    471             try
    472             {
    473                 cpcfg.close();
    474             }
    475             catch (Exception e)
    476             {
    477                 e.printStackTrace();
    478             }
    479         }
    480         // okay, classpath complete.
    481         System.setProperty("java.class.path",_classpath.toString());
    482         ClassLoader cl=_classpath.getClassLoader();
    483         if (_debug)
    484         {
    485             System.err.println("java.class.path="+System.getProperty("java.class.path"));
    486             System.err.println("jetty.home="+System.getProperty("jetty.home"));
    487             System.err.println("java.io.tmpdir="+System.getProperty("java.io.tmpdir"));
    488             System.err.println("java.class.path="+_classpath);
    489             System.err.println("classloader="+cl);
    490             System.err.println("classloader.parent="+cl.getParent());
    491         }
    492         // Invoke main(args) using new classloader.
    493         Thread.currentThread().setContextClassLoader(cl);
    494         // re-eval the policy now that env is set
    495         try
    496         {
    497             Policy policy=Policy.getPolicy();
    498             if (policy!=null)
    499                 policy.refresh();
    500         }
    501         catch (Exception e)
    502         {
    503             e.printStackTrace();
    504         }
    505         try
    506         {
    507             for (int i=0; i<args.length; i++)
    508             {
    509                 if (args[i]==null)
    510                     continue;
    511                 _xml.add(args[i]);
    512             }
    513             args=(String[])_xml.toArray(args);
    514             //check for override of start class
    515             String mainClass=System.getProperty("jetty.server");
    516             if (mainClass!=null)
    517                 _classname=mainClass;
    518             mainClass=System.getProperty("main.class");
    519             if (mainClass!=null)
    520                 _classname=mainClass;
    521             if (_debug)
    522                 System.err.println("main.class="+_classname);
    523             invokeMain(cl,_classname,args);
    524         }
    525         catch (Exception e)
    526         {
    527             e.printStackTrace();
    528         }
    529     }
    530
    531     /**
    532      * Stop a running jetty instance.
    533      */
    534     public void stop()
    535     {
    536         int _port=Integer.getInteger("STOP.PORT",-1).intValue();
    537         String _key=System.getProperty("STOP.KEY",null);
    538
    539         try
    540         {
    541             if (_port<=0)
    542                 System.err.println("STOP.PORT system property must be specified");
    543             if (_key==null)
    544             {
    545                 _key="";
    546                 System.err.println("STOP.KEY system property must be specified");
    547                 System.err.println("Using empty key");
    548             }
    549
    550             Socket s=new Socket(InetAddress.getByName("127.0.0.1"),_port);
    551             OutputStream out=s.getOutputStream();
    552             out.write((_key+"\r\nstop\r\n").getBytes());
    553             out.flush();
    554             s.close();
    555         }
    556         catch (ConnectException e)
    557         {
    558             System.err.println("ERROR: Not running!");
    559         }
    560         catch (Exception e)
    561         {
    562             e.printStackTrace();
    563         }
    564     }
    565
    566     private void addJars(File dir, Hashtable table, boolean recurse) throws IOException
    567     {
    568         File[] entries=dir.listFiles();
    569
    570         for (int i=0; entries!=null&&i<entries.length; i++)
    571         {
    572             File entry=entries[i];
    573
    574             if (entry.isDirectory()&&recurse)
    575                 addJars(entry,table,recurse);
    576             else
    577             {
    578                 String name=entry.getName().toLowerCase();
    579                 if (name.endsWith(".jar")||name.endsWith(".zip"))
    580                 {
    581                     String jar=entry.getCanonicalPath();
    582                     if (!table.containsKey(jar))
    583                     {
    584                         table.put(jar,jar);
    585                         boolean added=_classpath.addComponent(jar);
    586                         if (_debug)
    587                             System.err.println((added?"  CLASSPATH+=":"  !")+jar);
    588                     }
    589                 }
    590             }
    591         }
    592     }
    593 }