001    /* ActivationGroupDesc.java -- the RMI activation group descriptor
002       Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.rmi.activation;
041    
042    import gnu.java.rmi.activation.DefaultActivationGroup;
043    
044    import java.io.Serializable;
045    import java.rmi.MarshalledObject;
046    import java.util.Arrays;
047    import java.util.Enumeration;
048    import java.util.Iterator;
049    import java.util.Properties;
050    import java.util.TreeSet;
051    import java.util.zip.Adler32;
052    
053    /**
054     * Contains information, necessary to create of recreate the activation objects.
055     * The group descriptor contains:
056     * <ul>
057     * <li>The name of the group's class. This class is derived from the
058     * {@link ActivationGroup}.</li>
059     * <li>The group class code location.</li>
060     * <li>The marshalled object that contains the group specific initialization
061     * information</li>
062     * </ul>
063     * The groups are created by the {@link ActivationGroup#createGroup} method that
064     * expectes the group class to have the two parameter constructor, the first
065     * parameter being the {@link ActivationGroupID} and the second the
066     * {@link MarshalledObject}.
067     *
068     * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub)
069     */
070    public final class ActivationGroupDesc
071        implements Serializable
072    {
073      /**
074       * Contains the startup options for the {@link ActivationGroup}
075       * implementations. Allows to override system properties and specify other
076       * options for the implementation groups.
077       *
078       * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub)
079       */
080      public static class CommandEnvironment
081          implements Serializable
082      {
083    
084        /**
085         * Use the SVUID for interoperability.
086         */
087        static final long serialVersionUID = 6165754737887770191L;
088    
089        /**
090         * The zero size string array used as argv value when null is passed.
091         */
092        private static final String[] NO_ARGS = new String[0];
093    
094        /**
095         * The path to the java executable (or null for using default jre).
096         */
097        final String command;
098    
099        /**
100         * The extra parameters (may be empty array but never null).
101         */
102        final String[] options;
103    
104        /**
105         * Create the new command environment.
106         *
107         * @param commandPatch the full path (and name) to the java executable of
108         *          null for using the default executable.
109         * @param args extra options that will be used when creating the activation
110         *          group. Null has the same effect as the empty list.
111         */
112        public CommandEnvironment(String commandPatch, String[] args)
113        {
114          command = commandPatch;
115          if (args != null)
116            options = args;
117          else
118            options = NO_ARGS;
119        }
120    
121        /**
122         * Get the path to the java executable.
123         *
124         * @return the path to the java executable or null for using the default
125         * jre.
126         */
127        public String getCommandPath()
128        {
129          return command;
130        }
131    
132        /**
133         * Get the additional command options.
134         *
135         * @return the command options array, may be empty string
136         */
137        public String[] getCommandOptions()
138        {
139          return options;
140        }
141    
142        /**
143         * Compare for content equality.
144         */
145        public boolean equals(Object obj)
146        {
147          if (obj instanceof CommandEnvironment)
148            {
149              CommandEnvironment that = (CommandEnvironment) obj;
150    
151              if (command == null || that.command == null)
152                {
153                  // Use direct comparison if null is involved.
154                  if (command != that.command)
155                    return false;
156                }
157              else
158                {
159                  // Use .equals if null is not involved.
160                  if (! this.command.equals(that.command))
161                    return false;
162                }
163    
164              return Arrays.equals(options, that.options);
165            }
166          else
167            return false;
168        }
169    
170        /**
171         * Get the hash code.
172         */
173        public int hashCode()
174        {
175          int h = command == null ? 0 : command.hashCode();
176          for (int i = 0; i < options.length; i++)
177            h ^= options[i].hashCode();
178    
179          return h;
180        }
181      }
182    
183      /**
184       * Use the SVUID for interoperability.
185       */
186      static final long serialVersionUID = - 4936225423168276595L;
187    
188      /**
189       * The group class name or null for the default group class implementation.
190       */
191      final String className;
192    
193      /**
194       * The group class download location URL (codebase), ignored by the
195       * default implementation.
196       */
197      final String location;
198    
199      /**
200       * The group initialization data.
201       */
202      final MarshalledObject<?> data;
203    
204      /**
205       * The path to the group jre and the parameters of this jre, may be
206       * null for the default jre.
207       */
208      final ActivationGroupDesc.CommandEnvironment env;
209    
210      /**
211       * The properties that override the system properties.
212       */
213      final Properties props;
214    
215      /**
216       * The cached hash code.
217       */
218      transient long hash;
219    
220      /**
221       * Create the new activation group descriptor that will use the default
222       * activation group implementation with the given properties and
223       * environment.
224       *
225       * @param aProperties the properties that override the system properties
226       * @param environment the command line (and parameters), indicating, where to
227       *          find the jre executable and with that parameters to call it. May
228       *          be null if the default executable should be used. In this case,
229       *          the activation group with the null name (the system default group)
230       *          will be created.
231       */
232      public ActivationGroupDesc(Properties aProperties,
233                                 ActivationGroupDesc.CommandEnvironment environment)
234      {
235        this(DefaultActivationGroup.class.getName(), null, null, aProperties,
236             environment);
237      }
238    
239      /**
240       * Create the new activation group descriptor.
241       *
242       * @param aClassName the name of the group implementation class. The null
243       *          value indicates the default implementation.
244       * @param aLocation the location, from where the group implementation class
245       *          should be loaded (ignored for the system default implementation).
246       * @param aData the group intialization data
247       * @param aProperties the properties that will override the system properties
248       *          of the new group. These properties will be translated into -D
249       *          options.
250       * @param environment the record, containing path to the jre executable and
251       *          start options for the jre or null for using the default jre and
252       *          options.
253       */
254      public ActivationGroupDesc(String aClassName, String aLocation,
255                                 MarshalledObject<?> aData, Properties aProperties,
256                                 ActivationGroupDesc.CommandEnvironment environment)
257      {
258        className = aClassName;
259        location = aLocation;
260        data = aData;
261        props = aProperties;
262        env = environment;
263      }
264    
265      /**
266       * Get the activation group class name.
267       *
268       * @return the activation group class name (null for default implementation)
269       */
270      public String getClassName()
271      {
272        return className;
273      }
274    
275      /**
276       * Get the location, from where the group class will be loaded
277       *
278       * @return the location, from where the implementation should be loaded (null
279       *         for the default implementation)
280       */
281      public String getLocation()
282      {
283        return location;
284      }
285    
286      /**
287       * Get the group intialization data.
288       *
289       * @return the group intialization data in the marshalled form.
290       */
291      public MarshalledObject<?> getData()
292      {
293        return data;
294      }
295    
296      /**
297       * Get the overridded system properties.
298       *
299       * @return the overridden group system properties.
300       */
301      public Properties getPropertyOverrides()
302      {
303        return props;
304      }
305    
306      /**
307       * Get the group command environment, containing path to the jre executable
308       * and startup options.
309       *
310       * @return the command environment or null if the default environment should
311       *         be used.
312       */
313      public ActivationGroupDesc.CommandEnvironment getCommandEnvironment()
314      {
315        return env;
316      }
317    
318      /**
319       * Compare for the content equality.
320       */
321      public boolean equals(Object obj)
322      {
323        if (obj instanceof ActivationGroupDesc)
324          {
325            ActivationGroupDesc that = (ActivationGroupDesc) obj;
326    
327            // Ensure the hashcodes are computed.
328            if (hash == 0)
329              hashCode();
330            if (that.hash == 0)
331              that.hashCode();
332    
333            // We compare the hash fields as they are type long rather than int.
334            if (hash != that.hash)
335              return false;
336    
337            if (! eq(className, that.className))
338              return false;
339            if (! eq(data, that.data))
340              return false;
341            if (! eq(env, that.env))
342              return false;
343            if (! eq(location, that.location))
344              return false;
345    
346            // Compare the properties.
347            if (eq(props, that.props))
348              return true;
349    
350            if (props.size() != that.props.size())
351              return false;
352    
353            Enumeration en = props.propertyNames();
354            Object key, value;
355    
356            while (en.hasMoreElements())
357              {
358                key = en.nextElement();
359                if (! that.props.containsKey(key))
360                  return false;
361                if (! eq(props.get(key), that.props.get(key)))
362                  return false;
363              }
364            return true;
365          }
366        else
367          return false;
368      }
369    
370      /**
371       * Compare for direct equality if one or both parameters are null, otherwise
372       * call .equals.
373       */
374      static boolean eq(Object a, Object b)
375      {
376        if (a == null || b == null)
377          return a == b;
378        else
379          return a.equals(b);
380      }
381    
382      /**
383       * Return the hashcode.
384       */
385      public int hashCode()
386      {
387        if (hash==0)
388          {
389            // Using Adler32 - the hashcode is cached, will be computed only
390            // once and due need to scan properties is the expensive operation
391            // anyway. Reliability is more important.
392            Adler32 adler = new Adler32();
393            if (className!=null)
394              adler.update(className.getBytes());
395            if (data!=null)
396              adler.update(data.hashCode());
397            if (env!=null)
398              adler.update(env.hashCode());
399            if (location!=null)
400              adler.update(location.getBytes());
401            if (props!=null)
402              {
403                Enumeration en = props.propertyNames();
404    
405                // Using the intermediate sorted set to ensure that the
406                // properties are sorted.
407                TreeSet pr = new TreeSet();
408    
409                Object key;
410                Object value;
411                while (en.hasMoreElements())
412                  {
413                    key = en.nextElement();
414                    if (key!=null)
415                      pr.add(key);
416                  }
417    
418                Iterator it = pr.iterator();
419                while (it.hasNext())
420                  {
421                    key = it.next();
422                    value = props.get(key);
423                    adler.update(key.hashCode());
424                    if (value!=null)
425                      adler.update(value.hashCode());
426                  }
427              }
428              hash = adler.getValue();
429            }
430        return (int) hash;
431      }
432    
433    }