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 by using DNS and binding to local port", ioe);
060            }
061        }
062        // fallback
063        if (hostName == null) {
064            hostName = "localhost";
065        }
066        if (stub.length() == 0) {
067            stub = "-1-" + System.currentTimeMillis() + "-";
068        }
069        UNIQUE_STUB = stub;
070    }
071
072    /**
073     * Construct an IdGenerator
074     */
075    public IdGenerator(String prefix) {
076        synchronized (UNIQUE_STUB) {
077            this.seed = prefix + UNIQUE_STUB + (instanceCount++) + ":";
078            this.length = this.seed.length() + ("" + Long.MAX_VALUE).length();
079        }
080    }
081
082    public IdGenerator() {
083        this("ID:" + hostName);
084    }
085
086    /**
087     * As we have to find the hostname as a side-affect of generating a unique
088     * stub, we allow it's easy retrevial here
089     *
090     * @return the local host name
091     */
092
093    public static String getHostName() {
094        return hostName;
095    }
096
097
098    /**
099     * Generate a unqiue id
100     *
101     * @return a unique id
102     */
103
104    public synchronized String generateId() {
105        StringBuilder sb = new StringBuilder(length);
106        sb.append(seed);
107        sb.append(sequence.getAndIncrement());
108        return sb.toString();
109    }
110
111    /**
112     * Generate a unique ID - that is friendly for a URL or file system
113     *
114     * @return a unique id
115     */
116    public String generateSanitizedId() {
117        String result = generateId();
118        result = result.replace(':', '-');
119        result = result.replace('_', '-');
120        result = result.replace('.', '-');
121        return result;
122    }
123
124    /**
125     * From a generated id - return the seed (i.e. minus the count)
126     *
127     * @param id the generated identifer
128     * @return the seed
129     */
130    public static String getSeedFromId(String id) {
131        String result = id;
132        if (id != null) {
133            int index = id.lastIndexOf(':');
134            if (index > 0 && (index + 1) < id.length()) {
135                result = id.substring(0, index);
136            }
137        }
138        return result;
139    }
140
141    /**
142     * From a generated id - return the generator count
143     *
144     * @param id
145     * @return the count
146     */
147    public static long getSequenceFromId(String id) {
148        long result = -1;
149        if (id != null) {
150            int index = id.lastIndexOf(':');
151
152            if (index > 0 && (index + 1) < id.length()) {
153                String numStr = id.substring(index + 1, id.length());
154                result = Long.parseLong(numStr);
155            }
156        }
157        return result;
158    }
159
160    /**
161     * Does a proper compare on the ids
162     *
163     * @param id1
164     * @param id2
165     * @return 0 if equal else a positive if id1 is > id2 ...
166     */
167
168    public static int compare(String id1, String id2) {
169        int result = -1;
170        String seed1 = IdGenerator.getSeedFromId(id1);
171        String seed2 = IdGenerator.getSeedFromId(id2);
172        if (seed1 != null && seed2 != null) {
173            result = seed1.compareTo(seed2);
174            if (result == 0) {
175                long count1 = IdGenerator.getSequenceFromId(id1);
176                long count2 = IdGenerator.getSequenceFromId(id2);
177                result = (int)(count1 - count2);
178            }
179        }
180        return result;
181
182    }
183
184}