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.io.IOException; 020import java.net.ServerSocket; 021import java.util.concurrent.atomic.AtomicLong; 022 023import org.slf4j.Logger; 024import org.slf4j.LoggerFactory; 025 026/** 027 * Generator for Globally unique Strings. 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 final AtomicLong sequence = new AtomicLong(1); 037 private int length; 038 public static final String PROPERTY_IDGENERATOR_PORT ="activemq.idgenerator.port"; 039 040 static { 041 String stub = ""; 042 boolean canAccessSystemProps = true; 043 try { 044 SecurityManager sm = System.getSecurityManager(); 045 if (sm != null) { 046 sm.checkPropertiesAccess(); 047 } 048 } catch (SecurityException se) { 049 canAccessSystemProps = false; 050 } 051 052 if (canAccessSystemProps) { 053 int idGeneratorPort = 0; 054 ServerSocket ss = null; 055 try { 056 idGeneratorPort = Integer.parseInt(System.getProperty(PROPERTY_IDGENERATOR_PORT, "0")); 057 LOG.trace("Using port {}", idGeneratorPort); 058 hostName = InetAddressUtil.getLocalHostName(); 059 ss = new ServerSocket(idGeneratorPort); 060 stub = "-" + ss.getLocalPort() + "-" + System.currentTimeMillis() + "-"; 061 Thread.sleep(100); 062 } catch (Exception e) { 063 if (LOG.isTraceEnabled()) { 064 LOG.trace("could not generate unique stub by using DNS and binding to local port", e); 065 } else { 066 LOG.warn("could not generate unique stub by using DNS and binding to local port: {} {}", e.getClass().getCanonicalName(), e.getMessage()); 067 } 068 069 // Restore interrupted state so higher level code can deal with it. 070 if (e instanceof InterruptedException) { 071 Thread.currentThread().interrupt(); 072 } 073 } finally { 074 if (ss != null) { 075 try { 076 // TODO: replace the following line with IOHelper.close(ss) when Java 6 support is dropped 077 ss.close(); 078 } catch (IOException ioe) { 079 if (LOG.isTraceEnabled()) { 080 LOG.trace("Closing the server socket failed", ioe); 081 } else { 082 LOG.warn("Closing the server socket failed" + " due " + ioe.getMessage()); 083 } 084 } 085 } 086 } 087 } 088 // fallback 089 if (hostName == null) { 090 hostName = "localhost"; 091 } 092 hostName = sanitizeHostName(hostName); 093 094 if (stub.length() == 0) { 095 stub = "-1-" + System.currentTimeMillis() + "-"; 096 } 097 UNIQUE_STUB = stub; 098 } 099 100 /** 101 * Construct an IdGenerator 102 */ 103 public IdGenerator(String prefix) { 104 synchronized (UNIQUE_STUB) { 105 this.seed = prefix + UNIQUE_STUB + (instanceCount++) + ":"; 106 this.length = this.seed.length() + ("" + Long.MAX_VALUE).length(); 107 } 108 } 109 110 public IdGenerator() { 111 this("ID:" + hostName); 112 } 113 114 /** 115 * As we have to find the hostname as a side-affect of generating a unique 116 * stub, we allow it's easy retrieval here 117 * 118 * @return the local host name 119 */ 120 public static String getHostName() { 121 return hostName; 122 } 123 124 /** 125 * Generate a unique id 126 * 127 * @return a unique id 128 */ 129 public synchronized String generateId() { 130 StringBuilder sb = new StringBuilder(length); 131 sb.append(seed); 132 sb.append(sequence.getAndIncrement()); 133 return sb.toString(); 134 } 135 136 public static String sanitizeHostName(String hostName) { 137 boolean changed = false; 138 139 StringBuilder sb = new StringBuilder(); 140 for (char ch : hostName.toCharArray()) { 141 // only include ASCII chars 142 if (ch < 127) { 143 sb.append(ch); 144 } else { 145 changed = true; 146 } 147 } 148 149 if (changed) { 150 String newHost = sb.toString(); 151 LOG.info("Sanitized hostname from: {} to: {}", hostName, newHost); 152 return newHost; 153 } else { 154 return hostName; 155 } 156 } 157 158 /** 159 * Generate a unique ID - that is friendly for a URL or file system 160 * 161 * @return a unique id 162 */ 163 public String generateSanitizedId() { 164 String result = generateId(); 165 result = result.replace(':', '-'); 166 result = result.replace('_', '-'); 167 result = result.replace('.', '-'); 168 return result; 169 } 170 171 /** 172 * From a generated id - return the seed (i.e. minus the count) 173 * 174 * @param id the generated identifer 175 * @return the seed 176 */ 177 public static String getSeedFromId(String id) { 178 String result = id; 179 if (id != null) { 180 int index = id.lastIndexOf(':'); 181 if (index > 0 && (index + 1) < id.length()) { 182 result = id.substring(0, index); 183 } 184 } 185 return result; 186 } 187 188 /** 189 * From a generated id - return the generator count 190 * 191 * @param id 192 * @return the count 193 */ 194 public static long getSequenceFromId(String id) { 195 long result = -1; 196 if (id != null) { 197 int index = id.lastIndexOf(':'); 198 199 if (index > 0 && (index + 1) < id.length()) { 200 String numStr = id.substring(index + 1, id.length()); 201 result = Long.parseLong(numStr); 202 } 203 } 204 return result; 205 } 206 207 /** 208 * Does a proper compare on the ids 209 * 210 * @param id1 211 * @param id2 212 * @return 0 if equal else a positive if id1 is > id2 ... 213 */ 214 public static int compare(String id1, String id2) { 215 int result = -1; 216 String seed1 = IdGenerator.getSeedFromId(id1); 217 String seed2 = IdGenerator.getSeedFromId(id2); 218 if (seed1 != null && seed2 != null) { 219 result = seed1.compareTo(seed2); 220 if (result == 0) { 221 long count1 = IdGenerator.getSequenceFromId(id1); 222 long count2 = IdGenerator.getSequenceFromId(id2); 223 result = (int)(count1 - count2); 224 } 225 } 226 return result; 227 228 } 229 230}