KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > key > AbstractKeyProvider


1 /*
2  * Copyright 2006 Schlichtherle IT Services
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package de.schlichtherle.key;
18
19 import de.schlichtherle.util.ThreadLocalLong;
20
21 import java.lang.reflect.Array JavaDoc;
22 import java.util.Arrays JavaDoc;
23
24 /**
25  * This abstract class implements the base functionality required to be a
26  * "friendly" {@link KeyProvider}.
27  * Each instance of this class maintains a single key, which can be of any
28  * run time type (it is just required to be an {@link Object}).
29  * A clone of this key is returned on each call to {@link #getCreateKey}
30  * and {@link #getOpenKey}.
31  * Cloning is used for all array classes and all classes which properly
32  * implement the {@link Cloneable} interface.
33  * The class remains abstract because there is no meaningful template
34  * implementation of the {@link #invalidOpenKey()} method.
35  * <p>
36  * Other than the key, this class is stateless.
37  * Hence, instances may be shared among multiple protected resources,
38  * causing them to use the same key.
39  * However, this feature may be restricted by subclasses such as
40  * {@link PromptingKeyProvider} for example.
41  * <p>
42  * This class is thread safe.
43  *
44  * @see KeyProvider
45  *
46  * @author Christian Schlichtherle
47  * @version @version@
48  * @since TrueZIP 6.4 (renamed from SharedKeyProvider)
49  */

50 public abstract class AbstractKeyProvider implements KeyProvider {
51
52     private Object JavaDoc key;
53
54     private final ThreadLocalLong invalidated = new ThreadLocalLong();
55
56     /**
57      * Maps this instance as the key provider for the given resource
58      * identifier in the {@link KeyManager}.
59      * <p>
60      * The key manager will use this method whenever it adds a key provider
61      * which is actually an instance of this class.
62      * This allows subclasses to add additional behaviour or constraints
63      * whenever an instance is mapped in the <code>KeyManager</code>.
64      *
65      * @param resourceID The resource identifier to map this instance for.
66      *
67      * @return The key provider previously mapped for the given resource
68      * identifier or <code>null</code> if no key provider was mapped.
69      *
70      * @throws NullPointerException If <code>resourceID</code> is
71      * <code>null</code>.
72      * @throws IllegalStateException If mapping this instance is prohibited
73      * by a constraint in a subclass.
74      * Please refer to the respective subclass documentation for
75      * more information about its constraint(s).
76      */

77     protected KeyProvider addToKeyManager(String JavaDoc resourceID)
78     throws NullPointerException JavaDoc, IllegalStateException JavaDoc {
79         return KeyManager.mapKeyProvider(resourceID, this);
80     }
81
82     /**
83      * Remove this instance as the key provider for the given resource
84      * identifier from the map in the {@link KeyManager}.
85      * <p>
86      * The key manager will use this method whenever it adds a key provider
87      * which is actually an instance of this class.
88      * This allows subclasses to add additional behaviour or constraints
89      * whenever an instance is unmapped in the <code>KeyManager</code>.
90      *
91      * @param resourceID The resource identifier to unmap this instance from.
92      *
93      * @return The key provider previously mapped for the given resource
94      * identifier.
95      * @throws NullPointerException If <code>resourceID</code> is
96      * <code>null</code>.
97      * @throws IllegalStateException If unmapping this instance is prohibited
98      * by a constraint in a subclass.
99      * Please refer to the respective subclass documentation for
100      * more information about its constraint(s).
101      */

102     protected KeyProvider removeFromKeyManager(String JavaDoc resourceID)
103     throws NullPointerException JavaDoc, IllegalStateException JavaDoc {
104         return KeyManager.unmapKeyProvider(resourceID);
105     }
106
107     //
108
// Key related stuff.
109
//
110

111     /**
112      * Returns the single key maintained by this key provider.
113      * Client applications should not call this method directly,
114      * but rather call {@link #getOpenKey} or {@link #getCreateKey}.
115      * It is intended to be used by subclasses and user interface classes only.
116      */

117     public synchronized Object JavaDoc getKey() {
118         return key;
119     }
120
121     /**
122      * Sets the single key maintained by this key provider.
123      * Client applications should not call this method directly.
124      * It is intended to be used by subclasses and user interface classes only.
125      */

126     public synchronized void setKey(Object JavaDoc key) {
127         this.key = key;
128     }
129
130     /**
131      * Returns a clone of the key or the key itself if
132      * cloning it fails for some reason.
133      * If the key is an array, a shallow copy of the array is
134      * returned.
135      * When overwriting this method, please consider that the key
136      * may be <code>null</code>.
137      *
138      * @throws RuntimeException If cloning the key results in a runtime
139      * exception.
140      */

141     protected Object JavaDoc cloneKey() {
142         final Object JavaDoc key = getKey();
143         if (key == null)
144             return null; // the clone of null is null, right? :-)
145

146         // Could somebody please explain to me why the clone method is
147
// declared "protected" in Object and Cloneable is just a marker
148
// interface?
149
// And furthermore, why does clone() called via reflection on an
150
// array throw a NoSuchMethodException?
151
// Somehow, this design doesn't speak to me...
152
final Class JavaDoc c = key.getClass();
153         if (c.isArray()) {
154             final int l = Array.getLength(key);
155             final Object JavaDoc p = Array.newInstance(c.getComponentType(), l);
156             System.arraycopy(key, 0, p, 0, l);
157             return p;
158         } else if (key instanceof Cloneable JavaDoc) {
159             try {
160                 return key.getClass().getMethod("clone", null).invoke(key, null);
161             } catch (RuntimeException JavaDoc failure) {
162                 throw failure; // pass on
163
} catch (Exception JavaDoc failure) {
164                 // Fall through.
165
}
166         }
167
168         // Last resort.
169
return key;
170     }
171
172     /**
173      * Clears the data structure of the key itself and sets the
174      * reference to it to <code>null</code>.
175      * If the key is an array, the array is filled with zero values
176      * before setting the reference to <code>null</code>.
177      * When overwriting this method, please consider that the key
178      * may be <code>null</code>.
179      */

180     protected void resetKey() {
181         final Object JavaDoc key = getKey();
182         if (key == null)
183             return;
184         
185         setKey(null);
186
187         synchronized (key) {
188             if (key instanceof byte[])
189                 Arrays.fill((byte[]) key, (byte) 0);
190             else if (key instanceof char[])
191                 Arrays.fill((char[]) key, (char) 0);
192             else if (key instanceof short[])
193                 Arrays.fill((short[]) key, (short) 0);
194             else if (key instanceof int[])
195                 Arrays.fill((int[]) key, (int) 0);
196             else if (key instanceof long[])
197                 Arrays.fill((long[]) key, (long) 0);
198             else if (key instanceof float[])
199                 Arrays.fill((float[]) key, (float) 0);
200             else if (key instanceof double[])
201                 Arrays.fill((double[]) key, (double) 0);
202             else if (key instanceof boolean[])
203                 Arrays.fill((boolean[]) key, false);
204             else if (key instanceof Object JavaDoc[])
205                 Arrays.fill((Object JavaDoc[]) key, null);
206         }
207     }
208
209     /**
210      * This hook may be used to reset this key provider instance.
211      * Note that the implementation in this class does nothing.
212      * Since TrueZIP 6.4, this method is not called from the constructor
213      * anymore.
214      */

215     public void reset() {
216         // Do NOT call this - it limits the reusability!
217
//resetKey();
218
}
219
220     //
221
// Interface implementation:
222
//
223

224     /**
225      * Returns a clone of the key which may be used to create a new
226      * protected resource or entirely replace the contents of an already
227      * existing protected resource.
228      * Returns the key itself if cloning it fails for some reason.
229      * If the key is an array, a shallow copy of the array is
230      * returned.
231      *
232      * @throws RuntimeException If cloning the key results in a runtime
233      * exception.
234      *
235      * @see KeyProvider#getCreateKey
236      */

237     public Object JavaDoc getCreateKey() throws UnknownKeyException {
238         return cloneKey();
239     }
240
241     /**
242      * Calls {@link #getOpenKeyImpl} and enforces a three seconds suspension
243      * penalty if {@link #invalidOpenKey} was called by the same thread before.
244      * Because this method is final, this implementation qualifies as a
245      * "friendly" <code>KeyProvider</code> implementation, even when subclassed.
246      *
247      * @throws RuntimeException If cloning the key results in a runtime
248      * exception.
249      * @see KeyProvider#getOpenKey
250      */

251     public final Object JavaDoc getOpenKey() throws UnknownKeyException {
252         try {
253             return getOpenKeyImpl();
254         } finally {
255             enforceSuspensionPenalty();
256         }
257     }
258
259     /**
260      * Returns a clone of the key which may be used to open an
261      * existing protected resource in order to access its contents.
262      * Returns the key itself if cloning it fails for some reason.
263      * If the key is an array, a shallow copy of the array is
264      * returned.
265      *
266      * @throws RuntimeException If cloning the key results in a runtime
267      * exception.
268      *
269      * @see KeyProvider#getOpenKey
270      */

271     protected Object JavaDoc getOpenKeyImpl() throws UnknownKeyException {
272         return cloneKey();
273     }
274
275     private void enforceSuspensionPenalty() {
276         final long last = invalidated.getValue();
277         long delay;
278         InterruptedException JavaDoc interrupted = null;
279         while ((delay = System.currentTimeMillis() - last) < MIN_KEY_RETRY_DELAY) {
280             try {
281                 Thread.sleep(MIN_KEY_RETRY_DELAY - delay);
282             } catch (InterruptedException JavaDoc ex) {
283                 interrupted = ex;
284             }
285         }
286         if (interrupted != null)
287             Thread.currentThread().interrupt();
288     }
289
290     /**
291      * This method logs the current time for the current thread, which
292      * is later used by {@link #getOpenKey} to enforce the suspension penalty
293      * and then calls {@link #invalidOpenKeyImpl}.
294      * Because this method is final, this implementation qualifies as a
295      * "friendly" <code>KeyProvider</code> implementation, even when subclassed.
296      *
297      * @see KeyProvider#invalidOpenKey
298      */

299     public final void invalidOpenKey() {
300         invalidated.setValue(System.currentTimeMillis());
301         invalidOpenKeyImpl();
302     }
303
304     /**
305      * This method remains abstract in this class.
306      * Depending on the context, subclasses could use {@link #setKey(Object)}
307      * to fix the key or simply ignore the call.
308      *
309      * @see KeyProvider#invalidOpenKey
310      */

311     protected abstract void invalidOpenKeyImpl();
312 }
313
Popular Tags