/** * Copyright (c) 2009 - 2011 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.storage.config * * This software is licensed under the Artistic License 2.0, * see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php * for details */ package org.appwork.storage.config; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.appwork.storage.JSonStorage; import org.appwork.storage.StorageException; import org.appwork.storage.TypeRef; import org.appwork.storage.config.annotations.CryptedStorage; import org.appwork.storage.config.annotations.DefaultBooleanArrayValue; import org.appwork.storage.config.annotations.DefaultBooleanValue; import org.appwork.storage.config.annotations.DefaultByteArrayValue; import org.appwork.storage.config.annotations.DefaultByteValue; import org.appwork.storage.config.annotations.DefaultDoubleArrayValue; import org.appwork.storage.config.annotations.DefaultDoubleValue; import org.appwork.storage.config.annotations.DefaultEnumArrayValue; import org.appwork.storage.config.annotations.DefaultEnumValue; import org.appwork.storage.config.annotations.DefaultFloatArrayValue; import org.appwork.storage.config.annotations.DefaultFloatValue; import org.appwork.storage.config.annotations.DefaultIntArrayValue; import org.appwork.storage.config.annotations.DefaultIntValue; import org.appwork.storage.config.annotations.DefaultLongArrayValue; import org.appwork.storage.config.annotations.DefaultLongValue; import org.appwork.storage.config.annotations.DefaultObjectValue; import org.appwork.storage.config.annotations.DefaultStringArrayValue; import org.appwork.storage.config.annotations.DefaultStringValue; import org.appwork.storage.config.annotations.PlainStorage; import org.appwork.utils.logging.Log; import org.appwork.utils.reflection.Clazz; /** * @author thomas * */ public class MethodHandler { public static enum Type { GETTER, SETTER; } private final Type type; private final String key; private final Method method; private final boolean primitive; private Class<?> rawClass; private boolean defaultBoolean = false; private long defaultLong = 0l; private int defaultInteger = 0; private Enum<?> defaultEnum = null; private double defaultDouble = 0.0d; private String defaultString = null; private byte defaultByte = 0; private Object defaultObject = null; private float defaultFloat = 0.0f; private boolean crypted; private byte[] cryptKey; private final File path; private final Object defaultFactory; /** * @param getter * @param key * @param m * @param canStorePrimitive * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException * @throws ClassNotFoundException */ @SuppressWarnings({ "unchecked", "rawtypes" }) public MethodHandler(final StorageHandler<?> storageHandler, final Type getter, final String key, final Method m, final boolean primitive) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { this.type = getter; this.key = key; this.method = m; this.primitive = primitive; if (this.isGetter()) { this.rawClass = m.getReturnType(); } else { this.rawClass = m.getParameterTypes()[0]; } // get parent crypt infos this.crypted = storageHandler.isCrypted(); this.cryptKey = storageHandler.getKey(); this.path = new File(storageHandler.getPath() + "." + this.key + "." + (this.isCrypted() ? "ejs" : "json")); // read local cryptinfos final CryptedStorage an = m.getAnnotation(CryptedStorage.class); if (an != null) { this.crypted = true; if (an.key() != null) { this.cryptKey = an.key(); if (this.cryptKey.length != JSonStorage.KEY.length) { throw new InterfaceParseException("Crypt key for " + m + " is invalid"); } } } final PlainStorage anplain = m.getAnnotation(PlainStorage.class); if (anplain != null && this.crypted) { if (an != null) { throw new InterfaceParseException("Cannot Set CryptStorage and PlainStorage Annotation"); } // parent crypted, but plain for this single entry this.crypted = false; } /* * the following is all default value creation. To speed this up, we * could implement a @NoDefaultValues Annotation */ // init defaultvalues. read from annotations this.defaultFactory = storageHandler.getDefaultFactory(); final boolean hasDefaultFactory = storageHandler.getDefaultFactory() != null; if (primitive) { if (Clazz.isBoolean(this.rawClass)) { final DefaultBooleanValue ann = m.getAnnotation(DefaultBooleanValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultBoolean = ann.value(); } this.checkBadAnnotations(m, DefaultBooleanValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isLong(this.rawClass)) { final DefaultLongValue ann = m.getAnnotation(DefaultLongValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultLong = ann.value(); } this.checkBadAnnotations(m, DefaultLongValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isInteger(this.rawClass)) { final DefaultIntValue ann = m.getAnnotation(DefaultIntValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultInteger = ann.value(); } this.checkBadAnnotations(m, DefaultIntValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isByte(this.rawClass)) { final DefaultByteValue ann = m.getAnnotation(DefaultByteValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultByte = ann.value(); } this.checkBadAnnotations(m, DefaultByteValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isFloat(this.rawClass)) { final DefaultFloatValue ann = m.getAnnotation(DefaultFloatValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultFloat = ann.value(); } this.checkBadAnnotations(m, DefaultFloatValue.class, CryptedStorage.class, PlainStorage.class); } else if (this.rawClass == String.class) { final DefaultStringValue ann = m.getAnnotation(DefaultStringValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultString = ann.value(); } this.checkBadAnnotations(m, DefaultStringValue.class, CryptedStorage.class, PlainStorage.class); } else if (this.rawClass.isEnum()) { final DefaultEnumValue ann = m.getAnnotation(DefaultEnumValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } // chek if this is really the best way to convert string to // enum final int index = ann.value().lastIndexOf("."); final String name = ann.value().substring(index + 1); final String clazz = ann.value().substring(0, index); this.defaultEnum = Enum.valueOf((Class<Enum>) Class.forName(clazz), name); } this.checkBadAnnotations(m, DefaultEnumValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isDouble(this.rawClass)) { final DefaultDoubleValue ann = m.getAnnotation(DefaultDoubleValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultDouble = ann.value(); } this.checkBadAnnotations(m, DefaultDoubleValue.class, CryptedStorage.class, PlainStorage.class); } else { throw new StorageException("Invalid datatype: " + this.rawClass); } } else { if (this.rawClass.isArray()) { final Class<?> ct = this.rawClass.getComponentType(); if (Clazz.isBoolean(ct)) { final DefaultBooleanArrayValue ann = m.getAnnotation(DefaultBooleanArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultBooleanArrayValue.class, CryptedStorage.class, PlainStorage.class); this.checkBadAnnotations(m, DefaultBooleanArrayValue.class); } else if (Clazz.isLong(ct)) { final DefaultLongArrayValue ann = m.getAnnotation(DefaultLongArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultLongArrayValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isInteger(ct)) { final DefaultIntArrayValue ann = m.getAnnotation(DefaultIntArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultIntArrayValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isByte(ct)) { final DefaultByteArrayValue ann = m.getAnnotation(DefaultByteArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultByteArrayValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isFloat(ct)) { final DefaultFloatArrayValue ann = m.getAnnotation(DefaultFloatArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultFloatArrayValue.class, CryptedStorage.class, PlainStorage.class); } else if (ct == String.class) { /* obsolet here cause String[] is primitive */ final DefaultStringArrayValue ann = m.getAnnotation(DefaultStringArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultStringArrayValue.class, CryptedStorage.class, PlainStorage.class); } else if (ct.isEnum()) { final DefaultEnumArrayValue ann = m.getAnnotation(DefaultEnumArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } // chek if this is really the best way to convert string // to // enum final Enum[] ret = new Enum[ann.value().length]; for (int i = 0; i < ret.length; i++) { final int index = ann.value()[i].lastIndexOf("."); final String name = ann.value()[i].substring(index + 1); final String clazz = ann.value()[i].substring(0, index); ret[i] = Enum.valueOf((Class<Enum>) Class.forName(clazz), name); } this.defaultObject = ret; } this.checkBadAnnotations(m, DefaultEnumArrayValue.class, CryptedStorage.class, PlainStorage.class); } else if (Clazz.isDouble(ct)) { final DefaultDoubleArrayValue ann = m.getAnnotation(DefaultDoubleArrayValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = ann.value(); } this.checkBadAnnotations(m, DefaultDoubleArrayValue.class, CryptedStorage.class, PlainStorage.class); } } if (this.defaultObject == null) { // if rawtypoe is an storable, we can have defaultvalues in // json7String from final DefaultObjectValue ann = m.getAnnotation(DefaultObjectValue.class); if (ann != null) { if (hasDefaultFactory) { throw new InterfaceParseException(m + " is not allowed to have defaultvalues because interface has a Defaultfactory. Set Defaults in " + storageHandler.getDefaultFactory().getClass().getName() + "'s getters"); } this.defaultObject = JSonStorage.restoreFromString(ann.value(), this.rawClass); } this.checkBadAnnotations(m, DefaultObjectValue.class, CryptedStorage.class, PlainStorage.class); } } } /** * @param m * @param class1 */ private void checkBadAnnotations(final Method m, final Class<? extends Annotation>... classes) { /** * This main mark is important!! */ final String packag = PlainStorage.class.getPackage().getName(); main: for (final Annotation a : m.getAnnotations()) { // all other Annotations are ok anyway if (!a.getClass().getName().startsWith(packag)) { continue; } for (final Class<? extends Annotation> ok : classes) { if (ok.isAssignableFrom(a.getClass())) { continue main; } } throw new InterfaceParseException("Bad Annotation: " + a + " for " + m); } } public byte[] getCryptKey() { return this.cryptKey; } public boolean getDefaultBoolean() { if (this.defaultFactory != null) { try { return (Boolean) this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } ; return this.defaultBoolean; } public byte getDefaultByte() { if (this.defaultFactory != null) { try { return (Byte) this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } return this.defaultByte; } public double getDefaultDouble() { if (this.defaultFactory != null) { try { return (Double) this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } return this.defaultDouble; } public Enum<?> getDefaultEnum() { if (this.defaultFactory != null) { try { final Enum<?> ret = (Enum<?>) this.method.invoke(this.defaultFactory, new Object[] {}); if (ret != null) { return ret; } } catch (final Exception e) { Log.exception(e); } } return this.defaultEnum; } public float getDefaultFloat() { if (this.defaultFactory != null) { try { return (Float) this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } return this.defaultFloat; } public int getDefaultInteger() { if (this.defaultFactory != null) { try { return (Integer) this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } return this.defaultInteger; } public long getDefaultLong() { if (this.defaultFactory != null) { try { return (Long) this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } return this.defaultLong; } public Object getDefaultObject() { if (this.defaultFactory != null) { try { return this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); } } return this.defaultObject; } public String getDefaultString() { if (this.defaultFactory != null) { try { final String ret = (String) this.method.invoke(this.defaultFactory, new Object[] {}); return ret; } catch (final Exception e) { Log.exception(e); } } return this.defaultString; } public String getKey() { return this.key; } public Method getMethod() { return this.method; } public Class<?> getRawClass() { return this.rawClass; } public Type getType() { return this.type; } /** * @return */ public boolean isCrypted() { return this.crypted; } /** * @return */ public boolean isGetter() { return this.type == Type.GETTER; } public boolean isPrimitive() { return this.primitive; } /** * @return */ @SuppressWarnings("unchecked") public Object read() { try { Log.L.finer("Read Config: " + this.path.getAbsolutePath()); if (this.defaultFactory != null) { final Object dummy = new Object(); Object ret = JSonStorage.restoreFrom(this.path, !this.crypted, this.cryptKey, new TypeRef(this.method.getGenericReturnType()) { }, dummy); if (ret == dummy) { try { ret = this.method.invoke(this.defaultFactory, new Object[] {}); } catch (final Exception e) { Log.exception(e); ret = null; } this.defaultObject = ret; } return ret; } else { return JSonStorage.restoreFrom(this.path, !this.crypted, this.cryptKey, new TypeRef(this.method.getGenericReturnType()) { }, this.defaultObject); } } finally { if (!this.path.exists()) { this.write(this.defaultObject); } } } public void setDefaultBoolean(final boolean defaultBoolean) { this.defaultBoolean = defaultBoolean; } public void setDefaultByte(final byte defaultByte) { this.defaultByte = defaultByte; } public void setDefaultDouble(final double defaultDouble) { this.defaultDouble = defaultDouble; } public void setDefaultEnum(final Enum<?> defaultEnum) { this.defaultEnum = defaultEnum; } public void setDefaultFloat(final float defaultFloat) { this.defaultFloat = defaultFloat; } public void setDefaultInteger(final int defaultInteger) { this.defaultInteger = defaultInteger; } public void setDefaultLong(final long defaultLong) { this.defaultLong = defaultLong; } public void setDefaultObject(final Object defaultObject) { this.defaultObject = defaultObject; } public void setDefaultString(final String defaultString) { this.defaultString = defaultString; } public void setRawClass(final Class<?> rawClass) { this.rawClass = rawClass; } @Override public String toString() { return this.method + ""; } /** * @param object */ public void write(final Object object) { JSonStorage.saveTo(this.path, !this.crypted, this.cryptKey, JSonStorage.serializeToJson(object)); } }