View Javadoc

1   package org.apache.turbine.util.db;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.OutputStream;
21  
22  import java.util.StringTokenizer;
23  
24  import javax.mail.internet.MimeUtility;
25  
26  import org.apache.commons.lang.StringUtils;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  import org.apache.turbine.Turbine;
32  import org.apache.turbine.TurbineConstants;
33  
34  import org.apache.turbine.util.TurbineException;
35  
36  /***
37   * <p>This class generates universally unique id's in the form of a String.
38   * The id has three parts.  The first is supposed to be location dependent.
39   * The preferred location parameter is an ethernet (MAC) address, but an IP
40   * can be used as well.  if none is supplied a Math.random generated number
41   * will be used.  This part of the key will be 48 bits in length.
42   * The second part of the key is time related and will be the lower 48 bits
43   * of the long used to signify the time since Jan. 1, 1970.  This will
44   * cause key rollover in the year 6429.
45   * The preceding 12 bytes are Base64 encoded with the characters / and *
46   * replaced by _ (underscore) and - (dash).  Resulting in 16 characters.
47   * Finally a counter is used to hand out 4095 keys in between each
48   * timestamp.
49   * The resulting id is a String of 18 characters including:
50   * a-z,A-Z,0-9, and the previously mentioned - and _.</p>
51   *
52   * <p>Note this class does not save any state information, so it is important
53   * that time only moves forward to keep the integrity of the ids.  We
54   * might want to consider saving some state info.</p>
55   *
56   * <p>To specify the MAC/Ethernet address, add a uuid.address= property to the
57   * TurbineResources.properties file.</p>
58   *
59   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
60   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
61   * @version $Id: UUIdGenerator.java 264152 2005-08-29 14:50:22Z henning $
62   * @deprecated Use the Unique ID Service
63   */
64  public class UUIdGenerator
65  {
66      /*** Logging */
67      private static Log log = LogFactory.getLog(UUIdGenerator.class);
68  
69      private static final String errorString = "uuid.address property in "
70              + "TurbineResources.properties should be a valid IP\n "
71              + "e.g. 18.2.3.100, or an ethernet address e.g. "
72              + "AE:10:3E:de:f5:77 uuid.address was ";
73  
74      private byte[] address = new byte[6];
75      private String baseId = null;
76      private int counter = 0;
77  
78      /***
79       * Constructor
80       */
81      public UUIdGenerator() throws TurbineException
82      {
83          String addr =
84              Turbine.getConfiguration().getString(TurbineConstants.UUID_ADDRESS_KEY);
85  
86          if (StringUtils.isEmpty(addr))
87          {
88              log.info("UUIdGenerator is using a random number as the "
89                      + "base for id's.  This is not the best method for many "
90                      + "purposes, but may be adequate in some circumstances."
91                      + " Consider using an IP or ethernet (MAC) address if "
92                      + "available. Edit TurbineResources.properties file and "
93                      + "add a uuid.address= property.");
94  
95              for (int i = 0; i < 6; i++)
96              {
97                  address[i] = (byte) (255 * Math.random());
98              }
99          }
100         else
101         {
102             if (addr.indexOf(".") > 0)
103             {
104                 // we should have an IP
105                 StringTokenizer stok = new StringTokenizer(addr, ".");
106                 if (stok.countTokens() != 4)
107                 {
108                     throw new TurbineException(errorString + addr);
109                 }
110                 // this is meant to insure that id's made from ip addresses
111                 // will not conflict with MAC id's. I think MAC addresses
112                 // will never have the highest bit set.  Though this should
113                 // be investigated further.
114                 address[0] = (byte) 255;
115                 address[1] = (byte) 255;
116                 int i = 2;
117                 try
118                 {
119                     while (stok.hasMoreTokens())
120                     {
121                         address[i++] =
122                                 Integer.valueOf(stok.nextToken(),
123                                         16).byteValue();
124                     }
125                 }
126                 catch (Exception e)
127                 {
128                     throw new TurbineException(errorString + addr, e);
129                 }
130             }
131             else if (addr.indexOf(":") > 0)
132             {
133                 // we should have a MAC
134                 StringTokenizer stok = new StringTokenizer(addr, ":");
135                 if (stok.countTokens() != 6)
136                 {
137                     throw new TurbineException(errorString + addr);
138                 }
139                 int i = 0;
140                 try
141                 {
142                     while (stok.hasMoreTokens())
143                     {
144                         address[i++] = Byte.parseByte(stok.nextToken(), 16);
145                     }
146                 }
147                 catch (Exception e)
148                 {
149                     throw new TurbineException(errorString + addr, e);
150                 }
151             }
152             else
153             {
154                 throw new TurbineException(errorString + addr);
155             }
156         }
157     }
158 
159     /***
160      * Generates the new base id
161      */
162     private void generateNewBaseId() throws Exception
163     {
164         long now = System.currentTimeMillis();
165         byte[] nowBytes = org.apache.java.lang.Bytes.toBytes(now);
166         ByteArrayOutputStream bas = null;
167         OutputStream encodedStream = null;
168         try
169         {
170             bas = new ByteArrayOutputStream(16);
171             encodedStream = MimeUtility.encode(bas, "base64");
172             encodedStream.write(nowBytes);
173             baseId = bas.toString("ISO-8859-1"); // or maybe "US-ASCII"?
174             baseId = baseId.replace('/', '_');
175             baseId = baseId.replace('*', '-');
176         }
177         finally
178         {
179             if (bas != null)
180             {
181                 bas.close();
182             }
183             if (encodedStream != null)
184             {
185                 encodedStream.close();
186             }
187         }
188     }
189 
190     /***
191      * Gets the id
192      * @return the 18 character id
193      */
194     public String getId() throws Exception
195     {
196         int index = ++counter;
197         if (index > 4095)
198         {
199             synchronized (this)
200             {
201                 if (counter > 4095)
202                 {
203                     generateNewBaseId();
204                     counter = 0;
205                 }
206                 else
207                 {
208                     index = ++counter;
209                 }
210             }
211         }
212         StringBuffer idbuf = new StringBuffer(18);
213         idbuf.append(baseId);
214         idbuf.append(countChar[index / 64]);
215         idbuf.append(countChar[index % 64]);
216         return idbuf.toString();
217     }
218 
219     /***
220      * characters used in the ID
221      */
222     private static final char[] countChar =
223             {
224                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
225                 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
226                 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
227                 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
228                 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
229                 '8', '9', '-', '_'
230             };
231 }
232