001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.util; 029 030import org.opencms.main.CmsIllegalArgumentException; 031import org.opencms.main.CmsInitException; 032import org.opencms.main.CmsLog; 033 034import java.io.Externalizable; 035import java.io.IOException; 036import java.io.ObjectInput; 037import java.io.ObjectOutput; 038 039import org.apache.commons.logging.Log; 040 041import org.safehaus.uuid.EthernetAddress; 042import org.safehaus.uuid.UUID; 043import org.safehaus.uuid.UUIDGenerator; 044 045/** 046 * Generates a UUID using spatial and temporal uniqueness.<p> 047 * 048 * Spatial uniqueness is derived from 049 * ethernet address (MAC, 802.1); temporal from system clock.<p> 050 * 051 * For more information about the algorithm used, please see 052 * <a href="http://www.opengroup.org/dce/info/draft-leach-uuids-guids-01.txt"> 053 * draft-leach-uuids-guids-01.txt</a>.<p> 054 * 055 * Because Java is unable to read the MAC address of the machine 056 * (without using JNI), the MAC address has to be provided first 057 * by using the static {@link #init(String)} method.<p> 058 * 059 * This class is just a facade wrapper for the "real" UUID implementation.<p> 060 * 061 * @since 6.0.0 062 */ 063public final class CmsUUID extends Object implements Cloneable, Comparable<CmsUUID>, Externalizable { 064 065 /** A regular expression for matching UUIDs. */ 066 public static final String UUID_REGEX = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; 067 068 /** The log object for this class. */ 069 private static final Log LOG = CmsLog.getLog(CmsUUID.class); 070 071 /** Ethernet address of the server machine. */ 072 private static EthernetAddress m_ethernetAddress; 073 074 /** OpenCms UUID (name based uuid of "www.opencms.org" in the dns name space). */ 075 private static UUID m_opencmsUUID = UUIDGenerator.getInstance().generateNameBasedUUID( 076 new UUID(UUID.NAMESPACE_DNS), 077 "www.opencms.org"); 078 079 /** Constant for the null UUID. */ 080 private static final CmsUUID NULL_UUID = new CmsUUID(UUID.getNullUUID()); 081 082 /** Serial version UID required for safe serialization. */ 083 private static final long serialVersionUID = 1736324454709298676L; 084 085 /** Internal UUID implementation. */ 086 private transient UUID m_uuid; 087 088 /** 089 * Creates a new UUID.<p> 090 * 091 * Please note that the static init() method has to be called first to initialize the 092 * internet address of the machine.<p> 093 */ 094 public CmsUUID() { 095 096 if (m_ethernetAddress == null) { 097 // if no ethernet address is available, generate a dummy 098 // this is required because otherwise we can't ever de-serialize a CmsUUID outside of OpenCms, 099 // since the empty constructor is called when the de-serialization takes place 100 init(CmsStringUtil.getEthernetAddress()); 101 } 102 m_uuid = UUIDGenerator.getInstance().generateTimeBasedUUID(m_ethernetAddress); 103 } 104 105 /** 106 * Create a UUID based on a binary data array.<p> 107 * 108 * @param data a binary data array representing a UUID 109 */ 110 public CmsUUID(byte[] data) { 111 112 m_uuid = new UUID(data); 113 } 114 115 /** 116 * Create a UUID based on a String.<p> 117 * 118 * @param uuid a String representing a UUID 119 * @throws NumberFormatException in case uuid is not a valid UUID 120 */ 121 public CmsUUID(String uuid) 122 throws NumberFormatException { 123 124 m_uuid = new UUID(uuid); 125 } 126 127 /** 128 * Create a new UUID based on another one (used internal for cloning).<p> 129 * 130 * @param uuid the UUID to clone 131 */ 132 private CmsUUID(UUID uuid) { 133 134 m_uuid = uuid; 135 } 136 137 /** 138 * Check that the given id is not the null id.<p> 139 * 140 * @param id the id to check 141 * @param canBeNull only if flag is set, <code>null</code> is accepted 142 * 143 * @see #isNullUUID() 144 */ 145 public static void checkId(CmsUUID id, boolean canBeNull) { 146 147 if (canBeNull && (id == null)) { 148 return; 149 } 150 if ((!canBeNull && (id == null)) || id.isNullUUID()) { 151 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_INVALID_UUID_1, id)); 152 } 153 } 154 155 /** 156 * Returns a constant (name based) UUID, 157 * based on the given name in the OpenCms name space. 158 * 159 * @param name the name to derive the uuid from 160 * @return name based UUID of the given name 161 */ 162 public static CmsUUID getConstantUUID(String name) { 163 164 return new CmsUUID(UUIDGenerator.getInstance().generateNameBasedUUID(m_opencmsUUID, name)); 165 } 166 167 /** 168 * Returns a String representing a dummy (random based) ethernet address.<p> 169 * 170 * @return a String representing a dummy (random based) ethernet address 171 */ 172 public static String getDummyEthernetAddress() { 173 174 return UUIDGenerator.getInstance().getDummyAddress().toString(); 175 } 176 177 /** 178 * Returns a null UUID, 179 * use this null UUID to check if a UUID has been initialized or not.<p> 180 * 181 * @return a null UUID 182 */ 183 public static CmsUUID getNullUUID() { 184 185 return NULL_UUID; 186 } 187 188 /** 189 * Returns a constant (name based) UUID for OpenCms, 190 * based on "www.opencms.org" in the dns name space. 191 * 192 * @return name based UUID of OpenCms 193 */ 194 public static CmsUUID getOpenCmsUUID() { 195 196 return new CmsUUID(m_opencmsUUID); 197 } 198 199 /** 200 * Initialize the UUID generator with the ethernet address of the server machine.<p> 201 * 202 * The ethernetAddress parameter must represent a 'standard' ethernet MAC address string 203 * (e.g. '00:C0:F0:3D:5B:7C'). 204 * 205 * @param ethernetAddress the ethernet address of the server machine 206 * @throws CmsInitException in case the ethernetAddress String is not a valid ethernet address 207 */ 208 public static void init(String ethernetAddress) throws CmsInitException { 209 210 try { 211 m_ethernetAddress = new EthernetAddress(ethernetAddress); 212 } catch (Exception e) { 213 throw new CmsInitException( 214 Messages.get().container(Messages.ERR_INVALID_ETHERNET_ADDRESS_1, ethernetAddress)); 215 } 216 } 217 218 /** 219 * Returns <code>true</code> if the given UUID is valid.<p> 220 * 221 * @param uuid the UUID to check 222 * 223 * @return <code>true</code> if the given UUID is valid 224 */ 225 public static boolean isValidUUID(String uuid) { 226 227 try { 228 return (null != uuid) && (null != UUID.valueOf(uuid)); 229 } catch (NumberFormatException e) { 230 // return false 231 } 232 return false; 233 } 234 235 /** 236 * Returns the given String transformed to a UUID in case the String is a valid UUID.<p> 237 * 238 * @param uuid the String to transform to a UUID 239 * 240 * @return the given String transformed to a UUID in case the String is a valid UUID 241 * 242 * @throws NumberFormatException in case the String is no valid UUID 243 */ 244 public static CmsUUID valueOf(String uuid) throws NumberFormatException { 245 246 return new CmsUUID(UUID.valueOf(uuid)); 247 } 248 249 /** 250 * Creates a clone of this CmsUUID.<p> 251 * 252 * @return a clone of this CmsUUID 253 */ 254 @Override 255 public Object clone() { 256 257 if (this == NULL_UUID) { 258 return NULL_UUID; 259 } 260 return new CmsUUID((UUID)m_uuid.clone()); 261 } 262 263 /** 264 * @see java.lang.Comparable#compareTo(Object) 265 */ 266 public int compareTo(CmsUUID obj) { 267 268 return m_uuid.compareTo(obj.m_uuid); 269 } 270 271 /** 272 * @see java.lang.Object#equals(java.lang.Object) 273 */ 274 @Override 275 public boolean equals(Object obj) { 276 277 if (obj == this) { 278 return true; 279 } 280 if (obj instanceof CmsUUID) { 281 return ((CmsUUID)obj).m_uuid.equals(m_uuid); 282 } 283 return false; 284 } 285 286 /** 287 * Returns the String representation of this UUID, same as {@link #toString()}.<p> 288 * 289 * This method is useful if bean like access to the UUID String is required.<p> 290 * 291 * @return the String representation of this UUID 292 */ 293 public String getStringValue() { 294 295 return toString(); 296 } 297 298 /** 299 * Optimized hashCode implementation for UUID's.<p> 300 * 301 * @see java.lang.Object#hashCode() 302 */ 303 @Override 304 public int hashCode() { 305 306 return m_uuid.hashCode(); 307 } 308 309 /** 310 * Returns true if this UUID is equal to the null UUID.<p> 311 * 312 * @return true if this UUID is equal to the null UUID 313 */ 314 public boolean isNullUUID() { 315 316 if (this == NULL_UUID) { 317 return true; 318 } 319 return m_uuid.equals(UUID.getNullUUID()); 320 } 321 322 /** 323 * @see java.io.Externalizable#readExternal(java.io.ObjectInput) 324 */ 325 public void readExternal(ObjectInput in) { 326 327 Object o = null; 328 try { 329 o = in.readObject(); 330 } catch (Throwable e) { 331 // there are 2 development version of OpenCms (6.1.7 and 6.1.8) which had a different format, 332 // here the Object was preceded by a Long 333 try { 334 // first read the long, we don't really need it but it must be removed from the stream 335 in.readLong(); 336 o = in.readObject(); 337 } catch (Throwable t) { 338 if (LOG.isDebugEnabled()) { 339 LOG.debug(Messages.get().getBundle().key(Messages.LOG_READ_UUID_OLD_1, o), t); 340 } 341 } 342 } 343 344 if (o instanceof String) { 345 // this UUID has been serialized using the new method 346 if (LOG.isDebugEnabled()) { 347 LOG.debug(Messages.get().getBundle().key(Messages.LOG_READ_UUID_1, o)); 348 } 349 m_uuid = new UUID((String)o); 350 } 351 352 // log an error if the uuid could not be deserialized 353 if (m_uuid == null) { 354 // UUID cannot be deserialized 355 if (LOG.isDebugEnabled()) { 356 LOG.debug(Messages.get().getBundle().key(Messages.LOG_ERR_READ_UUID_0)); 357 } 358 } 359 } 360 361 /** 362 * Returns the UUID as a 16-byte byte array.<p> 363 * 364 * @return 16-byte byte array that contains the UUID's bytes in the network byte order 365 */ 366 public byte[] toByteArray() { 367 368 return m_uuid.toByteArray(); 369 } 370 371 /** 372 * @see java.lang.Object#toString() 373 */ 374 @Override 375 public String toString() { 376 377 return m_uuid.toString(); 378 } 379 380 /** 381 * 382 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) 383 */ 384 public void writeExternal(ObjectOutput out) throws IOException { 385 386 if (LOG.isDebugEnabled()) { 387 LOG.debug(Messages.get().getBundle().key(Messages.LOG_WRITE_UUID_1, toString())); 388 } 389 out.writeObject(toString()); 390 } 391}