All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.key.AbstractKeyProvider Maven / Gradle / Ivy

Go to download

TrueZIP is a Java based Virtual File System (VFS) to enable transparent, multi-threaded read/write access to archive files (ZIP, TAR etc.) as if they were directories. Archive files may be arbitrarily nested and the nesting level is only limited by heap and file system size.

The newest version!
/*
 * Copyright (C) 2006-2010 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.schlichtherle.key;

import de.schlichtherle.util.ThreadLocalLong;

import java.lang.reflect.Array;
import java.util.Arrays;

/**
 * This abstract class implements the base functionality required to be a
 * "friendly" {@link KeyProvider}.
 * Each instance of this class maintains a single key, which can be of any
 * run time type (it is just required to be an {@link Object}).
 * A clone of this key is returned on each call to {@link #getCreateKey}
 * and {@link #getOpenKey}.
 * Cloning is used for all array classes and all classes which properly
 * implement the {@link Cloneable} interface.
 * The class remains abstract because there is no meaningful template
 * implementation of the {@link #invalidOpenKeyImpl()} method.
 * 

* Other than the key, this class is stateless. * Hence, instances may be shared among multiple protected resources, * causing them to use the same key. * However, this feature may be restricted by subclasses such as * {@link PromptingKeyProvider} for example. *

* This class is thread safe. * * @see KeyProvider * * @author Christian Schlichtherle * @version $Id$ * @since TrueZIP 6.4 (renamed from SharedKeyProvider) */ public abstract class AbstractKeyProvider implements KeyProvider { private Object key; private final ThreadLocalLong invalidated = new ThreadLocalLong(); /** * Returns the single key maintained by this key provider. * Client applications should not call this method directly, * but rather call {@link #getOpenKey} or {@link #getCreateKey}. * It is intended to be used by subclasses and user interface classes only. * * @deprecated You should not use this method from this class. * It will be moved to a subclass in the next major version release. */ public synchronized Object getKey() { return key; } /** * Sets the single key maintained by this key provider. * Client applications should not call this method directly. * It is intended to be used by subclasses and user interface classes only. * * @deprecated You should not use this method from this class. * It will be moved to a subclass in the next major version release. */ public synchronized void setKey(Object key) { this.key = key; } /** * Forwards the call to {@link #getCreateKeyImpl}. * * @return A clone of the return value of {@code getCreateKeyImpl}. * In case of an array, a shallow copy of the array is returned. * @throws UnknownKeyException If {@code getCreateKeyImpl} throws * this or the returned key is {@code null}. * @throws RuntimeException If cloning the key results in a runtime * exception. * @see KeyProvider#getCreateKey */ public Object getCreateKey() throws UnknownKeyException { final Object key = getCreateKeyImpl(); if (key == null) throw new UnknownKeyException(); return clone(key); } /** * Returns the key which should be used to create a new protected * resource or entirely replace the contents of an already existing * protected resource. * * @return A template for the {@code key} to use or {@code null}. * @throws UnknownKeyException If the required key is unknown. * At the subclasses discretion, this may mean that prompting for * the key has been disabled or cancelled by the user. * @see KeyProvider#getCreateKey */ protected Object getCreateKeyImpl() throws UnknownKeyException { return getKey(); } /** * Forwards the call to {@link #getOpenKeyImpl} and enforces a three * seconds suspension penalty if {@link #invalidOpenKey} was called by * the same thread before. * Because this method is final, this qualifies the implementation in * this class as a "friendly" {@code KeyProvider} implementation, * even when subclassed. * * @return A clone of the return value of {@code getOpenKeyImpl}. * In case of an array, a shallow copy of the array is returned. * @throws UnknownKeyException If {@code getOpenKeyImpl} throws * this or the returned key is {@code null}. * @throws RuntimeException If cloning the key results in a runtime * exception. * @see KeyProvider#getOpenKey */ public final Object getOpenKey() throws UnknownKeyException { try { final Object key = getOpenKeyImpl(); if (key == null) throw new UnknownKeyException(); return clone(key); } finally { enforceSuspensionPenalty(); } } /** * Returns the key which should be used to open an existing protected * resource in order to access its contents. * * @return A template for the {@code key} to use or {@code null}. * @throws UnknownKeyException If the required key is unknown. * At the subclasses discretion, this may mean that prompting for * the key has been disabled or cancelled by the user. * @see KeyProvider#getCreateKey */ protected Object getOpenKeyImpl() throws UnknownKeyException { return getKey(); } /** * This method logs the current time for the current thread, which * is later used by {@link #getOpenKey} to enforce the suspension penalty * and then calls {@link #invalidOpenKeyImpl}. * Because this method is final, this implementation qualifies as a * "friendly" {@code KeyProvider} implementation, even when subclassed. * * @see KeyProvider#invalidOpenKey */ public final void invalidOpenKey() { invalidated.setValue(System.currentTimeMillis()); invalidOpenKeyImpl(); } /** * Sublasses must implement this method. * * @see KeyProvider#invalidOpenKey */ protected abstract void invalidOpenKeyImpl(); /** * This hook may be overridden to reset this key provider instance. * The implementation in this class does nothing. */ public void reset() { // Do NOT call this - it limits the reusability! //resetKey(); } /** * Returns a clone of the key, which may be {@code null}. * If the key is an array, a shallow copy of the array is * returned. * When overriding this method, please consider that the key * may be {@code null}. * * @deprecated You should not use or override this method. * This method will vanish in the next major version release. * @throws RuntimeException If cloning the key results in a runtime * exception. */ protected Object cloneKey() { final Object key = getKey(); if (key == null) return null; // the clone of null is null, right? :-) return clone(key); } /** * Returns a clone of the key. * If the key is an array, a shallow copy of the array is returned. * * @throws RuntimeException If cloning the key results in a runtime * exception. */ static Object clone(final Object key) { // Could somebody please explain to me why the clone method is // declared "protected" in Object and Cloneable is just a marker // interface? // And furthermore, why does clone() called via Reflection on an // array throw a NoSuchMethodException? // Somehow, this design doesn't speak to me... final Class c = key.getClass(); if (c.isArray()) { final int l = Array.getLength(key); final Object p = Array.newInstance(c.getComponentType(), l); System.arraycopy(key, 0, p, 0, l); return p; } else { try { return key.getClass().getMethod("clone", null).invoke(key, null); } catch (RuntimeException ex) { throw ex; // pass on } catch (Exception ex) { throw new RuntimeException(ex); } } } /** * Clears the data structure of the key itself and sets the * reference to it to {@code null}. * If the key is an array, the array is filled with zero values * before setting the reference to {@code null}. * When overwriting this method, please consider that the key * may be {@code null}. * * @deprecated You should not use or override this method. * This method will vanish in the next major version release. */ protected void resetKey() { final Object key = getKey(); if (key == null) return; setKey(null); synchronized (key) { if (key instanceof byte[]) Arrays.fill((byte[]) key, (byte) 0); else if (key instanceof char[]) Arrays.fill((char[]) key, (char) 0); else if (key instanceof short[]) Arrays.fill((short[]) key, (short) 0); else if (key instanceof int[]) Arrays.fill((int[]) key, 0); else if (key instanceof long[]) Arrays.fill((long[]) key, (long) 0); else if (key instanceof float[]) Arrays.fill((float[]) key, (float) 0); else if (key instanceof double[]) Arrays.fill((double[]) key, (double) 0); else if (key instanceof boolean[]) Arrays.fill((boolean[]) key, false); else if (key instanceof Object[]) Arrays.fill((Object[]) key, null); } } private void enforceSuspensionPenalty() { final long last = invalidated.getValue(); long delay; InterruptedException interrupted = null; while ((delay = System.currentTimeMillis() - last) < MIN_KEY_RETRY_DELAY) { try { Thread.sleep(MIN_KEY_RETRY_DELAY - delay); } catch (InterruptedException ex) { interrupted = ex; } } if (interrupted != null) Thread.currentThread().interrupt(); } /** * Maps this instance as the key provider for the given resource * identifier in the {@link KeyManager}. *

* The key manager will use this method whenever it adds a key provider * which is actually an instance of this class. * This allows subclasses to add additional behaviour or constraints * whenever an instance is mapped in the {@code KeyManager}. * * @param resourceID The resource identifier to map this instance for. * * @return The key provider previously mapped for the given resource * identifier or {@code null} if no key provider was mapped. * * @throws NullPointerException If {@code resourceID} is * {@code null}. * @throws IllegalStateException If mapping this instance is prohibited * by a constraint in a subclass. * Please refer to the respective subclass documentation for * more information about its constraint(s). */ protected KeyProvider addToKeyManager(String resourceID) throws NullPointerException, IllegalStateException { return KeyManager.mapKeyProvider(resourceID, this); } /** * Remove this instance as the key provider for the given resource * identifier from the map in the {@link KeyManager}. *

* The key manager will use this method whenever it adds a key provider * which is actually an instance of this class. * This allows subclasses to add additional behaviour or constraints * whenever an instance is unmapped in the {@code KeyManager}. * * @param resourceID The resource identifier to unmap this instance from. * @return The key provider previously mapped for the given resource * identifier. * @throws NullPointerException If {@code resourceID} is * {@code null}. * @throws IllegalStateException If unmapping this instance is prohibited * by a constraint in a subclass. * Please refer to the respective subclass documentation for * more information about its constraint(s). */ protected KeyProvider removeFromKeyManager(String resourceID) throws NullPointerException, IllegalStateException { return KeyManager.unmapKeyProvider(resourceID); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy