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

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

/*
 * 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: AbstractKeyProvider.java,v 1.4 2010/08/20 13:09:45 christian_schlichtherle Exp $ * @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 - 2025 Weber Informatics LLC | Privacy Policy