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     */
017    package org.apache.activemq.util;
018    
019    import java.net.ServerSocket;
020    import java.util.concurrent.atomic.AtomicLong;
021    
022    import org.slf4j.Logger;
023    import org.slf4j.LoggerFactory;
024    
025    /**
026     * Generator for Globally unique Strings.
027     */
028    
029    public 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    }