001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.util;
018
019import java.net.ServerSocket;
020import java.util.concurrent.atomic.AtomicLong;
021
022import org.slf4j.Logger;
023import org.slf4j.LoggerFactory;
024
025/**
026 * Generator for Globally unique Strings.
027 */
028
029public class IdGenerator {
030
031    private static final Logger LOG = LoggerFactory.getLogger(IdGenerator.class);
032    private static final String UNIQUE_STUB;
033    private static int instanceCount;
034    private static String hostName;
035    private String seed;
036    private AtomicLong sequence = new AtomicLong(1);
037    private int length;
038
039    static {
040        String stub = "";
041        boolean canAccessSystemProps = true;
042        try {
043            SecurityManager sm = System.getSecurityManager();
044            if (sm != null) {
045                sm.checkPropertiesAccess();
046            }
047        } catch (SecurityException se) {
048            canAccessSystemProps = false;
049        }
050
051        if (canAccessSystemProps) {
052            try {
053                hostName = InetAddressUtil.getLocalHostName();
054                ServerSocket ss = new ServerSocket(0);
055                stub = "-" + ss.getLocalPort() + "-" + System.currentTimeMillis() + "-";
056                Thread.sleep(100);
057                ss.close();
058            } catch (Exception ioe) {
059                LOG.warn("could not generate unique stub", ioe);
060            }
061        } else {
062            hostName = "localhost";
063            stub = "-1-" + System.currentTimeMillis() + "-";
064        }
065        UNIQUE_STUB = stub;
066    }
067
068    /**
069     * Construct an IdGenerator
070     */
071    public IdGenerator(String prefix) {
072        synchronized (UNIQUE_STUB) {
073            this.seed = prefix + UNIQUE_STUB + (instanceCount++) + ":";
074            this.length = this.seed.length() + ("" + Long.MAX_VALUE).length();
075        }
076    }
077
078    public IdGenerator() {
079        this("ID:" + hostName);
080    }
081
082    /**
083     * As we have to find the hostname as a side-affect of generating a unique
084     * stub, we allow it's easy retrevial here
085     *
086     * @return the local host name
087     */
088
089    public static String getHostName() {
090        return hostName;
091    }
092
093
094    /**
095     * Generate a unqiue id
096     *
097     * @return a unique id
098     */
099
100    public synchronized String generateId() {
101        StringBuilder sb = new StringBuilder(length);
102        sb.append(seed);
103        sb.append(sequence.getAndIncrement());
104        return sb.toString();
105    }
106
107    /**
108     * Generate a unique ID - that is friendly for a URL or file system
109     *
110     * @return a unique id
111     */
112    public String generateSanitizedId() {
113        String result = generateId();
114        result = result.replace(':', '-');
115        result = result.replace('_', '-');
116        result = result.replace('.', '-');
117        return result;
118    }
119
120    /**
121     * From a generated id - return the seed (i.e. minus the count)
122     *
123     * @param id the generated identifer
124     * @return the seed
125     */
126    public static String getSeedFromId(String id) {
127        String result = id;
128        if (id != null) {
129            int index = id.lastIndexOf(':');
130            if (index > 0 && (index + 1) < id.length()) {
131                result = id.substring(0, index + 1);
132            }
133        }
134        return result;
135    }
136
137    /**
138     * From a generated id - return the generator count
139     *
140     * @param id
141     * @return the count
142     */
143    public static long getSequenceFromId(String id) {
144        long result = -1;
145        if (id != null) {
146            int index = id.lastIndexOf(':');
147
148            if (index > 0 && (index + 1) < id.length()) {
149                String numStr = id.substring(index + 1, id.length());
150                result = Long.parseLong(numStr);
151            }
152        }
153        return result;
154    }
155
156    /**
157     * Does a proper compare on the ids
158     *
159     * @param id1
160     * @param id2
161     * @return 0 if equal else a positive if id1 is > id2 ...
162     */
163
164    public static int compare(String id1, String id2) {
165        int result = -1;
166        String seed1 = IdGenerator.getSeedFromId(id1);
167        String seed2 = IdGenerator.getSeedFromId(id2);
168        if (seed1 != null && seed2 != null) {
169            result = seed1.compareTo(seed2);
170            if (result == 0) {
171                long count1 = IdGenerator.getSequenceFromId(id1);
172                long count2 = IdGenerator.getSequenceFromId(id2);
173                result = (int)(count1 - count2);
174            }
175        }
176        return result;
177
178    }
179
180}