From 2da419063f2fc838fba04fb17bdf0f584b73956c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 27 Apr 2013 09:23:20 +0200 Subject: [PATCH] added jaxb implementation for new configuration entry store api --- .../store/JAXBConfigurationEntryStore.java | 407 ++++++++++++++++++ .../JAXBConfigurationEntryStoreFactory.java | 113 +++++ .../java/sonia/scm/store/JAXBDataStore.java | 5 +- .../sonia/scm/store/JAXBStoreFactory.java | 11 +- .../java/sonia/scm/store/StoreConstants.java | 46 ++ .../JAXBConfigurationEntryStoreTest.java | 57 +++ 6 files changed, 627 insertions(+), 12 deletions(-) create mode 100644 scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java create mode 100644 scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java create mode 100644 scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java create mode 100644 scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java new file mode 100644 index 0000000000..b8a02b68ea --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -0,0 +1,407 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Maps; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.security.KeyGenerator; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileOutputStream; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; + +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 javax.xml.namespace.QName; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.stream.StreamSource; + +/** + * + * @author Sebastian Sdorra + * + * @param + */ +public class JAXBConfigurationEntryStore + implements ConfigurationEntryStore +{ + + /** Field description */ + private static final Object LOCK = new Object(); + + /** Field description */ + private static final String TAG_CONFIGURATION = "configuration"; + + /** Field description */ + private static final String TAG_ENTRY = "entry"; + + /** Field description */ + private static final String TAG_KEY = "key"; + + /** Field description */ + private static final String TAG_VALUE = "value"; + + /** + * the logger for JAXBConfigurationEntryStore + */ + private static final Logger logger = + LoggerFactory.getLogger(JAXBConfigurationEntryStore.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * + * @param keyGenerator + * @param file + * @param type + */ + JAXBConfigurationEntryStore(KeyGenerator keyGenerator, File file, + Class type) + { + this.keyGenerator = keyGenerator; + this.file = file; + this.type = type; + + try + { + this.context = JAXBContext.newInstance(type); + + if (file.exists()) + { + load(); + } + } + catch (JAXBException ex) + { + throw new StoreException("could not create jaxb context", ex); + } + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Override + public void clear() + { + logger.debug("clear configuration store"); + + synchronized (LOCK) + { + entries.clear(); + store(); + } + } + + /** + * Method description + * + * + * @param item + * + * @return + */ + @Override + public String put(V item) + { + String id = keyGenerator.createKey(); + + put(id, item); + + return id; + } + + /** + * Method description + * + * + * @param id + * @param item + */ + @Override + public void put(String id, V item) + { + logger.debug("put item {} to configuration store", id); + + synchronized (LOCK) + { + entries.put(id, item); + store(); + } + } + + /** + * Method description + * + * + * @param id + */ + @Override + public void remove(String id) + { + logger.debug("remove item {} from configuration store", id); + + synchronized (LOCK) + { + entries.remove(id); + store(); + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param id + * + * @return + */ + @Override + public V get(String id) + { + logger.trace("get item {} from configuration store"); + + return entries.get(id); + } + + /** + * Method description + * + * + * @return + */ + @Override + public Map getAll() + { + logger.trace("get all items from configuration store"); + + return Collections.unmodifiableMap(entries); + } + + /** + * Method description + * + * + * @param predicate + * + * @return + */ + @Override + public Collection getMatchingValues(Predicate predicate) + { + return Collections2.filter(entries.values(), predicate); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param writer + */ + private void close(XMLStreamWriter writer) + { + if (writer != null) + { + try + { + writer.close(); + } + catch (XMLStreamException ex) + { + logger.error("could not close writer", ex); + } + } + } + + /** + * Method description + * + * + * @param reader + */ + private void close(XMLStreamReader reader) + { + if (reader != null) + { + try + { + reader.close(); + } + catch (XMLStreamException ex) + { + logger.error("could not close reader", ex); + } + } + } + + /** + * Method description + * + * + * @return + */ + private void load() + { + logger.debug("load configuration from {}", file); + + XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + + XMLStreamReader reader = null; + + try + { + Unmarshaller u = context.createUnmarshaller(); + + reader = xmlInputFactory.createXMLStreamReader(new StreamSource(file)); + reader.nextTag(); + reader.nextTag(); + + while (reader.isStartElement() && reader.getLocalName().equals(TAG_ENTRY)) + { + + // read key + reader.nextTag(); + + String key = reader.getElementText(); + + // read entry + reader.nextTag(); + + V v = (V) u.unmarshal(reader, type).getValue(); + + entries.put(key, v); + } + } + catch (Exception ex) + { + throw new StoreException("could not load configuration", ex); + } + finally + { + close(reader); + } + } + + /** + * Method description + * + */ + private void store() + { + logger.debug("store configuration to {}", file); + + XMLStreamWriter writer = null; + + try + { + writer = XMLOutputFactory.newFactory().createXMLStreamWriter( + new FileOutputStream(file)); + writer.writeStartDocument(); + writer.writeStartElement(TAG_CONFIGURATION); + + Marshaller m = context.createMarshaller(); + + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + + for (Entry e : entries.entrySet()) + { + writer.writeStartElement(TAG_ENTRY); + writer.writeStartElement(TAG_KEY); + writer.writeCharacters(e.getKey()); + writer.writeEndElement(); + + JAXBElement je = new JAXBElement(QName.valueOf(TAG_VALUE), type, + e.getValue()); + + m.marshal(je, writer); + } + + writer.writeEndElement(); + writer.writeEndDocument(); + } + catch (Exception ex) + { + throw new StoreException("could not store configuration", ex); + } + finally + { + close(writer); + } + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private JAXBContext context; + + /** Field description */ + private Map entries = Maps.newHashMap(); + + /** Field description */ + private File file; + + /** Field description */ + private KeyGenerator keyGenerator; + + /** Field description */ + private Class type; +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java new file mode 100644 index 0000000000..296d03cd70 --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.SCMContextProvider; +import sonia.scm.security.KeyGenerator; +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; + +/** + * + * @author Sebastian Sdorra + */ +@Singleton +public class JAXBConfigurationEntryStoreFactory + implements ConfigurationEntryStoreFactory +{ + + /** + * the logger for JAXBConfigurationEntryStoreFactory + */ + private static final Logger logger = + LoggerFactory.getLogger(JAXBConfigurationEntryStoreFactory.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param keyGenerator + * @param context + */ + @Inject + public JAXBConfigurationEntryStoreFactory(KeyGenerator keyGenerator, + SCMContextProvider context) + { + this.keyGenerator = keyGenerator; + directory = new File(context.getBaseDirectory(), + StoreConstants.CONFIGDIRECTORY_NAME); + IOUtil.mkdirs(directory); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param type + * @param name + * @param + * + * @return + */ + @Override + public ConfigurationEntryStore getStore(Class type, String name) + { + logger.debug("create new configuration store for type {} with name {}", + type, name); + + return new JAXBConfigurationEntryStore(keyGenerator, + new File(directory, name.concat(StoreConstants.FILE_EXTENSION)), type); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private File directory; + + /** Field description */ + private KeyGenerator keyGenerator; +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java index 40b22a182b..40ba0a2053 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java @@ -61,9 +61,6 @@ import javax.xml.bind.Marshaller; public class JAXBDataStore extends FileBasedStore implements DataStore { - /** Field description */ - private static final String SUFFIX = ".xml"; - /** * the logger for JAXBDataStore */ @@ -82,7 +79,7 @@ public class JAXBDataStore extends FileBasedStore implements DataStore */ public JAXBDataStore(KeyGenerator keyGenerator, Class type, File directory) { - super(directory, SUFFIX); + super(directory, StoreConstants.FILE_EXTENSION); this.keyGenerator = keyGenerator; try diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java index 3291be4806..34f8fb3c98 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBStoreFactory.java @@ -56,12 +56,6 @@ import java.io.IOException; public class JAXBStoreFactory implements ListenableStoreFactory { - /** Field description */ - public static final String CONFIGDIRECTORY_NAME = "config"; - - /** Field description */ - public static final String FILE_EXTENSION = ".xml"; - /** the logger for JAXBStoreFactory */ private static final Logger logger = LoggerFactory.getLogger(JAXBStoreFactory.class); @@ -91,7 +85,7 @@ public class JAXBStoreFactory implements ListenableStoreFactory public void init(SCMContextProvider context) { configDirectory = new File(context.getBaseDirectory(), - CONFIGDIRECTORY_NAME); + StoreConstants.CONFIGDIRECTORY_NAME); IOUtil.mkdirs(configDirectory); } @@ -115,7 +109,8 @@ public class JAXBStoreFactory implements ListenableStoreFactory throw new IllegalStateException("store factory is not initialized"); } - File configFile = new File(configDirectory, name.concat(FILE_EXTENSION)); + File configFile = new File(configDirectory, + name.concat(StoreConstants.FILE_EXTENSION)); if (logger.isDebugEnabled()) { diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java b/scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java new file mode 100644 index 0000000000..a44fda417d --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/StoreConstants.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.store; + +/** + * + * @author Sebastian Sdorra + */ +public interface StoreConstants +{ + + /** Field description */ + public static final String CONFIGDIRECTORY_NAME = "config"; + + /** Field description */ + public static final String FILE_EXTENSION = ".xml"; +} diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java new file mode 100644 index 0000000000..7e393454aa --- /dev/null +++ b/scm-dao-xml/src/test/java/sonia/scm/store/JAXBConfigurationEntryStoreTest.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.store; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.security.UUIDKeyGenerator; + +/** + * + * @author Sebastian Sdorra + */ +public class JAXBConfigurationEntryStoreTest extends ConfigurationEntryStoreTestBase +{ + + /** + * Method description + * + * + * @return + */ + @Override + protected ConfigurationEntryStoreFactory createConfigurationStoreFactory() + { + return new JAXBConfigurationEntryStoreFactory(new UUIDKeyGenerator(), + contextProvider); + } +}