KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > model > FingerprintMap


1 package hudson.model;
2
3 import hudson.Util;
4
5 import java.io.File JavaDoc;
6 import java.io.IOException JavaDoc;
7 import java.lang.ref.WeakReference JavaDoc;
8 import java.util.concurrent.ConcurrentHashMap JavaDoc;
9
10 /**
11  * Cache of {@link Fingerprint}s.
12  *
13  * <p>
14  * This implementation makes sure that no two {@link Fingerprint} objects
15  * lie around for the same hash code, and that unused {@link Fingerprint}
16  * will be adequately GC-ed to prevent memory leak.
17  *
18  * @author Kohsuke Kawaguchi
19  */

20 public final class FingerprintMap {
21     /**
22      * The value is either {@code WeakReference<Fingerprint>} or {@link Loading}.
23      *
24      * If it's {@link WeakReference}, that represents the currently available value.
25      * If it's {@link Loading}, then that indicates the fingerprint is being loaded.
26      * The thread can wait on this object to be notified when the loading completes.
27      */

28     private final ConcurrentHashMap JavaDoc<String JavaDoc,Object JavaDoc> core = new ConcurrentHashMap JavaDoc<String JavaDoc,Object JavaDoc>();
29
30     /**
31      * Used in {@link FingerprintMap#core} to indicate that the loading of a fingerprint
32      * is in progress, so that we can avoid creating two {@link Fingerprint}s for the same hash code,
33      * but do so without having a single lock.
34      */

35     private static class Loading {
36         private Fingerprint value;
37         private boolean set;
38
39         public synchronized void set(Fingerprint value) {
40             this.set = true;
41             this.value = value;
42             notifyAll();
43         }
44
45         /**
46          * Blocks until the value is {@link #set(Fingerprint)} by another thread
47          * and returns the value.
48          */

49         public synchronized Fingerprint get() {
50             try {
51                 while(!set)
52                     wait();
53                 return value;
54             } catch (InterruptedException JavaDoc e) {
55                 // assume the loading failed, but make sure we process interruption properly later
56
Thread.currentThread().interrupt();
57                 return null;
58             }
59         }
60     }
61
62     /**
63      * Returns true if there's some data in the fingerprint database.
64      */

65     public boolean isReady() {
66         return new File JavaDoc( Hudson.getInstance().getRootDir(),"fingerprints").exists();
67     }
68
69     /**
70      * @param build
71      * set to non-null if {@link Fingerprint} to be created (if so)
72      * will have this build as the owner. Otherwise null, to indicate
73      * an owner-less build.
74      */

75     public Fingerprint getOrCreate(AbstractBuild build, String JavaDoc fileName, byte[] md5sum) throws IOException JavaDoc {
76         return getOrCreate(build,fileName, Util.toHexString(md5sum));
77     }
78
79     public Fingerprint getOrCreate(AbstractBuild build, String JavaDoc fileName, String JavaDoc md5sum) throws IOException JavaDoc {
80         assert build!=null;
81         assert fileName!=null;
82         Fingerprint fp = get(md5sum);
83         if(fp!=null)
84             return fp; // found it.
85

86         // not found. need to create one.
87
// creates a new one.
88
// since it's nearly impossible for two different files to have the same md5 sum,
89
// this part is not synchronized.
90
fp = new Fingerprint(build,fileName,toByteArray(md5sum));
91
92         core.put(md5sum,new WeakReference JavaDoc<Fingerprint>(fp));
93
94         return fp;
95     }
96
97     public synchronized Fingerprint get(String JavaDoc md5sum) throws IOException JavaDoc {
98         if(md5sum.length()!=32)
99             return null; // illegal input
100
md5sum = md5sum.toLowerCase();
101
102         while(true) {
103             Object JavaDoc value = core.get(md5sum);
104
105             if(value instanceof WeakReference JavaDoc) {
106                 WeakReference JavaDoc<Fingerprint> wfp = (WeakReference JavaDoc<Fingerprint>) value;
107                 Fingerprint fp = wfp.get();
108                 if(fp!=null)
109                     return fp; // found it
110
}
111             if(value instanceof Loading) {
112                 // another thread is loading it. get the value from there.
113
return ((Loading)value).get();
114             }
115
116             // the fingerprint doesn't seem to be loaded thus far, so let's load it now.
117
// the care needs to be taken that other threads might be trying to do the same.
118
Loading l = new Loading();
119             if(value==null ? core.putIfAbsent(md5sum,l)!=null : !core.replace(md5sum,value,l)) {
120                 // the value has changed since then. another thread is attempting to do the same.
121
// go back to square 1 and try it again.
122
continue;
123             }
124
125             Fingerprint fp = Fingerprint.load(toByteArray(md5sum));
126             // let other threads know that the value is available now
127
l.set(fp);
128
129             // the map needs to be updated to reflect the result of loading
130
if(fp!=null)
131                 core.put(md5sum,new WeakReference JavaDoc<Fingerprint>(fp));
132             else
133                 core.remove(md5sum);
134
135             return fp;
136         }
137
138     }
139
140     private byte[] toByteArray(String JavaDoc md5sum) {
141         byte[] data = new byte[16];
142         for( int i=0; i<md5sum.length(); i+=2 )
143             data[i/2] = (byte)Integer.parseInt(md5sum.substring(i,i+2),16);
144         return data;
145     }
146
147 }
148
Popular Tags