001/* ActivationGroupDesc.java -- the RMI activation group descriptor
002   Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.rmi.activation;
041
042import gnu.java.rmi.activation.DefaultActivationGroup;
043
044import java.io.Serializable;
045import java.rmi.MarshalledObject;
046import java.util.Arrays;
047import java.util.Enumeration;
048import java.util.Iterator;
049import java.util.Properties;
050import java.util.TreeSet;
051import 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 */
070public 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}