de.schlichtherle.key.PromptingKeyManager Maven / Gradle / Ivy
Show all versions of truezip Show documentation
/*
* 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 java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.Map;
/**
* An abstract {@link KeyManager} which prompts the user for a key if required.
*
* This class maintains a map of user interface classes for the
* {@code PromptingKeyProvider} class and each of its subclasses which
* require an individual user interface.
* The particular user interface classes are determined by a subclass of this
* key manager. This enables the subclass to determine which user interface
* technology should actually be used to prompt the user for a key.
* For example, the implementation in the class
* {@link de.schlichtherle.key.passwd.swing.PromptingKeyManager} uses Swing
* to prompt the user for either a password or a key file.
*
* Subclasses must use the method {@link #mapPromptingKeyProviderUIType} to
* register a user interface class for a particular user interface class
* identifier (the value returned by {@link PromptingKeyProvider#getUIClassID}).
* This is best done in the constructor of the subclass.
*
* Note that class loading and instantiation may happen in a JVM shutdown hook,
* so class initializers and constructors must behave accordingly.
* In particular, it's not permitted to construct or use a Swing GUI there.
*
* This class is thread safe.
*
* @author Christian Schlichtherle
* @version $Id$
* @since TrueZIP 6.0
*/
public abstract class PromptingKeyManager extends KeyManager {
private static volatile boolean prompting = true;
private final Map providerUITypes = new HashMap();
/**
* Constructs a new {@code PromptingKeyManager}.
* This instance maps the following key provider types using
* {@link KeyManager#mapKeyProviderType}:
*
*
* forKeyProviderType
* useKeyProviderType
*
*
* KeyProvider.class
* PromptingKeyProvider.class
*
*
* AesKeyProvider.class
* PromptingAesKeyProvider.class
*
*
*/
public PromptingKeyManager() {
mapKeyProviderType(KeyProvider.class, PromptingKeyProvider.class);
mapKeyProviderType(AesKeyProvider.class, PromptingAesKeyProvider.class);
}
//
// Static methods:
//
/**
* Returns {@code true} if and only if prompting mode is enabled.
* This is a class property.
*
* Note that subclasses might add additional behaviour to both
* {@link #isPrompting} and {@link #setPrompting} through the default
* key manager instance (see {@link #getInstance}).
* Regardless, an application may safely assume that
* {@code isPrompting()} reflects the actual behaviour of the API
* in this package although it may not reflect the parameter value of
* the last call to {@code setPrompting(boolean)}.
*
* @return Whether or not the user will be prompted for a key if required.
*
* @see #setPrompting
*/
public static boolean isPrompting() {
KeyManager manager = getInstance();
return manager instanceof PromptingKeyManager
&& ((PromptingKeyManager) manager).isPromptingImpl();
}
/**
* Called by {@link #isPrompting} on the default key manager instance in
* order to implement its behaviour and allow subclasses to override it.
* Subclasses should call the implementation in this class when
* overriding this method.
*
* @see #setPromptingImpl
* @see #getInstance
*
* @since TrueZIP 6.1
*/
protected boolean isPromptingImpl() {
return prompting;
}
/**
* Enables or disables prompting mode.
* If prompting mode is enabled, the user will be prompted for a key when
* a {@link PromptingKeyProvider} is first requested to provide a key
* for the respective resource.
* If prompting mode is disabled, all attempts to prompt the user will
* result in an {@link UnknownKeyException} until prompting mode is
* enabled again.
*
* This is a class property.
*
* Note that subclasses might add additional behaviour to both
* {@link #isPrompting} and {@link #setPrompting} through the default
* key manager instance (see {@link #getInstance}).
* Regardless, an application may safely assume that
* {@code isPrompting()} reflects the actual behaviour of the API
* in this package although it may not reflect the parameter value of
* the last call to {@code setPrompting(boolean)}.
*
* @param prompting The value of the property {@code prompting}.
*
* @see #isPrompting
*/
public static void setPrompting(boolean prompting) {
KeyManager manager = getInstance();
if (manager instanceof PromptingKeyManager)
((PromptingKeyManager) manager).setPromptingImpl(prompting);
}
/**
* Called by {@link #isPrompting} on the default key manager instance in
* order to implement its behaviour and allow subclasses to override it.
* Subclasses should call the implementation in this class when
* overriding this method.
*
* @see #isPromptingImpl
* @see #getInstance
*
* @since TrueZIP 6.1
*/
protected void setPromptingImpl(boolean prompting) {
PromptingKeyManager.prompting = prompting;
}
static void ensurePrompting()
throws KeyPromptingDisabledException {
KeyManager manager = getInstance();
if (manager instanceof PromptingKeyManager)
((PromptingKeyManager) manager).ensurePromptingImpl();
}
/**
* Called by some methods in the {@link PromptingKeyProvider} class in
* order to ensure that prompting mode is enabled.
* This method may be overridden by subclasses in order to throw a more
* detailed exception.
*
* The implementation in this class is equivalent to:
*
if (!isPromptingImpl())
throw new KeyPromptingDisabledException();
*
*
* @since TrueZIP 6.1
*/
protected void ensurePromptingImpl()
throws KeyPromptingDisabledException {
if (!isPromptingImpl())
throw new KeyPromptingDisabledException();
}
/**
* Resets all cancelled key prompts, forcing a new prompt on the next
* call to {@link PromptingKeyProvider#getOpenKey()} or
* {@link PromptingKeyProvider#getCreateKey()}.
* Of course, this call only affects instances of
* {@link PromptingKeyProvider}.
*/
public static void resetCancelledPrompts() {
forEachKeyProvider(new KeyProviderCommand() {
public void run(String resourceID, KeyProvider provider) {
if (provider instanceof PromptingKeyProvider)
((PromptingKeyProvider) provider).resetCancelledPrompt();
}
});
}
//
// Instance stuff:
//
/**
* @deprecated Use {@link #mapPromptingKeyProviderUIType(String, Class)
* mapPromptingKeyProviderUIType(uiClassID, uiClass)} instead.
*/
protected final void register(
final String uiClassID,
final Class uiClass) {
mapPromptingKeyProviderUIType(uiClassID, uiClass);
}
/**
* Subclasses must use this method to register a user interface class
* for a particular user interface class identifier as returned by
* {@link PromptingKeyProvider#getUIClassID}.
* This is best done in the constructor of the subclass.
*
* @param uiClassID The identifier of the user interface class.
* @param uiClass The user interface class.
* This class must have a nullary constructor.
* @see #getKeyProvider(String, Class)
* @since TrueZIP 6.1
* @throws NullPointerException If any of the parameters is
* {@code null}.
* @throws IllegalArgumentException If {@code uiClass} does not
* provide a public constructor with no parameters.
*/
protected synchronized final void mapPromptingKeyProviderUIType(
final String uiClassID,
final Class uiClass) {
if (uiClassID == null)
throw new NullPointerException("uiClassID");
if (!PromptingKeyProviderUI.class.isAssignableFrom(uiClass))
throw new IllegalArgumentException(
uiClass.getName() + " must be PromptingKeyProviderUI or a subclass!");
try {
uiClass.getConstructor(null);
} catch (NoSuchMethodException noPublicNullaryConstructor) {
final IllegalArgumentException iae = new IllegalArgumentException(
uiClass.getName() + " (no public nullary constructor)");
iae.initCause(noPublicNullaryConstructor);
throw iae;
}
providerUITypes.put(uiClassID, uiClass);
}
/**
* Behaves like the super class implementation, but adds additional
* behaviour in case the resulting key provider is an instance of
* {@link PromptingKeyProvider}.
* In this case, the appropriate user interface instance is determined
* and installed in the key provider before it is returned.
*
* @see KeyManager#getKeyProvider(String, Class)
*/
public KeyProvider getKeyProvider(
final String resourceID,
final Class keyProviderType)
throws NullPointerException, ClassCastException, IllegalArgumentException {
final KeyProvider provider
= super.getKeyProvider(resourceID, keyProviderType);
if (provider instanceof PromptingKeyProvider) {
final PromptingKeyProvider pkp = (PromptingKeyProvider) provider;
pkp.setUI(getUI(pkp.getUIClassID()));
}
return provider;
}
private synchronized PromptingKeyProviderUI getUI(final String uiClassID) {
final Object value = providerUITypes.get(uiClassID);
final PromptingKeyProviderUI pkpui;
if (value instanceof Class) {
try {
pkpui = (PromptingKeyProviderUI) ((Class) value).newInstance();
} catch (InstantiationException failure) {
throw new UndeclaredThrowableException(failure);
} catch (IllegalAccessException failure) {
throw new UndeclaredThrowableException(failure);
}
providerUITypes.put(uiClassID, pkpui);
} else if (value != null) {
pkpui = (PromptingKeyProviderUI) value;
} else { // value == null
throw new IllegalArgumentException(uiClassID +
" (unknown user interface for PromptingKeyProvider)");
}
return pkpui;
}
}