View Javadoc

1   // ========================================================================
2   // Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  package org.eclipse.jetty.start;
14  
15  import java.io.BufferedReader;
16  import java.io.File;
17  import java.io.FileInputStream;
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.InputStreamReader;
21  import java.io.OutputStream;
22  import java.lang.reflect.Constructor;
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.Arrays;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Hashtable;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.StringTokenizer;
40  
41  /*-------------------------------------------*/
42  /**
43   * Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the
44   * start.jar archive. It allows an application to be started with the command "java -jar
45   * start.jar". The behaviour of Main is controlled by the "org/eclipse/start/start.config" file
46   * obtained as a resource or file. This can be overridden with the START system property. The
47   * format of each line in this file is:
48   * 
49   * <PRE>
50   * Each line contains entry in the format:
51   * 
52   *  SUBJECT [ [!] CONDITION [AND|OR] ]*
53   * 
54   * where SUBJECT: 
55   *   ends with ".class" is the Main class to run.
56   *   ends with ".xml" is a configuration file for the command line
57   *   ends with "/" is a directory from which to add all jar and zip files. 
58   *   ends with "/*" is a directory from which to add all unconsidered jar and zip files.
59   *   ends with "/**" is a directory from which to recursively add all unconsidered jar and zip files.
60   *   Containing = are used to assign system properties.
61   *   Containing ~= are used to assign start properties.
62   *   Containing /= are used to assign a canonical path.
63   *   all other subjects are treated as files to be added to the classpath.
64   * 
65   * ${name} is expanded to a start property
66   * $(name) is expanded to either a start property or a system property. 
67   * The start property ${version} is defined as the version of the start.jar
68   * 
69   * Files starting with "/" are considered absolute, all others are relative to
70   * the home directory.
71   * 
72   * CONDITION is one of:
73   *   always
74   *   never
75   *   available classname         - true if class on classpath
76   *   property name               - true if set as start property
77   *   system   name               - true if set as system property
78   *   exists file                 - true if file/dir exists
79   *   java OPERATOR version       - java version compared to literal
80   *   nargs OPERATOR number       - number of command line args compared to literal
81   *   OPERATOR := one of "<",">","<=",">=","==","!="
82   * 
83   * CONTITIONS can be combined with AND OR or !, with AND being the assume
84   * operator for a list of CONDITIONS.
85   * 
86   * Classpath operations are evaluated on the fly, so once a class or jar is
87   * added to the classpath, subsequent available conditions will see that class.
88   * 
89   * The configuration file may be divided into sections with option names like:
90   * [ssl,default]
91   * 
92   * Clauses after a section header will only be included if they match one of the tags in the 
93   * options property.  By default options are set to "default,*" or the OPTIONS property may
94   * be used to pass in a list of tags, eg. :
95   * 
96   *    java -jar start.jar OPTIONS=jetty,jsp,ssl
97   * 
98   * The tag '*' is always appended to the options, so any section with the * tag is always 
99   * applied.
100  * 
101  * </PRE>
102  * 
103  * 
104  */
105 public class Main
106 {
107     private static final String _version = (Main.class.getPackage()!=null && Main.class.getPackage().getImplementationVersion()!=null)
108         ?Main.class.getPackage().getImplementationVersion()
109         :"Unknown";
110         
111     public static boolean DEBUG=false;
112     
113     private Map<String,String> _properties = new HashMap<String,String>();
114     
115     
116     private String _classname=null;
117     private Classpath _classpath=new Classpath();
118     
119     private boolean _showVersions=false;
120     private List<String> _xml=new ArrayList<String>();
121     private Set<String> _activeOptions = new HashSet<String>();
122     private Set<String> _options = new HashSet<String>();
123     private Set<String> _policies = new HashSet<String>();     
124     
125     /*
126     private String _config=System.getProperty("START","org/eclipse/jetty/start/start.config");
127     */
128     
129     public static void main(String[] args)
130     {
131         try
132         {
133             Main main=new Main();
134             List<String> arguments = new ArrayList<String>(Arrays.asList(args));
135             
136             for (int i=0; i<arguments.size(); i++)
137             {
138                 String arg=arguments.get(i);
139                 if (arg.equalsIgnoreCase("--help"))
140                 {
141                     usage();
142                 }
143                 
144                 if (arg.equalsIgnoreCase("--stop"))
145                 {
146                     int port = Integer.parseInt(main.getProperty("STOP.PORT","-1"));
147                     String key = main.getProperty("STOP.KEY", null);
148                     main.stop(port,key);
149                     return;
150                 }
151                 
152                 if (arg.equalsIgnoreCase("--version")||arg.equalsIgnoreCase("-v")||arg.equalsIgnoreCase("-info"))
153                 {
154                     arguments.remove(i--);
155                     main._showVersions=true;
156                 }
157 
158                 if (arg.indexOf('=')>=0)
159                 {
160                     arguments.remove(i--);
161                     String[] assign=arg.split("=",2);
162                     
163                     if (assign.length==2)
164                         main.setProperty(assign[0],assign[1]);
165                     else
166                         main.setProperty(assign[0],null);
167                 }
168             }
169             
170             DEBUG=Boolean.parseBoolean(main.getProperty("DEBUG","false"));
171             main.start(arguments.toArray(new String[arguments.size()]));        
172             
173         }
174         catch (Exception e)
175         {
176             e.printStackTrace();
177             usage();
178         }
179     }
180 
181     private String getSystemProperty(String name)
182     {
183         if ("version".equalsIgnoreCase(name))
184             return _version;
185         if (_properties.containsKey(name))
186             return _properties.get(name);
187         return System.getProperty(name);
188     }
189     
190     private String getProperty(String name)
191     {
192         if ("version".equalsIgnoreCase(name))
193             return _version;
194         
195         return _properties.get(name);
196     }
197     
198     private String getProperty(String name, String dftValue)
199     {
200         if (_properties.containsKey(name))
201             return _properties.get(name);
202         return dftValue;
203     }
204 
205     private void setProperty(String name, String value)
206     {
207         _properties.put(name,value);
208     }
209 
210     private static void usage()
211     {
212         System.err.println("Usage: java -jar start.jar [--help|--stop|--version] [OPTIONS=option,...] [name=value ...] [config ...]");        
213         System.exit(1);
214     }
215 
216     static File getDirectory(String name)
217     {
218         try
219         {
220             if (name!=null)
221             {
222                 File dir=new File(name).getCanonicalFile();
223                 if (dir.isDirectory())
224                 {
225                     return dir;
226                 }
227             }
228         }
229         catch (IOException e)
230         {
231         }
232         return null;
233     }
234 
235     boolean isAvailable(String classname)
236     {
237         try
238         {
239             Class.forName(classname);
240             return true;
241         }
242         catch (NoClassDefFoundError e)
243         {
244             if (DEBUG)
245                 System.err.println(e);
246         }
247         catch (ClassNotFoundException e)
248         {            
249             if (DEBUG)
250                 System.err.println(e);
251         }
252         ClassLoader loader=_classpath.getClassLoader();
253         try
254         {
255             loader.loadClass(classname);
256             return true;
257         }
258         catch (NoClassDefFoundError e)
259         {
260             if (DEBUG)
261                 System.err.println(e);
262         }
263         catch (ClassNotFoundException e)
264         {
265             if (DEBUG)
266                 System.err.println(e);
267         }
268         return false;
269     }
270 
271     public void invokeMain(ClassLoader classloader, String classname, String[] args) throws IllegalAccessException, InvocationTargetException,
272             NoSuchMethodException, ClassNotFoundException
273     {
274         Class invoked_class=null;
275         
276         try
277         {
278             invoked_class=classloader.loadClass(classname);
279         }
280         catch(ClassNotFoundException e)
281         {
282             //ignored
283         }
284         
285         if (DEBUG || _showVersions || invoked_class==null)
286         {
287             if (invoked_class==null)
288                 System.err.println("ClassNotFound: "+classname);
289             else
290                 System.err.println(classname+" "+invoked_class.getPackage().getImplementationVersion());
291             File[] elements = _classpath.getElements();
292             for (int i=0;i<elements.length;i++)
293                 System.err.println("  "+elements[i].getAbsolutePath());
294             if (_showVersions || invoked_class==null)
295             {
296                 System.err.println("OPTIONS: "+_options);
297 	        usage();
298             }
299         }
300 
301         Class[] method_param_types=new Class[1];
302         method_param_types[0]=args.getClass();
303         Method main=null;
304         main=invoked_class.getDeclaredMethod("main",method_param_types);
305         Object[] method_params=new Object[1];
306         method_params[0]=args;
307 
308         main.invoke(null,method_params);
309     }
310 
311     /* ------------------------------------------------------------ */
312     String expand(String s)
313     {
314         int i1=0;
315         int i2=0;
316         while (s!=null)
317         {
318             i1=s.indexOf("$(");
319             if (i1<0)
320                 break;
321             i2=s.indexOf(")",i1+2);
322             if (i2<0)
323                 break;
324             String name=s.substring(i1+2,i2);
325             String property=getSystemProperty(name);
326             s=s.substring(0,i1)+property+s.substring(i2+1);
327         }
328         
329         i1=0;
330         i2=0;
331         while (s!=null)
332         {
333             i1=s.indexOf("${");
334             if (i1<0)
335                 break;
336             i2=s.indexOf("}",i1+2);
337             if (i2<0)
338                 break;
339             String name=s.substring(i1+2,i2);
340             String property=getProperty(name);
341             s=s.substring(0,i1)+property+s.substring(i2+1);
342         }
343         
344         return s;
345     }
346 
347     /* ------------------------------------------------------------ */
348     void configure(InputStream config, int nargs) throws Exception
349     {
350         BufferedReader cfg=new BufferedReader(new InputStreamReader(config,"ISO-8859-1"));
351         Version java_version=new Version(System.getProperty("java.version"));
352         Version ver=new Version();
353         // JAR's already processed
354         Set<String> done=new HashSet<String>();
355         
356         // Initial classpath
357         String classpath=System.getProperty("CLASSPATH");
358         if (classpath!=null)
359         {
360             StringTokenizer tok=new StringTokenizer(classpath,File.pathSeparator);
361             while (tok.hasMoreTokens())
362                 _classpath.addComponent(tok.nextToken());
363         }
364 
365         List<String> section=null;
366         String o=getProperty("OPTIONS","default");
367         _activeOptions.addAll(Arrays.asList((o.toString()+",*").split("[ ,]")));
368         List<String> unsatisfied_options = new ArrayList<String>( _activeOptions );
369         
370         // Handle line by line
371         String line=null;
372         while (true)
373         {
374             line=cfg.readLine();
375             if (line==null)
376                 break;
377             String trim=line.trim();
378             if (trim.length()==0||trim.startsWith("#"))
379                 continue;
380             
381             // handle options
382             if (trim.startsWith("[") && trim.endsWith("]"))
383             {
384                 section = Arrays.asList(trim.substring(1,trim.length()-1).split("[ ,]"));  
385                 _options.addAll(section);
386             }
387             
388             if (section!=null && Collections.disjoint(section,_activeOptions))
389                 continue;
390             if (section!=null)
391                 unsatisfied_options.removeAll(section);
392             try
393             {
394                 StringTokenizer st=new StringTokenizer(line);
395                 String subject=st.nextToken();
396                 boolean expression=true;
397                 boolean not=false;
398                 String condition=null;
399                 // Evaluate all conditions
400                 while (st.hasMoreTokens())
401                 {
402                     condition=st.nextToken();
403                     if (condition.equalsIgnoreCase("!"))
404                     {
405                         not=true;
406                         continue;
407                     }
408                     if (condition.equalsIgnoreCase("OR"))
409                     {
410                         if (expression)
411                             break;
412                         expression=true;
413                         continue;
414                     }
415                     if (condition.equalsIgnoreCase("AND"))
416                     {
417                         if (!expression)
418                             break;
419                         continue;
420                     }
421                     boolean eval=true;
422                     if (condition.equals("true")||condition.equals("always"))
423                     {
424                         eval=true;
425                     }
426                     else if (condition.equals("false")||condition.equals("never"))
427                     {
428                         eval=false;
429                     }
430                     else if (condition.equals("available"))
431                     {
432                         String class_to_check=st.nextToken();
433                         eval=isAvailable(class_to_check);
434                     }
435                     else if (condition.equals("exists"))
436                     {
437                         try
438                         {
439                             eval=false;
440                             File file=new File(expand(st.nextToken()));
441                             eval=file.exists();
442                         }
443                         catch (Exception e)
444                         {
445                             if (DEBUG)
446                                 e.printStackTrace();
447                         }
448                     }
449                     else if (condition.equals("property"))
450                     {
451                         String property=getProperty(st.nextToken());
452                         eval=property!=null&&property.length()>0;
453                     }
454                     else if (condition.equals("system"))
455                     {
456                         String property=System.getProperty(st.nextToken());
457                         eval=property!=null&&property.length()>0;
458                     }
459                     else if (condition.equals("java"))
460                     {
461                         String operator=st.nextToken();
462                         String version=st.nextToken();
463                         ver.parse(version);
464                         eval=(operator.equals("<")&&java_version.compare(ver)<0)||(operator.equals(">")&&java_version.compare(ver)>0)
465                                 ||(operator.equals("<=")&&java_version.compare(ver)<=0)||(operator.equals("=<")&&java_version.compare(ver)<=0)
466                                 ||(operator.equals("=>")&&java_version.compare(ver)>=0)||(operator.equals(">=")&&java_version.compare(ver)>=0)
467                                 ||(operator.equals("==")&&java_version.compare(ver)==0)||(operator.equals("!=")&&java_version.compare(ver)!=0);
468                     }
469                     else if (condition.equals("nargs"))
470                     {
471                         String operator=st.nextToken();
472                         int number=Integer.parseInt(st.nextToken());
473                         eval=(operator.equals("<")&&nargs<number)||(operator.equals(">")&&nargs>number)||(operator.equals("<=")&&nargs<=number)
474                                 ||(operator.equals("=<")&&nargs<=number)||(operator.equals("=>")&&nargs>=number)||(operator.equals(">=")&&nargs>=number)
475                                 ||(operator.equals("==")&&nargs==number)||(operator.equals("!=")&&nargs!=number);
476                     }
477                     else
478                     {
479                         System.err.println("ERROR: Unknown condition: "+condition);
480                         eval=false;
481                     }
482                     expression&=not?!eval:eval;
483                     not=false;
484                 }
485                 String file=expand(subject).replace('/',File.separatorChar);
486                 if (DEBUG)
487                     System.err.println((expression?"T ":"F ")+line);
488                 if (!expression)
489                 {
490                     done.add(file);
491                     continue;
492                 }
493                 
494                 
495                 // Handle the subject
496                 if (subject.indexOf("~=")>0)
497                 {
498                     int i=file.indexOf("~=");
499                     String property=file.substring(0,i);
500                     String value=file.substring(i+2);
501                     if (DEBUG)
502                         System.err.println("  "+property+"~="+value);
503                     setProperty(property,value);
504                 }
505                 if (subject.indexOf("/=")>0)
506                 {
507                     int i=file.indexOf("/=");
508                     String property=file.substring(0,i);
509                     String value=file.substring(i+2);
510                     String canonical=new File(value).getCanonicalPath();
511                     if (DEBUG)
512                         System.err.println("  "+property+"/="+value+"=="+canonical);
513                     setProperty(property,canonical);
514                 }
515                 else if (subject.indexOf("=")>0)
516                 {
517                     int i=file.indexOf("=");
518                     String property=file.substring(0,i);
519                     String value=file.substring(i+1);
520                     if (DEBUG)
521                         System.err.println("  "+property+"="+value);
522                     System.setProperty(property,value);
523                 }
524                 else if (subject.endsWith("/*"))
525                 {
526                     // directory of JAR files - only add jars and zips
527                     // within the directory
528                     File dir=new File(file.substring(0,file.length()-1));
529                     addJars(dir,done,false);
530                 }
531                 else if (subject.endsWith("/**"))
532                 {
533                     //directory hierarchy of jar files - recursively add all
534                     //jars and zips in the hierarchy
535                     File dir=new File(file.substring(0,file.length()-2));
536                     addJars(dir,done,true);
537                 }
538                 else if (subject.endsWith("/"))
539                 {
540                     // class directory
541                     File cd=new File(file);
542                     String d=cd.getCanonicalPath();
543                     if (!done.contains(d))
544                     {
545                         done.add(d);
546                         boolean added=_classpath.addComponent(d);
547                         if (DEBUG)
548                             System.err.println((added?"  CLASSPATH+=":"  !")+d);
549                     }
550                 }
551                 else if (subject.toLowerCase().endsWith(".xml"))
552                 {
553                     // Config file
554                     File f=new File(file);
555                     if (f.exists())
556                         _xml.add(f.getCanonicalPath());
557                     if (DEBUG)
558                         System.err.println("  ARGS+="+f);
559                 }
560                 else if (subject.toLowerCase().endsWith(".class"))
561                 {
562                     // Class
563                     String cn=expand(subject.substring(0,subject.length()-6));
564                     if (cn!=null&&cn.length()>0)
565                     {
566                         if (DEBUG)
567                             System.err.println("  CLASS="+cn);
568                         _classname=cn;
569                     }
570                 }
571                 else if (subject.toLowerCase().endsWith(".path"))
572                 {
573                     //classpath (jetty.class.path?) to add to runtime classpath
574                     String cn=expand(subject.substring(0,subject.length()-5));
575                     if (cn!=null&&cn.length()>0)
576                     {
577                         if (DEBUG)
578                             System.err.println("  PATH="+cn);
579                         _classpath.addClasspath(cn);
580                     }                  
581                 }
582                 else if (subject.toLowerCase().endsWith(".policy"))
583                 {
584                     //policy file to parse
585                     String cn=expand(subject.substring(0,subject.length()));
586                     if (cn!=null&&cn.length()>0)
587                     {
588                         if (DEBUG)
589                             System.err.println("  POLICY="+cn);
590                         _policies.add(cn);
591                     }                  
592                 }
593                 else
594                 {
595                     // single JAR file
596                     File f=new File(file);
597                     if(f.exists())
598                     {
599                         String d=f.getCanonicalPath();
600                         if (!done.contains(d))
601                         {
602                             done.add(d);
603                             boolean added=_classpath.addComponent(d);
604                             if (!added)
605                             {
606                                 added=_classpath.addClasspath(expand(subject));
607                                 if (DEBUG)
608                                     System.err.println((added?"  CLASSPATH+=":"  !")+d);
609                             }
610                             else if (DEBUG)
611                                 System.err.println((added?"  CLASSPATH+=":"  !")+d);
612                         }
613                     }
614                 }
615             }
616             catch (Exception e)
617             {
618                 System.err.println("on line: '"+line+"'");
619                 e.printStackTrace();
620             }
621         }
622 
623         if (unsatisfied_options!=null && unsatisfied_options.size()>0)
624         {
625             System.err.println("Unresolved options: "+unsatisfied_options);
626         }
627     }
628 
629     /* ------------------------------------------------------------ */
630     public void start(String[] args)
631     {
632         // set up classpath:
633         InputStream cpcfg=null;
634         try
635         {
636             int port = Integer.parseInt(getProperty("STOP.PORT","-1"));
637             String key = getProperty("STOP.KEY", null);
638             
639             Monitor.monitor(port,key);
640 
641             String config=getProperty("START","org/eclipse/jetty/start/start.config");
642             if (DEBUG)
643             {
644                 System.err.println("config="+config);
645                 System.err.println("properties="+_properties);
646             }
647             cpcfg=getClass().getClassLoader().getResourceAsStream(config);
648             if (cpcfg==null)
649                 cpcfg=new FileInputStream(config);
650             
651             configure(cpcfg,args.length);
652             
653             String jetty_home=System.getProperty("jetty.home");
654             if (jetty_home!=null)
655             {
656                 File file=new File(jetty_home);
657                 String canonical=file.getCanonicalPath();
658                 System.setProperty("jetty.home",canonical);
659             }
660             
661         }
662         catch (Exception e)
663         {
664             e.printStackTrace();
665             System.exit(1);
666         }
667         finally
668         {
669             try
670             {
671                 cpcfg.close();
672             }
673             catch (Exception e)
674             {
675                 e.printStackTrace();
676             }
677         }
678         // okay, classpath complete.
679         System.setProperty("java.class.path",_classpath.toString());
680         ClassLoader cl=_classpath.getClassLoader();
681         if (DEBUG)
682         {
683             System.err.println("java.class.path="+System.getProperty("java.class.path"));
684             System.err.println("jetty.home="+System.getProperty("jetty.home"));
685             System.err.println("java.io.tmpdir="+System.getProperty("java.io.tmpdir"));
686             System.err.println("java.class.path="+_classpath);
687             System.err.println("classloader="+cl);
688             System.err.println("classloader.parent="+cl.getParent());
689         }
690         // Invoke main(args) using new classloader.
691         Thread.currentThread().setContextClassLoader(cl);
692         // re-eval the policy now that env is set
693         try
694         {
695         	if ( _activeOptions.contains("secure") )
696         	{
697         	    Class jettyPolicy = cl.loadClass( "org.eclipse.jetty.policy.JettyPolicy" );
698         	    Constructor c = jettyPolicy.getConstructor( new Class[] { Set.class, Map.class } );
699         	    Object policyClass = c.newInstance( _policies, _properties );
700         	    
701         		Policy.setPolicy( (Policy)policyClass );
702         		System.setSecurityManager( new SecurityManager() );
703         	}
704         	else
705         	{
706         		Policy policy=Policy.getPolicy();
707         		if (policy!=null)
708         			policy.refresh();
709         	}
710         }
711         catch (Exception e)
712         {
713             e.printStackTrace();
714         }
715         try
716         {
717             for (int i=0; i<args.length; i++)
718             {
719                 if (args[i]==null)
720                     continue;
721                 _xml.add(args[i]);
722             }
723             args=(String[])_xml.toArray(args);
724             //check for override of start class
725             String mainClass=System.getProperty("jetty.server");
726             if (mainClass!=null)
727                 _classname=mainClass;
728             mainClass=System.getProperty("main.class");
729             if (mainClass!=null)
730                 _classname=mainClass;
731             if (DEBUG)
732                 System.err.println("main.class="+_classname);
733             invokeMain(cl,_classname,args);
734         }
735         catch (Exception e)
736         {
737             e.printStackTrace();
738         }
739     }
740 
741     /**
742      * Stop a running jetty instance.
743      */
744     public void stop(int port,String key)
745     {
746         int _port=port;
747         String _key=key;
748 
749         try
750         {
751             if (_port<=0)
752                 System.err.println("STOP.PORT system property must be specified");
753             if (_key==null)
754             {
755                 _key="";
756                 System.err.println("STOP.KEY system property must be specified");
757                 System.err.println("Using empty key");
758             }
759 
760             Socket s=new Socket(InetAddress.getByName("127.0.0.1"),_port);
761             OutputStream out=s.getOutputStream();
762             out.write((_key+"\r\nstop\r\n").getBytes());
763             out.flush();
764             s.close();
765         }
766         catch (ConnectException e)
767         {
768             System.err.println("ERROR: Not running!");
769         }
770         catch (Exception e)
771         {
772             e.printStackTrace();
773         }
774     }
775 
776     private void addJars(File dir, Set<String> table, boolean recurse) throws IOException
777     {
778         File[] entries=dir.listFiles();
779 
780         for (int i=0; entries!=null&&i<entries.length; i++)
781         {
782             File entry=entries[i];
783 
784             if (entry.isDirectory()&&recurse)
785                 addJars(entry,table,recurse);
786             else
787             {
788                 String name=entry.getName().toLowerCase();
789                 if (name.endsWith(".jar")||name.endsWith(".zip"))
790                 {
791                     String jar=entry.getCanonicalPath();
792                     if (!table.contains(jar))
793                     {
794                         table.add(jar);
795                         boolean added=_classpath.addComponent(jar);
796                         if (DEBUG)
797                             System.err.println((added?"  CLASSPATH+=":"  !")+jar);
798                     }
799                 }
800             }
801         }
802     }
803 }