001    /*
002      Copyright (C) 2001 Lionel Seinturier
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser Generaly Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
017    
018    package org.objectweb.jac.core.dist;
019    
020    import java.net.InetAddress;
021    import java.util.Arrays;
022    import java.util.Hashtable;
023    import org.apache.log4j.Logger;
024    
025    /**
026     * Distd is an abstract class for all daemon objects.
027     * Its implementation is protocol dependent (eg CORBA or RMI).
028     *
029     * This is an abstract class that needs to be subclassed
030     * (see org.objectweb.jac.dist.rmi.RMIDistd).
031     *
032     * Daemons hold containers which themselves hold remote objects.
033     *
034     * @see org.objectweb.jac.core.dist.Distd#init()
035     * @see org.objectweb.jac.core.dist.Distd#newContainer(String)
036     * @see org.objectweb.jac.core.dist.Distd#newContainer(String,String)
037     * @see org.objectweb.jac.core.dist.Distd#run()
038     *
039     * @author <a href="http://www-src.lip6.fr/homepages/Lionel.Seinturier/index-eng.html">Lionel Seinturier</a>
040     */
041     
042    abstract public class Distd {
043        static final Logger logger = Logger.getLogger("dist");
044    
045        /** The number of bytes that have been transmitted to the output of
046            this deamon. */
047        public static long outputCount = 0;
048        /** The number of bytes that have been transmitted to the input of
049            this deamon. */
050        public static long inputCount = 0;
051    
052        /**
053         * Store a string array into a hashtable.
054         *
055         * @param strs  the string array
056         * @return      the hashtable
057         */
058        public static Hashtable stringArrayToHashtable(String[] strs) {
059       
060            Hashtable ret = new Hashtable();
061          
062            for ( int i=0 ; i < strs.length ; i++ ) {
063                ret.put( strs[i], "" );
064            }
065          
066            return ret;
067        }
068    
069        /**
070         * This abstract method initializes the underlying communication
071         * protocol.  
072         */
073        abstract public void init();
074    
075        /**
076         * This abstract method creates a new container.
077         *
078         * @param name  the container name
079         * @return      the container reference
080         */
081        abstract protected RemoteContainer newContainer(String name) 
082            throws Exception;
083    
084        /**
085         * This abstract method creates a new container and
086         * instantiates a given class.
087         *
088         * @param name       the container name
089         * @param className  the name of the class to instantiate
090         * @return           the container reference
091         */
092        abstract protected RemoteContainer newContainer(String name, String className) 
093            throws Exception;
094    
095        /**
096         * This abstract method enters the event loop of the underlying
097         * communication protocol.
098         */
099        abstract public void run();
100    
101        /**
102         * The is the main constructor of Distd.
103         *
104         * @param args  command line arguments
105         */
106        public Distd(String[] args) {
107    
108            /** Parse command line arguments */
109            Hashtable hArgs = parseArguments(args, flags, options);
110          
111          
112            /** Initialize the underlying communication protocol */
113            init();
114    
115            try {
116       
117                /** Check the flags and options */
118                verbose =
119                    (hArgs.get("v") == null && hArgs.get("-verbose") == null) ?
120                    false : true ;
121    
122                Object oNames = hArgs.get("_files");
123                if (oNames == null)
124                    usage();
125             
126                String[] names = (String[])oNames;
127                if (names.length != 1)
128                    usage();
129                String name = names[0];
130          
131          
132                /** Instantiates a container. */
133                RemoteContainer container = null;
134    
135                if (getClass().getClassLoader() instanceof DistdClassLoader) {
136                    logger.debug("DistdClassLoader...");
137                    Object classRepositoryName = hArgs.get("r");
138                    if (classRepositoryName == null) 
139                        classRepositoryName = hArgs.get("-repository");
140                    if (classRepositoryName == null)
141                        classRepositoryName = "s0";
142                    classRepositoryName = getFullHostName((String)classRepositoryName);
143                    DistdClassLoader.classRepositoryName = (String)classRepositoryName;
144                    referenceContainerName = (String) classRepositoryName;
145                } else {
146                    //System.out.println("NOT DistdClassLoader");
147                }
148             
149                Object className = hArgs.get("i");
150                if (className == null)
151                    className = hArgs.get("-init");
152             
153                // we try 20 times to launch it because protocol
154                // initialization may take some time
155                int ok = 20;
156                while (ok>0) {
157                    try {
158                        if (className == null) {
159                            container = newContainer(name);
160                        } else {
161                            container = newContainer(name, (String)className);
162                        }
163                        logger.info("--- Distd started.");
164                        ok = 0;
165                    } catch(Exception e) {
166                        ok--;
167                        if (ok == 0) {
168                            logger.error("ERROR: distd did not start",e);
169                        } else {
170                            logger.debug("--- Distd starting try, "+ok+" left...");
171                            Thread.sleep(200);
172                        }
173                    }
174                }
175    
176                containers.put(name, container);
177                localContainerName = container.getName();
178             
179                /** Enter the daemon event loop. */
180                run();
181    
182                if (getClass().getClassLoader() instanceof DistdClassLoader) {
183                    ((DistdClassLoader)getClass().getClassLoader())
184                        .bootstrapping = false;
185                }
186    
187            } catch(Exception e) { 
188                logger.error("Distd "+Arrays.asList(args),e); 
189            }
190        }
191    
192        /** Store the reference container. */
193        public static String referenceContainerName = null;
194    
195        /**
196         * Get the full host name from an incomplete host name.<p>
197         *
198         * For instance, if the local host name is h1:<p>
199         *
200         * <ul>
201         *   <li>"s0" → "//h1/s0"</li>
202         *   <li> "//localhost/s1" → "//h1/s0</li>
203         *   <li>"//h2/s0" → "//h2/s0"</li>
204         *   <li>"\\h2\s0" → "//h2/s0" (for Windows command line compatibility)</li>
205         * </ul>
206         * 
207         * @param name the incomplete host name
208         * @return the complete host name 
209         */   
210        public static String getFullHostName(String name) {
211            String fullname = "";
212            name = name.replace('\\','/');
213            if (!name.startsWith("//")) {
214                try {
215                    fullname = "//"+InetAddress.getLocalHost().getHostName()+"/"+name;
216                } catch(Exception e) {
217                    logger.error("getFullHostName "+name,e); 
218                }
219            }
220            if (name.startsWith("//localhost/")) {
221                try {
222                    fullname = "//" + InetAddress.getLocalHost().getHostName() + 
223                        "/" + name.substring(12);
224                } catch(Exception e) { 
225                    logger.error("getFullHostName "+name,e); 
226                }
227            }
228            if (fullname.equals(""))
229                fullname = name;
230            return fullname;
231        }
232    
233        /** Store the reference container name. */
234        //public static String reference_container_name = null;
235       
236        /** Returns the reference container. */
237        //public static RemoteContainer getReferenceContainer () {
238        //   //      System.out.println ( "resolving container " + reference_container_name );
239        //   if ( reference_container == null && reference_container_name != null ) {
240        //      reference_container = RemoteContainer.resolve( reference_container_name );
241        //   }
242        //   return reference_container;
243        //}
244    
245        /** Store the local container name. */
246        protected static String localContainerName = "";
247       
248        /** Get the local container name. */ 
249        public static String getLocalContainerName () {
250            return localContainerName;
251        }
252       
253        /** Registered flags. */
254        protected static final String[] flags = {"v","-verbose"};
255       
256        /** Registered options. */
257        protected static final String[] options = {"i","-init","r","-repository"};
258    
259        /** verbose tells whether information message should be printed or not */
260        protected static boolean verbose = false;
261    
262    
263        /** Display command line arguments. */
264       
265        protected static void usage() {
266            System.out.println(
267                "Usage: java org.objectweb.jac.core.dist.Distd [options] name\n" +
268                "\n" +
269                "-v --verbose: display system informations\n" +
270                "-i --init classname: create an instance of classname\n" +
271                "-r --repository sitename: the site where the classes repository is located (the loader will load the classes from this site if possible -- else from the local file system)\n" +
272                "name: the daemon identifier"
273            );
274            System.exit(1);
275        }
276    
277        /**
278         * Parse command line arguments composed of flags, options and files.
279         *
280         * @param args      command line arguments
281         * @param flags     registered flags (e.g. -verbose, -quiet, etc.)
282         * @param options   registered options (e.g. -d classes, etc.)
283         * @return          a hashtable containing 3 types of entries:
284         *                 one for each flag and option, and one for the files.
285         *                 If an unregistered flag or option is encountered the
286         *                 usage method is called.
287         *                 The value associated to the entries is a empty string
288         *                 for flags,
289         *                 and the option value (e.g. classes/) for options.
290         *                 Files are stored with an entry whose key is "_files",
291         *                 and whose value is an array of strings.
292         */
293        protected static Hashtable parseArguments(String[] args, 
294                                                  String[] flags, 
295                                                  String[] options) {
296          
297            /** Store flags and options in hashtables */
298          
299            Hashtable hFlags = stringArrayToHashtable(flags);
300            Hashtable hOptions = stringArrayToHashtable(options);
301          
302          
303            /** Scan the arguments */
304          
305            Hashtable result = new Hashtable();
306          
307            String flagOrOption;
308            Object flag, option;
309          
310            for (int i=0; i<args.length; i++) {
311                if (args[i].startsWith("-")) {
312                    // This is either a flag or an option
313                    flagOrOption = args[i].substring(1);
314                    flag = hFlags.get(flagOrOption);
315                    option = hOptions.get(flagOrOption);
316                
317                    if (flag != null) {
318                        result.put(flagOrOption, "");
319                    } else if (option != null) {
320                        // Move to the value of the option
321                        i++;
322                        if (i >= args.length) { 
323                            usage();
324                        }
325                        result.put(flagOrOption,args[i]);
326                    } else {
327                        usage();
328                    }
329                } else {
330                    // This is neither a flag, nor an option.
331                    // This must be the begining of file names.
332                    int numberOfFiles = args.length - i;
333                    String[] files = new String[numberOfFiles];
334                    System.arraycopy(args, i, files, 0, numberOfFiles);
335                
336                    result.put("_files", files);
337                
338                    return result;
339                }
340            }
341          
342            return result;
343        }
344    
345        /** Containers hold by the current daemon. */
346        protected static Hashtable containers = new Hashtable();
347    
348       
349        /**
350         * Test whether the daemon contains a given container.
351         *
352         * @param container the container's name
353         * @return true if the container is contained in the current daemon
354         */    
355        public static boolean containsContainer(RemoteContainer container) {
356            return containers.values().contains(container);
357        }
358    }