You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by da...@apache.org on 2006/04/18 08:44:21 UTC

svn commit: r394860 [2/3] - in /geronimo/branches/1.1: applications/console-standard/src/java/org/apache/geronimo/console/configmanager/ modules/connector-builder/src/test/org/apache/geronimo/connector/deployment/ modules/deploy-tool/src/java/org/apach...

Added: geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleMonitor.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleMonitor.java?rev=394860&view=auto
==============================================================================
--- geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleMonitor.java (added)
+++ geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleMonitor.java Mon Apr 17 23:44:17 2006
@@ -0,0 +1,104 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.kernel.config;
+
+import org.apache.geronimo.kernel.repository.Artifact;
+
+/**
+ * This interface can be used to monitor the progress of an operation on the
+ * configuration manager.  Typically, the monitor will receive a number of
+ * calls to addConfiguration as the configuration manager decides which
+ * configurations will be effected by the operations.  This is followed by a
+ * call to loading, starting, stopping or unloading and then a call to
+ * succeeded or failed for each configuration added.
+ *
+ *
+ * The one notable exception to this is the load operation which calls
+ * addConfiguration and and immediately follows it with a reading and then
+ * succeeded or failed.  This is because the load operation needs to read each
+ * configuration to determine which additional configurations will need to be
+ * loaded.
+ *
+ * When an operation fails, the failed method is called with the cause.  The
+ * configuration manager normally will follow the failure with compensating
+ * actions to bring the server back to the original state.  For example, if it
+ * loaded a configuration, it will unload it.  Each of the compensating
+ * actions will cause events to be fired.
+ *
+ * When the, operation is completed, the finished method will be called.  This
+ *  should be called event if the operation ultimately fails and throws an
+ * exception.  It is recommended that you do not rely the finished method to
+ * be called in the case of an Exception as there are cases that will cause
+ * the configuration manager to immediately return without notification (such
+ * as an AssertionError).
+ * @version $Rev$ $Date$
+ */
+public interface LifecycleMonitor {
+    /**
+     * Adds a configuration to be monitored.
+     * @param configurationId the configuration identifier
+     */
+    void addConfiguration(Artifact configurationId);
+
+    /**
+     * The configuration manager has started reading the specified configuration.
+     * @param configurationId the configuration identifier
+     */
+    void reading(Artifact configurationId);
+
+    /**
+     * The configuration manager has begun loading the specified configuration.
+     * @param configurationId the configuration identifier
+     */
+    void loading(Artifact configurationId);
+
+    /**
+     * The configuration manager has begun starting the specified configuration.
+     * @param configurationId the configuration identifier
+     */
+    void starting(Artifact configurationId);
+
+    /**
+     * The configuration manager has begun stopping the specified configuration.
+     * @param configurationId the configuration identifier
+     */
+    void stopping(Artifact configurationId);
+
+    /**
+     * The configuration manager has begun unloading the specified configuration.
+     * @param configurationId the configuration identifier
+     */
+    void unloading(Artifact configurationId);
+
+    /**
+     * The previous operation on the specified configuration has completed successfully.
+     * @param configurationId the configuration identifier
+     */
+    void succeeded(Artifact configurationId);
+
+    /**
+     * The previous operation on the specified configuration has failed due to the specified exception.
+     * @param configurationId the configuration identifier
+     */
+    void failed(Artifact configurationId, Throwable cause);
+
+    /**
+     * The operation on the configuration manager has finished.
+     */
+    void finished();
+}
+

Added: geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleResults.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleResults.java?rev=394860&view=auto
==============================================================================
--- geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleResults.java (added)
+++ geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/LifecycleResults.java Mon Apr 17 23:44:17 2006
@@ -0,0 +1,288 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.kernel.config;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedHashMap;
+import java.util.Set;
+import java.util.Map;
+import java.util.Collections;
+import java.io.Serializable;
+
+import org.apache.geronimo.kernel.repository.Artifact;
+
+/**
+ * This class contains the results of a lifecycle operation on the configuation manager.
+ * @version $Rev$ $Date$
+ */
+public class LifecycleResults implements Serializable {
+    private static final long serialVersionUID = 4660197333193740244L;
+    private final Set loaded = new LinkedHashSet();
+    private final Set unloaded = new LinkedHashSet();
+    private final Set started = new LinkedHashSet();
+    private final Set stopped = new LinkedHashSet();
+    private final Set restarted = new LinkedHashSet();
+    private final Set reloaded = new LinkedHashSet();
+    private final Map failed = new LinkedHashMap();
+
+    /**
+     * Was the specified configuration loaded.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration was loaded during the lifecycle operation
+     */
+    public boolean wasLoaded(Artifact configurationId) {
+        return loaded.contains(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations loaded.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Set getLoaded() {
+        return Collections.unmodifiableSet(loaded);
+    }
+
+    /**
+     * Adds a configuration the set of loaded configurations.
+     * @param configurationId the configuration identifiers (Artifact)
+     */
+    public void addLoaded(Artifact configurationId) {
+        loaded.add(configurationId);
+    }
+
+    /**
+     * Clears the existing loaded set and add alls the specified configurations to the set
+     * @param loaded the configuration identifiers (Artifact)
+     */
+    public void setLoaded(Set loaded) {
+        this.loaded.clear();
+        this.loaded.addAll(loaded);
+    }
+
+    /**
+     * Was the specified configuration unloaded.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration was unloaded during the lifecycle operation
+     */
+    public boolean wasUnloaded(Artifact configurationId) {
+        return unloaded.contains(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations unloaded.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Set getUnloaded() {
+        return Collections.unmodifiableSet(unloaded);
+    }
+
+    /**
+     * Adds a configuration the set of unloaded configurations.
+     * @param configurationId the configuration identifiers (Artifact)
+     */
+    public void addUnloaded(Artifact configurationId) {
+        unloaded.add(configurationId);
+    }
+
+    /**
+     * Clears the existing unloaded set and add alls the specified configurations to the set
+     * @param unloaded the configuration identifiers (Artifact)
+     */
+    public void setUnloaded(Set unloaded) {
+        this.unloaded.clear();
+        this.unloaded.addAll(unloaded);
+    }
+
+    /**
+     * Was the specified configuration started.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration was started during the lifecycle operation
+     */
+    public boolean wasStarted(Artifact configurationId) {
+        return started.contains(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations started.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Set getStarted() {
+        return Collections.unmodifiableSet(started);
+    }
+
+    /**
+     * Adds a configuration the set of started configurations.
+     * @param configurationId the configuration identifiers (Artifact)
+     */
+    public void addStarted(Artifact configurationId) {
+        started.add(configurationId);
+    }
+
+    /**
+     * Clears the existing started set and add alls the specified configurations to the set
+     * @param started the configuration identifiers (Artifact)
+     */
+    public void setStarted(Set started) {
+        this.started.clear();
+        this.started.addAll(started);
+    }
+
+    /**
+     * Was the specified configuration stopped.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration was stopped during the lifecycle operation
+     */
+    public boolean wasStopped(Artifact configurationId) {
+        return stopped.contains(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations stopped.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Set getStopped() {
+        return Collections.unmodifiableSet(stopped);
+    }
+
+    /**
+     * Adds a configuration the set of stopped configurations.
+     * @param configurationId the configuration identifiers (Artifact)
+     */
+    public void addStopped(Artifact configurationId) {
+        stopped.add(configurationId);
+    }
+
+    /**
+     * Clears the existing stopped set and add alls the specified configurations to the set
+     * @param stopped the configuration identifiers (Artifact)
+     */
+    public void setStopped(Set stopped) {
+        this.stopped.clear();
+        this.stopped.addAll(stopped);
+    }
+
+    /**
+     * Was the specified configuration restarted.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration was restarted during the lifecycle operation
+     */
+    public boolean wasRestarted(Artifact configurationId) {
+        return restarted.contains(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations restarted.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Set getRestarted() {
+        return Collections.unmodifiableSet(restarted);
+    }
+
+    /**
+     * Adds a configuration the set of restarted configurations.
+     * @param configurationId the configuration identifiers (Artifact)
+     */
+    public void addRestarted(Artifact configurationId) {
+        restarted.add(configurationId);
+    }
+
+    /**
+     * Clears the existing restarted set and add alls the specified configurations to the set
+     * @param restarted the configuration identifiers (Artifact)
+     */
+    public void setRestarted(Set restarted) {
+        this.restarted.clear();
+        this.restarted.addAll(restarted);
+    }
+
+    /**
+     * Was the specified configuration reloaded.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration was reloaded during the lifecycle operation
+     */
+    public boolean wasReloaded(Artifact configurationId) {
+        return reloaded.contains(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations reloaded.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Set getReloaded() {
+        return Collections.unmodifiableSet(reloaded);
+    }
+
+    /**
+     * Adds a configuration the set of reloaded configurations.
+     * @param configurationId the configuration identifiers (Artifact)
+     */
+    public void addReloaded(Artifact configurationId) {
+        reloaded.add(configurationId);
+    }
+
+    /**
+     * Clears the existing reloaded set and add alls the specified configurations to the set
+     * @param reloaded the configuration identifiers (Artifact)
+     */
+    public void setReloaded(Set reloaded) {
+        this.reloaded.clear();
+        this.reloaded.addAll(reloaded);
+    }
+
+    /**
+     * Was the specified configuration failed the operation and threw an exception.
+     * @param configurationId the configuration identifier
+     * @return true if the specified configuration failed the operation and threw an exception during the lifecycle operation
+     */
+    public boolean wasFailed(Artifact configurationId) {
+        return failed.containsKey(configurationId);
+    }
+
+    /**
+     * Gets the exception that caused the operation on the specified configuration to fail.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Throwable getFailedCause(Artifact configurationId) {
+        return (Throwable) failed.get(configurationId);
+    }
+
+    /**
+     * Gets the configuration identifiers (Artifact) of the configurations that failed the operation and threw an exception.
+     * @return the configuration identifiers (Artifact)
+     */
+    public Map getFailed() {
+        return Collections.unmodifiableMap(failed);
+    }
+
+    /**
+     * Adds a configuration and associated causal exception to this result.
+     * @param configurationId the configuration identifiers (Artifact)
+     * @param cause the exception that caused the operation on the specified configuration to fail
+     */
+    public void addFailed(Artifact configurationId, Throwable cause) {
+        failed.put(configurationId, cause);
+    }
+
+    /**
+     * Clears the existing failed map and add alls the specified configurations to the map
+     * @param failed a map from configuration identifier (Artifact) to causal exception
+     */
+    public void setFailed(Map failed) {
+        this.failed.clear();
+        this.failed.putAll(failed);
+    }
+}

Modified: geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullConfigurationStore.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullConfigurationStore.java?rev=394860&r1=394859&r2=394860&view=diff
==============================================================================
--- geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullConfigurationStore.java (original)
+++ geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullConfigurationStore.java Mon Apr 17 23:44:17 2006
@@ -23,8 +23,7 @@
 import java.io.File;
 import java.io.OutputStream;
 import java.util.List;
-import java.net.URL;
-import java.net.URI;
+import java.util.Set;
 import java.net.MalformedURLException;
 
 /**
@@ -36,7 +35,7 @@
     public boolean isInPlaceConfiguration(Artifact configId) throws NoSuchConfigException, IOException {
         return false;
     }
-    
+
     public void install(ConfigurationData configurationData) throws IOException, InvalidConfigException {
     }
 
@@ -67,7 +66,7 @@
         return null;
     }
 
-    public URL resolve(Artifact configId, String moduleName, URI uri) throws NoSuchConfigException, MalformedURLException {
+    public Set resolve(Artifact configId, String moduleName, String path) throws NoSuchConfigException, MalformedURLException {
         return null;
     }
 

Added: geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullLifecycleMonitor.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullLifecycleMonitor.java?rev=394860&view=auto
==============================================================================
--- geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullLifecycleMonitor.java (added)
+++ geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/NullLifecycleMonitor.java Mon Apr 17 23:44:17 2006
@@ -0,0 +1,53 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.kernel.config;
+
+import org.apache.geronimo.kernel.repository.Artifact;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class NullLifecycleMonitor implements LifecycleMonitor {
+    public static final NullLifecycleMonitor INSTANCE = new NullLifecycleMonitor();
+
+    public void addConfiguration(Artifact configurationId) {
+    }
+
+    public void reading(Artifact configurationId) {
+    }
+
+    public void loading(Artifact configurationId) {
+    }
+
+    public void starting(Artifact configurationId) {
+    }
+
+    public void stopping(Artifact configurationId) {
+    }
+
+    public void unloading(Artifact configurationId) {
+    }
+
+    public void succeeded(Artifact configurationId) {
+    }
+
+    public void failed(Artifact configurationId, Throwable cause) {
+    }
+
+    public void finished() {
+    }
+}

Added: geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/Os.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/Os.java?rev=394860&view=auto
==============================================================================
--- geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/Os.java (added)
+++ geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/Os.java Mon Apr 17 23:44:17 2006
@@ -0,0 +1,231 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.kernel.config;
+
+import java.util.Locale;
+
+/**
+ * Condition that tests the OS type.
+ *
+ * @version $Rev$ $Date$
+ */
+public class Os {
+    private static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US);
+    private static final String OS_ARCH = System.getProperty("os.arch").toLowerCase(Locale.US);
+    private static final String OS_VERSION = System.getProperty("os.version").toLowerCase(Locale.US);
+    private static final String PATH_SEP = System.getProperty("path.separator");
+
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_WINDOWS = "windows";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_9X = "win9x";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_NT = "winnt";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_OS2 = "os/2";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_NETWARE = "netware";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_DOS = "dos";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_MAC = "mac";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_TANDEM = "tandem";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_UNIX = "unix";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_VMS = "openvms";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_ZOS = "z/os";
+    /**
+     * OS family that can be tested for. {@value}
+     */
+    public static final String FAMILY_OS400 = "os/400";
+
+    private Os() {
+        //default
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS family.
+     * @param family the family to check for
+     * @return true if the OS matches
+     * @since 1.5
+     */
+    public static boolean isFamily(String family) {
+        return isOs(family, null, null, null);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS name.
+     *
+     * @param name  The OS family type desired<br />
+     *               Possible values:<br />
+     *               <ul>
+     *               <li>dos</li>
+     *               <li>mac</li>
+     *               <li>netware</li>
+     *               <li>os/2</li>
+     *               <li>tandem</li>
+     *               <li>unix</li>
+     *               <li>windows</li>
+     *               <li>win9x</li>
+     *               <li>z/os</li>
+     *               <li>os/400</li>
+     *               </ul>
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isName(String name) {
+        return isOs(null, name, null, null);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS architecture.
+     *
+     * @param arch the OS architecture to check for
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isArch(String arch) {
+        return isOs(null, null, arch, null);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS version.
+     *
+     * @param version the OS version to check for
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isVersion(String version) {
+        return isOs(null, null, null, version);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS family, name, architecture and version
+     *
+     * @param family   The OS family
+     * @param name   The OS name
+     * @param arch   The OS architecture
+     * @param version   The OS version
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isOs(String family, String name, String arch,
+                               String version) {
+        boolean retValue = false;
+
+        if (family != null || name != null || arch != null
+            || version != null) {
+
+            boolean isFamily = true;
+            boolean isName = true;
+            boolean isArch = true;
+            boolean isVersion = true;
+
+            if (family != null) {
+
+                //windows probing logic relies on the word 'windows' in
+                //the OS
+                boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1;
+                boolean is9x = false;
+                boolean isNT = false;
+                if(isWindows) {
+                    //there are only four 9x platforms that we look for
+                    is9x = (OS_NAME.indexOf("95") >= 0
+                            || OS_NAME.indexOf("98") >= 0
+                            || OS_NAME.indexOf("me") >= 0
+                            //wince isn't really 9x, but crippled enough to
+                            //be a muchness. Ant doesnt run on CE, anyway.
+                            || OS_NAME.indexOf("ce") >= 0);
+                    isNT = !is9x;
+                }
+                if (family.equals(FAMILY_WINDOWS)) {
+                    isFamily = isWindows;
+                } else if (family.equals(FAMILY_9X)) {
+                    isFamily = isWindows && is9x;
+                } else if (family.equals(FAMILY_NT)) {
+                    isFamily = isWindows && isNT;
+                } else if (family.equals(FAMILY_OS2)) {
+                    isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1;
+                } else if (family.equals(FAMILY_NETWARE)) {
+                    isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1;
+                } else if (family.equals(FAMILY_DOS)) {
+                    isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE);
+                } else if (family.equals(FAMILY_MAC)) {
+                    isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1;
+                } else if (family.equals(FAMILY_TANDEM)) {
+                    isFamily = OS_NAME.indexOf("nonstop_kernel") > -1;
+                } else if (family.equals(FAMILY_UNIX)) {
+                    isFamily = PATH_SEP.equals(":")
+                        && !isFamily(FAMILY_VMS)
+                        && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x"));
+                } else if (family.equals(FAMILY_ZOS)) {
+                    isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1
+                        || OS_NAME.indexOf("os/390") > -1;
+                } else if (family.equals(FAMILY_OS400)) {
+                    isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1;
+                } else if (family.equals(FAMILY_VMS)) {
+                    isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1;
+                } else {
+                    throw new IllegalArgumentException(
+                        "Don\'t know how to detect os family \""
+                        + family + "\"");
+                }
+            }
+            if (name != null) {
+                isName = name.equals(OS_NAME);
+            }
+            if (arch != null) {
+                isArch = arch.equals(OS_ARCH);
+            }
+            if (version != null) {
+                isVersion = version.equals(OS_VERSION);
+            }
+            retValue = isFamily && isName && isArch && isVersion;
+        }
+        return retValue;
+    }
+}
\ No newline at end of file

Added: geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/SelectorUtils.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/SelectorUtils.java?rev=394860&view=auto
==============================================================================
--- geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/SelectorUtils.java (added)
+++ geronimo/branches/1.1/modules/kernel/src/java/org/apache/geronimo/kernel/config/SelectorUtils.java Mon Apr 17 23:44:17 2006
@@ -0,0 +1,714 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.kernel.config;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * <p>This is a utility class used by selectors and DirectoryScanner. The
+ * functionality more properly belongs just to selectors, but unfortunately
+ * DirectoryScanner exposed these as protected methods. Thus we have to
+ * support any subclasses of DirectoryScanner that may access these methods.
+ * </p>
+ * <p>This is a Singleton.</p>
+ *
+ * @version $Rev$ $Date$
+ */
+public final class SelectorUtils {
+    private static SelectorUtils instance = new SelectorUtils();
+
+    private static boolean onNetWare = Os.isFamily("netware");
+    private static boolean onDos = Os.isFamily("dos");
+
+    /**
+     * Private Constructor
+     */
+    private SelectorUtils() {
+    }
+
+    /**
+     * Retrieves the instance of the Singleton.
+     * @return singleton instance
+     */
+    public static SelectorUtils getInstance() {
+        return instance;
+    }
+
+    /**
+     * Tests whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     * <p>
+     * This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     *
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    public static boolean matchPatternStart(String pattern, String str) {
+        return matchPatternStart(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     * <p>
+     * This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    public static boolean matchPatternStart(String pattern, String str,
+                                            boolean isCaseSensitive) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(File.separator)
+                != pattern.startsWith(File.separator)) {
+            return false;
+        }
+
+        String[] patDirs = tokenizePathAsArray(pattern);
+        String[] strDirs = tokenizePathAsArray(str);
+
+        int patIdxStart = 0;
+        int patIdxEnd = patDirs.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strDirs.length - 1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs[patIdxStart];
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            return true;
+        } else if (patIdxStart > patIdxEnd) {
+            // String not exhausted, but pattern is. Failure.
+            return false;
+        } else {
+            // pattern now holds ** while string is not exhausted
+            // this will generate false positives but we can live with that.
+            return true;
+        }
+    }
+
+    /**
+     * Tests whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     *
+     * @return <code>true</code> if the pattern matches against the string,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean matchPath(String pattern, String str) {
+        return matchPath(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return <code>true</code> if the pattern matches against the string,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean matchPath(String pattern, String str,
+                                    boolean isCaseSensitive) {
+        String[] patDirs = tokenizePathAsArray(pattern);
+        String[] strDirs = tokenizePathAsArray(str);
+
+        int patIdxStart = 0;
+        int patIdxEnd = patDirs.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strDirs.length - 1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs[patIdxStart];
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs[i].equals("**")) {
+                    patDirs = null;
+                    strDirs = null;
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            if (patIdxStart > patIdxEnd) {
+                // String not exhausted, but pattern is. Failure.
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+        }
+
+        // up to last '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs[patIdxEnd];
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs[i].equals("**")) {
+                    patDirs = null;
+                    strDirs = null;
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patDirs[i].equals("**")) {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // '**/**' situation, so skip one
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp - patIdxStart - 1);
+            int strLength = (strIdxEnd - strIdxStart + 1);
+            int foundIdx = -1;
+            strLoop:
+                        for (int i = 0; i <= strLength - patLength; i++) {
+                            for (int j = 0; j < patLength; j++) {
+                                String subPat = patDirs[patIdxStart + j + 1];
+                                String subStr = strDirs[strIdxStart + i + j];
+                                if (!match(subPat, subStr, isCaseSensitive)) {
+                                    continue strLoop;
+                                }
+                            }
+
+                            foundIdx = strIdxStart + i;
+                            break;
+                        }
+
+            if (foundIdx == -1) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (!patDirs[i].equals("**")) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str) {
+        return match(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str,
+                                boolean isCaseSensitive) {
+        char[] patArr = pattern.toCharArray();
+        char[] strArr = str.toCharArray();
+        int patIdxStart = 0;
+        int patIdxEnd = patArr.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strArr.length - 1;
+        char ch;
+
+        boolean containsStar = false;
+        for (int i = 0; i < patArr.length; i++) {
+            if (patArr[i] == '*') {
+                containsStar = true;
+                break;
+            }
+        }
+
+        if (!containsStar) {
+            // No '*'s, so we make a shortcut
+            if (patIdxEnd != strIdxEnd) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patIdxEnd; i++) {
+                ch = patArr[i];
+                if (ch != '?') {
+                    if (isCaseSensitive && ch != strArr[i]) {
+                        return false; // Character mismatch
+                    }
+                    if (!isCaseSensitive && Character.toUpperCase(ch)
+                            != Character.toUpperCase(strArr[i])) {
+                        return false;  // Character mismatch
+                    }
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+        if (patIdxEnd == 0) {
+            return true; // Pattern contains only '*', which matches anything
+        }
+
+        // Process characters before first star
+        while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
+            if (ch != '?') {
+                if (isCaseSensitive && ch != strArr[strIdxStart]) {
+                    return false; // Character mismatch
+                }
+                if (!isCaseSensitive && Character.toUpperCase(ch)
+                        != Character.toUpperCase(strArr[strIdxStart])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // Process characters after last star
+        while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
+            if (ch != '?') {
+                if (isCaseSensitive && ch != strArr[strIdxEnd]) {
+                    return false; // Character mismatch
+                }
+                if (!isCaseSensitive && Character.toUpperCase(ch)
+                        != Character.toUpperCase(strArr[strIdxEnd])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // process pattern between stars. padIdxStart and patIdxEnd point
+        // always to a '*'.
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patArr[i] == '*') {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // Two stars next to each other, skip the first one.
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp - patIdxStart - 1);
+            int strLength = (strIdxEnd - strIdxStart + 1);
+            int foundIdx = -1;
+            strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    ch = patArr[patIdxStart + j + 1];
+                    if (ch != '?') {
+                        if (isCaseSensitive && ch != strArr[strIdxStart + i
+                                + j]) {
+                            continue strLoop;
+                        }
+                        if (!isCaseSensitive
+                            && Character.toUpperCase(ch)
+                                != Character.toUpperCase(strArr[strIdxStart + i + j])) {
+                            continue strLoop;
+                        }
+                    }
+                }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (patArr[i] != '*') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Breaks a path up into a Vector of path elements, tokenizing on
+     * <code>File.separator</code>.
+     *
+     * @param path Path to tokenize. Must not be <code>null</code>.
+     *
+     * @return a Vector of path elements from the tokenized path
+     */
+    public static Vector tokenizePath (String path) {
+        return tokenizePath(path, File.separator);
+    }
+
+    /**
+     * Verifies that the specified filename represents an absolute path.
+     * Differs from new java.io.File("filename").isAbsolute() in that a path
+     * beginning with a double file separator--signifying a Windows UNC--must
+     * at minimum match "\\a\b" to be considered an absolute path.
+     * @param filename the filename to be checked.
+     * @return true if the filename represents an absolute path.
+     * @throws java.lang.NullPointerException if filename is null.
+     * @since Ant 1.6.3
+     */
+    public static boolean isAbsolutePath(String filename) {
+        int len = filename.length();
+        if (len == 0) {
+            return false;
+        }
+        char sep = File.separatorChar;
+        filename = filename.replace('/', sep).replace('\\', sep);
+        char c = filename.charAt(0);
+        if (!(onDos || onNetWare)) {
+            return (c == sep);
+        }
+        if (c == sep) {
+            if (!(onDos && len > 4 && filename.charAt(1) == sep)) {
+                return false;
+            }
+            int nextsep = filename.indexOf(sep, 2);
+            return nextsep > 2 && nextsep + 1 < len;
+        }
+        int colon = filename.indexOf(':');
+        return (Character.isLetter(c) && colon == 1
+            && filename.length() > 2 && filename.charAt(2) == sep)
+            || (onNetWare && colon > 0);
+    }
+
+    /**
+     * Dissect the specified absolute path.
+     * @param path the path to dissect.
+     * @return String[] {root, remaining path}.
+     * @throws java.lang.NullPointerException if path is null.
+     */
+    public static String[] dissect(String path) {
+        char sep = File.separatorChar;
+        path = path.replace('/', sep).replace('\\', sep);
+
+        // make sure we are dealing with an absolute path
+        if (!isAbsolutePath(path)) {
+            throw new IllegalArgumentException(path + " is not an absolute path");
+        }
+        String root = null;
+        int colon = path.indexOf(':');
+        if (colon > 0 && (onDos || onNetWare)) {
+
+            int next = colon + 1;
+            root = path.substring(0, next).toUpperCase();
+            char[] ca = path.toCharArray();
+            root += sep;
+            //remove the initial separator; the root has it.
+            next = (ca[next] == sep) ? next + 1 : next;
+
+            StringBuffer sbPath = new StringBuffer();
+            // Eliminate consecutive slashes after the drive spec:
+            for (int i = next; i < ca.length; i++) {
+                if (ca[i] != sep || ca[i - 1] != sep) {
+                    sbPath.append(ca[i]);
+                }
+            }
+            path = sbPath.toString();
+        } else if (path.length() > 1 && path.charAt(1) == sep) {
+            // UNC drive
+            int nextsep = path.indexOf(sep, 2);
+            nextsep = path.indexOf(sep, nextsep + 1);
+            root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
+            path = path.substring(root.length());
+        } else {
+            root = File.separator;
+            path = path.substring(1);
+        }
+        return new String[] {root, path};
+    }
+
+
+    /**
+     * Breaks a path up into a Vector of path elements, tokenizing on
+     *
+     * @param path Path to tokenize. Must not be <code>null</code>.
+     * @param separator the separator against which to tokenize.
+     *
+     * @return a Vector of path elements from the tokenized path
+     * @since Ant 1.6
+     */
+    public static Vector tokenizePath (String path, String separator) {
+        Vector ret = new Vector();
+        if (isAbsolutePath(path)) {
+            String[] s = dissect(path);
+            ret.add(s[0]);
+            path = s[1];
+        }
+        StringTokenizer st = new StringTokenizer(path, separator);
+        while (st.hasMoreTokens()) {
+            ret.addElement(st.nextToken());
+        }
+        return ret;
+    }
+
+    /**
+     * Same as {@link #tokenizePath tokenizePath} but hopefully faster.
+     */
+    private static String[] tokenizePathAsArray(String path) {
+        String root = null;
+        if (isAbsolutePath(path)) {
+            String[] s = dissect(path);
+            root = s[0];
+            path = s[1];
+        }
+        char sep = File.separatorChar;
+        int start = 0;
+        int len = path.length();
+        int count = 0;
+        for (int pos = 0; pos < len; pos++) {
+            if (path.charAt(pos) == sep) {
+                if (pos != start) {
+                    count++;
+                }
+                start = pos + 1;
+            }
+        }
+        if (len != start) {
+            count++;
+        }
+        String[] l = new String[count + ((root == null) ? 0 : 1)];
+
+        if (root != null) {
+            l[0] = root;
+            count = 1;
+        } else {
+            count = 0;
+        }
+        start = 0;
+        for (int pos = 0; pos < len; pos++) {
+            if (path.charAt(pos) == sep) {
+                if (pos != start) {
+                    String tok = path.substring(start, pos);
+                    l[count++] = tok;
+                }
+                start = pos + 1;
+            }
+        }
+        if (len != start) {
+            String tok = path.substring(start);
+            l[count/*++*/] = tok;
+        }
+        return l;
+    }
+
+
+    /**
+     * Returns dependency information on these two files. If src has been
+     * modified later than target, it returns true. If target doesn't exist,
+     * it likewise returns true. Otherwise, target is newer than src and
+     * is not out of date, thus the method returns false. It also returns
+     * false if the src file doesn't even exist, since how could the
+     * target then be out of date.
+     *
+     * @param src the original file
+     * @param target the file being compared against
+     * @param granularity the amount in seconds of slack we will give in
+     *        determining out of dateness
+     * @return whether the target is out of date
+     */
+    public static boolean isOutOfDate(File src, File target, int granularity) {
+        if (!src.exists()) {
+            return false;
+        }
+        if (!target.exists()) {
+            return true;
+        }
+        if ((src.lastModified() - granularity) > target.lastModified()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * "Flattens" a string by removing all whitespace (space, tab, linefeed,
+     * carriage return, and formfeed). This uses StringTokenizer and the
+     * default set of tokens as documented in the single arguement constructor.
+     *
+     * @param input a String to remove all whitespace.
+     * @return a String that has had all whitespace removed.
+     */
+    public static String removeWhitespace(String input) {
+        StringBuffer result = new StringBuffer();
+        if (input != null) {
+            StringTokenizer st = new StringTokenizer(input);
+            while (st.hasMoreTokens()) {
+                result.append(st.nextToken());
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Tests if a string contains stars or question marks
+     * @param input a String which one wants to test for containing wildcard
+     * @return true if the string contains at least a star or a question mark
+     */
+    public static boolean hasWildcards(String input) {
+        return (input.indexOf('*') != -1 || input.indexOf('?') != -1);
+    }
+
+    /**
+     * removes from a pattern all tokens to the right containing wildcards
+     * @param input the input string
+     * @return the leftmost part of the pattern without wildcards
+     */
+    public static String rtrimWildcardTokens(String input) {
+        Vector v = tokenizePath(input, File.separator);
+        StringBuffer sb = new StringBuffer();
+        for (int counter = 0; counter < v.size(); counter++) {
+            if (hasWildcards((String) v.elementAt(counter))) {
+                break;
+            }
+            if (counter > 0 && sb.charAt(sb.length() - 1) != File.separatorChar) {
+                sb.append(File.separator);
+            }
+            sb.append((String) v.elementAt(counter));
+        }
+        return sb.toString();
+    }
+}
+