package kvstore; import static kvstore.KVConstants.ERROR_NO_SUCH_KEY; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.OutputStream; import java.io.FileOutputStream; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import kvstore.xml.KVPairType; import kvstore.xml.KVStoreType; import kvstore.xml.ObjectFactory; /** * This is a basic key-value store. Ideally this would go to disk, or some other * backing store. */ public class KVStore implements KeyValueInterface { public ConcurrentHashMap<String, String> store; /** * Construct a new KVStore. */ public KVStore() { resetStore(); } public void resetStore() { this.store = new ConcurrentHashMap<String, String>(); } /** * Insert key, value pair into the store. * * @param key String key * @param value String value */ @Override public void put(String key, String value) { store.put(key, value); } /** * Retrieve the value corresponding to the provided key * @param key String key * @throws KVException with ERROR_NO_SUCH_KEY if key does not exist in store */ @Override public String get(String key) throws KVException { String retVal = this.store.get(key); if (retVal == null) { KVMessage msg = new KVMessage(KVConstants.RESP, ERROR_NO_SUCH_KEY); throw new KVException(msg); } return retVal; } /** * Delete the value corresponding to the provided key. * * @param key String key * @throws KVException with ERROR_NO_SUCH_KEY if key does not exist in store */ @Override public void del(String key) throws KVException { if(key != null) { if (!this.store.containsKey(key)) { KVMessage msg = new KVMessage(KVConstants.RESP, ERROR_NO_SUCH_KEY); throw new KVException(msg); } this.store.remove(key); } } private synchronized JAXBElement<KVStoreType> getXMLRoot() throws JAXBException { ObjectFactory factory = new ObjectFactory(); KVStoreType xmlStore = factory.createKVStoreType(); for (Entry<String, String> e : store.entrySet()) { KVPairType kvPair = factory.createKVPairType(); kvPair.setKey(e.getKey()); kvPair.setValue(e.getValue()); xmlStore.getKVPair().add(kvPair); } return factory.createKVStore(xmlStore); } private void marshalTo(OutputStream os) throws JAXBException { JAXBContext context = JAXBContext.newInstance(KVStoreType.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty("com.sun.xml.internal.bind.xmlHeaders", "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false); marshaller.marshal(getXMLRoot(), os); } private KVStoreType unmarshal(File f) throws JAXBException { JAXBContext context = JAXBContext.newInstance(ObjectFactory.class); Unmarshaller unmarshaller = context.createUnmarshaller(); KVStoreType xmlStore = ((JAXBElement<KVStoreType>) unmarshaller.unmarshal(f)).getValue(); return xmlStore; } /** * Serialize this store to XML. See the spec for specific output format. * This method is best effort. Any exceptions that appear can be dropped. */ public String toXML() { ByteArrayOutputStream os = new ByteArrayOutputStream(); try { marshalTo(os); } catch (JAXBException e) { e.printStackTrace(); } return os.toString(); } @Override public String toString() { return this.toXML(); } /** * Serialize to XML and write the output to a file. * This method is best effort. Any exceptions that arise can be dropped. * * @param fileName the file to write the serialized store */ public void dumpToFile(String fileName) { try { File f = new File(fileName); FileOutputStream os = new FileOutputStream(f); marshalTo(os); } catch (Exception e) {} } /** * Replaces the contents of the store with the contents of a file * written by dumpToFile; the previous contents of the store are lost. * The store is cleared even if the file does not exist. * This method is best effort. Any exceptions that arise can be dropped. * * @param fileName the file containing the serialized store data */ public void restoreFromFile(String fileName) { resetStore(); try { KVStoreType kvst = unmarshal(new File(fileName)); for(KVPairType pair : kvst.getKVPair()) put(pair.getKey(), pair.getValue()); } catch (Exception e) {} } }