package org.appwork.storage; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import org.appwork.utils.Application; import org.appwork.utils.logging.Log; public class JsonKeyValueStorage extends Storage { private final HashMap<String, Object> map; private final String name; private final File file; private final boolean plain; private final byte[] key; private boolean autoPutValues = true; private boolean closed = false; public JsonKeyValueStorage(final File file) throws StorageException { this(file, false); } public JsonKeyValueStorage(final File file, final boolean plain) throws StorageException { this(file, plain, JSonStorage.KEY); } public JsonKeyValueStorage(final File file, final boolean plain, final byte[] key) throws StorageException { this.map = new HashMap<String, Object>(); this.plain = plain; this.file = file; this.name = file.getName(); this.key = key; synchronized (JSonStorage.LOCK) { final HashMap<String, Object> load = JSonStorage.restoreFrom(file, plain, key, new TypeRef<HashMap<String, Object>>() { }, new HashMap<String, Object>()); this.map.putAll(load); } } public JsonKeyValueStorage(final String name) throws StorageException { this(name, false); } public JsonKeyValueStorage(final String name, final boolean plain) throws StorageException { this(name, plain, JSonStorage.KEY); } public JsonKeyValueStorage(final String name, final boolean plain, final byte[] key) throws StorageException { this.map = new HashMap<String, Object>(); this.name = name; this.plain = plain; this.file = Application.getResource("cfg/" + name + (plain ? ".json" : ".ejs")); Log.L.finer("Read Config: " + this.file.getAbsolutePath()); this.key = key; synchronized (JSonStorage.LOCK) { final HashMap<String, Object> load = JSonStorage.restoreFrom(this.file, plain, key, new TypeRef<HashMap<String, Object>>() { }, new HashMap<String, Object>()); // Log.L.finer(JSonStorage.toString(load)); this.map.putAll(load); } } @Override public void clear() throws StorageException { Entry<String, Object> next; for (final Iterator<Entry<String, Object>> it = this.map.entrySet().iterator(); it.hasNext();) { next = it.next(); it.remove(); this.getEventSender().fireEvent(new StorageKeyRemovedEvent<Object>(this, next.getKey(), next.getValue())); } this.map.clear(); } /* * (non-Javadoc) * * @see org.appwork.storage.Storage#close() */ @Override public void close() { this.closed = true; } @Override public long decrease(final String key) { long ret = this.get(key, 0l).intValue(); this.put(key, --ret); return ret; } @SuppressWarnings("unchecked") @Override public <E> E get(final String key, final E def) throws StorageException { final boolean contains = this.map.containsKey(key); Object ret = contains ? this.map.get(key) : null; if (ret != null && def != null && ret.getClass() != def.getClass()) { /* ret class different from def class, so we have to convert */ if (ret instanceof ArrayList<?>) { int index = 0; /* array of something */ if (def instanceof int[]) { final int[] rets = new int[((ArrayList<?>) ret).size()]; for (final Object o : (ArrayList<?>) ret) { if (o instanceof Integer) { rets[index++] = (Integer) o; } else { throw new RuntimeException("Cannot cast " + o.getClass() + " to int"); } } ret = rets; } else if (def instanceof Integer[]) { final Integer[] rets = new Integer[((ArrayList<?>) ret).size()]; for (final Object o : (ArrayList<?>) ret) { if (o instanceof Integer) { rets[index++] = (Integer) o; } else { throw new RuntimeException("Cannot cast " + o.getClass() + " to Integer"); } } ret = rets; } else if (def instanceof long[]) { final long[] rets = new long[((ArrayList<?>) ret).size()]; for (final Object o : (ArrayList<?>) ret) { if (o instanceof Long) { rets[index++] = (Long) o; } else if (o instanceof Integer) { rets[index++] = ((Integer) o).longValue(); } else { throw new RuntimeException("Cannot cast " + o.getClass() + " to long"); } } ret = rets; } else if (def instanceof Long[]) { final Long[] rets = new Long[((ArrayList<?>) ret).size()]; for (final Object o : (ArrayList<?>) ret) { if (o instanceof Integer) { rets[index++] = ((Integer) o).longValue(); } else if (o instanceof Long) { rets[index++] = (Long) o; } else { throw new RuntimeException("Cannot cast " + o.getClass() + " to Long"); } } ret = rets; } else if (def instanceof String[]) { final String[] rets = new String[((ArrayList<?>) ret).size()]; for (final Object o : (ArrayList<?>) ret) { if (o instanceof String) { rets[index++] = (String) o; } else { throw new RuntimeException("Cannot cast " + o.getClass() + " to Long"); } } ret = rets; } } if (def instanceof Long) { if (ret instanceof Integer) { // this is normal, because jackson converts tiny longs to // ints automatically // Log.exception(Level.FINE, new // Exception("Had to convert integer to long for storage " + // this.name + "." + key + "=" + ret)); ret = new Long(((Integer) ret).longValue()); } } else if (def instanceof Integer) { if (ret instanceof Long) { // Log.exception(Level.FINE, new // Exception("Had to convert long to integer for storage " + // this.name + "." + key + "=" + ret)); ret = new Integer(((Long) ret).intValue()); } } } // put entry if we have no entry if (!contains && this.autoPutValues) { ret = def; if (def instanceof Boolean) { this.put(key, (Boolean) def); } else if (def instanceof Long) { this.put(key, (Long) def); } else if (def instanceof Long[]) { this.put(key, (Long[]) def); } else if (def instanceof long[]) { this.put(key, (long[]) def); } else if (def instanceof Integer) { this.put(key, (Integer) def); } else if (def instanceof Integer[]) { this.put(key, (Integer[]) def); } else if (def instanceof int[]) { this.put(key, (int[]) def); } else if (def instanceof Byte) { this.put(key, (Byte) def); } else if (def instanceof String || def == null) { this.put(key, (String) def); } else if (def instanceof String[]) { this.put(key, (String[]) def); } else if (def instanceof Enum<?>) { this.put(key, (Enum<?>) def); } else if (def instanceof Double) { this.put(key, (Double) def); } else { throw new StorageException("Invalid datatype: " + (def != null ? def.getClass() : "null")); } } if (def instanceof Enum<?> && ret instanceof String) { try { ret = Enum.valueOf(((Enum<?>) def).getDeclaringClass(), (String) ret); } catch (final Throwable e) { Log.exception(e); if (this.autoPutValues) { this.put(key, (Enum<?>) def); } ret = def; } } return (E) ret; } public File getFile() { return this.file; } /* * (non-Javadoc) * * @see org.appwork.storage.Storage#getID() */ @Override public String getID() { return this.file.getAbsolutePath(); } /** * @return the key */ public byte[] getKey() { return this.key; } public String getName() { return this.name; } /* * (non-Javadoc) * * @see org.appwork.storage.Storage#hasProperty(java.lang.String) */ @Override public boolean hasProperty(final String key) { // TODO Auto-generated method stub return this.map.containsKey(key); } @Override public long increase(final String key) { long ret = this.get(key, 0).intValue(); this.put(key, ++ret); return ret; } /** * @return the autoPutValues */ public boolean isAutoPutValues() { return this.autoPutValues; } public boolean isPlain() { return this.plain; } public void put(final String key, final boolean value) throws StorageException { final boolean contains = this.map.containsKey(key); final boolean old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Boolean>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Boolean>(this, key, value)); } } @Override public void put(final String key, final Boolean value) throws StorageException { final boolean contains = this.map.containsKey(key); final Boolean old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Boolean>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Boolean>(this, key, value)); } } @Override public void put(final String key, final Byte value) throws StorageException { final boolean contains = this.map.containsKey(key); final Byte old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Byte>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Byte>(this, key, value)); } } @Override public void put(final String key, final Double value) throws StorageException { final boolean contains = this.map.containsKey(key); final Double old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Double>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Double>(this, key, value)); } } @Override public void put(final String key, final Enum<?> value) throws StorageException { final boolean contains = this.map.containsKey(key); final Enum<?> old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Enum<?>>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Enum<?>>(this, key, value)); } } @Override public void put(final String key, final Float value) throws StorageException { final boolean contains = this.map.containsKey(key); final Float old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Float>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Float>(this, key, value)); } } public void put(final String key, final int value) throws StorageException { final boolean contains = this.map.containsKey(key); final Integer old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Integer>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Integer>(this, key, value)); } } public void put(final String key, final int[] value) { final boolean contains = this.map.containsKey(key); final int[] old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<int[]>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<int[]>(this, key, value)); } } @Override public void put(final String key, final Integer value) throws StorageException { final boolean contains = this.map.containsKey(key); final Integer old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Integer>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Integer>(this, key, value)); } } public void put(final String key, final Integer[] value) { final boolean contains = this.map.containsKey(key); final Integer[] old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Integer[]>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Integer[]>(this, key, value)); } } public void put(final String key, final long value) throws StorageException { final boolean contains = this.map.containsKey(key); final Long old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Long>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Long>(this, key, value)); } } @Override public void put(final String key, final Long value) throws StorageException { final boolean contains = this.map.containsKey(key); final Long old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Long>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Long>(this, key, value)); } } public void put(final String key, final long[] value) { final boolean contains = this.map.containsKey(key); final long[] old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<long[]>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<long[]>(this, key, value)); } } /** * @param key * @param def */ public void put(final String key, final Long[] value) { final boolean contains = this.map.containsKey(key); final Long[] old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<Long[]>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<Long[]>(this, key, value)); } } @Override public void put(final String key, final String value) throws StorageException { final boolean contains = this.map.containsKey(key); final String old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<String>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<String>(this, key, value)); } } public void put(final String key, final String[] value) throws StorageException { final boolean contains = this.map.containsKey(key); final String[] old = contains ? this.get(key, value) : null; this.map.put(key, value); if (contains) { this.getEventSender().fireEvent(new StorageValueChangeEvent<String[]>(this, key, old, value)); } else { this.getEventSender().fireEvent(new StorageKeyAddedEvent<String[]>(this, key, value)); } } @Override public Object remove(final String key) { Object ret; if ((ret = this.map.remove(key)) != null) { this.getEventSender().fireEvent(new StorageKeyRemovedEvent<Object>(this, key, ret)); } return ret; } @Override public void save() throws StorageException { if (this.closed) { throw new StorageException("StorageChest already closed!"); } synchronized (JSonStorage.LOCK) { String json = null; /* * writer are not threadsafe, * http://wiki.fasterxml.com/JacksonBestPracticeThreadSafety */ json = JSonStorage.getMapper().objectToString(this.map); JSonStorage.saveTo(this.file, this.plain, this.key, json); } } /** * @param autoPutValues * the autoPutValues to set */ public void setAutoPutValues(final boolean autoPutValues) { this.autoPutValues = autoPutValues; } @Override public String toString() { try { return JSonStorage.getMapper().objectToString(this.map); } catch (final Throwable e) { return this.map.toString(); } } }