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 index b25c1c9e9a..c2047486b4 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.store; //~--- non-JDK imports -------------------------------------------------------- @@ -35,9 +35,7 @@ import sonia.scm.security.KeyGenerator; import sonia.scm.xml.IndentXMLStreamWriter; import sonia.scm.xml.XmlStreams; -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; @@ -51,94 +49,66 @@ import java.util.Map.Entry; //~--- JDK imports ------------------------------------------------------------ /** - * - * @author Sebastian Sdorra - * * @param + * @author Sebastian Sdorra */ -public class JAXBConfigurationEntryStore implements ConfigurationEntryStore -{ +public class JAXBConfigurationEntryStore implements ConfigurationEntryStore { - /** Field description */ + /** + * Field description + */ private static final String TAG_CONFIGURATION = "configuration"; - /** Field description */ + /** + * Field description + */ private static final String TAG_ENTRY = "entry"; - /** Field description */ + /** + * Field description + */ private static final String TAG_KEY = "key"; - /** Field description */ + /** + * Field description + */ private static final String TAG_VALUE = "value"; /** * the logger for JAXBConfigurationEntryStore */ - private static final Logger logger = - LoggerFactory.getLogger(JAXBConfigurationEntryStore.class); + private static final Logger LOG = LoggerFactory.getLogger(JAXBConfigurationEntryStore.class); - //~--- constructors --------------------------------------------------------- - /** - * Constructs ... - * - * - * - * @param keyGenerator - * @param file - * @param type - */ - JAXBConfigurationEntryStore(File file, KeyGenerator keyGenerator, - Class type) - { + private final File file; + private final KeyGenerator keyGenerator; + private final Class type; + private final TypedStoreContext context; + private final Map entries = Maps.newHashMap(); + + JAXBConfigurationEntryStore(File file, KeyGenerator keyGenerator, Class type, TypedStoreContext context) { this.file = file; this.keyGenerator = keyGenerator; 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); + this.context = context; + // initial load + if (file.exists()) { + load(); } } - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - */ @Override - public void clear() - { - logger.debug("clear configuration store"); + public void clear() { + LOG.debug("clear configuration store"); - synchronized (file) - { + synchronized (file) { entries.clear(); store(); } } - /** - * Method description - * - * - * @param item - * - * @return - */ @Override - public String put(V item) - { + public String put(V item) { String id = keyGenerator.createKey(); put(id, item); @@ -146,225 +116,141 @@ public class JAXBConfigurationEntryStore implements ConfigurationEntryStore getAll() - { - logger.trace("get all items from configuration store"); + public Map getAll() { + LOG.trace("get all items from configuration store"); return Collections.unmodifiableMap(entries); } - /** - * Method description - * - * - * @param predicate - * - * @return - */ @Override - public Collection getMatchingValues(Predicate predicate) - { + public Collection getMatchingValues(Predicate predicate) { return Collections2.filter(entries.values(), predicate); } - //~--- methods -------------------------------------------------------------- + private void load() { + LOG.debug("load configuration from {}", file); - /** - * Method description - * - * - * @return - */ - private void load() - { - logger.debug("load configuration from {}", file); + context.withUnmarshaller(u -> { + XMLStreamReader reader = null; + try { + reader = XmlStreams.createReader(file); - XMLStreamReader reader = null; - - try - { - Unmarshaller u = context.createUnmarshaller(); - - reader = XmlStreams.createReader(file); - - // configuration - reader.nextTag(); - - // entry start - reader.nextTag(); - - while (reader.isStartElement() && reader.getLocalName().equals(TAG_ENTRY)) - { - - // read key + // configuration reader.nextTag(); - String key = reader.getElementText(); - - // read value + // entry start reader.nextTag(); - JAXBElement element = u.unmarshal(reader, type); + while (reader.isStartElement() && reader.getLocalName().equals(TAG_ENTRY)) { - if (!element.isNil()) - { - V v = element.getValue(); - - logger.trace("add element {} to configuration entry store", v); - - entries.put(key, v); - } - else - { - logger.warn("could not unmarshall object of entry store"); - } - - // closed or new entry tag - if (reader.nextTag() == XMLStreamReader.END_ELEMENT) - { - - // fixed format, start new entry + // read key reader.nextTag(); + + String key = reader.getElementText(); + + // read value + reader.nextTag(); + + JAXBElement element = u.unmarshal(reader, type); + + if (!element.isNil()) { + V v = element.getValue(); + + LOG.trace("add element {} to configuration entry store", v); + + entries.put(key, v); + } else { + LOG.warn("could not unmarshall object of entry store"); + } + + // closed or new entry tag + if (reader.nextTag() == XMLStreamReader.END_ELEMENT) { + + // fixed format, start new entry + reader.nextTag(); + } } + } finally { + XmlStreams.close(reader); } - } - catch (Exception ex) - { - throw new StoreException("could not load configuration", ex); - } - finally - { - XmlStreams.close(reader); - } + }); } /** * Method description - * */ - private void store() - { - logger.debug("store configuration to {}", file); + private void store() { + LOG.debug("store configuration to {}", file); - CopyOnWrite.withTemporaryFile( - temp -> { - try (IndentXMLStreamWriter writer = XmlStreams.createWriter(temp)) { - writer.writeStartDocument(); + context.withMarshaller(m -> { + m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); - // configuration start - writer.writeStartElement(TAG_CONFIGURATION); + CopyOnWrite.withTemporaryFile( + temp -> { + try (IndentXMLStreamWriter writer = XmlStreams.createWriter(temp)) { + writer.writeStartDocument(); - Marshaller m = context.createMarshaller(); + // configuration start + writer.writeStartElement(TAG_CONFIGURATION); - m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); - for (Entry e : entries.entrySet()) { + for (Entry e : entries.entrySet()) { - // entry start - writer.writeStartElement(TAG_ENTRY); + // entry start + writer.writeStartElement(TAG_ENTRY); - // key start - writer.writeStartElement(TAG_KEY); - writer.writeCharacters(e.getKey()); + // key start + writer.writeStartElement(TAG_KEY); + writer.writeCharacters(e.getKey()); - // key end - writer.writeEndElement(); - - // value - JAXBElement je = new JAXBElement<>(QName.valueOf(TAG_VALUE), type, - e.getValue()); - - m.marshal(je, writer); - - // entry end + // key end + writer.writeEndElement(); + + // value + JAXBElement je = new JAXBElement<>(QName.valueOf(TAG_VALUE), type, + e.getValue()); + + m.marshal(je, writer); + + // entry end + writer.writeEndElement(); + } + + // configuration end writer.writeEndElement(); + writer.writeEndDocument(); } - - // configuration end - writer.writeEndElement(); - writer.writeEndDocument(); - } - }, - file.toPath() - ); + }, + file.toPath() + ); + }); } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final File file; - - /** Field description */ - private JAXBContext context; - - /** Field description */ - private Map entries = Maps.newHashMap(); - - /** 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 index 83294038d6..7feae4e0df 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.store; //~--- non-JDK imports -------------------------------------------------------- @@ -55,6 +55,8 @@ public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory return new JAXBConfigurationEntryStore<>( getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), storeParameters.getRepositoryId()), keyGenerator, - storeParameters.getType()); + storeParameters.getType(), + TypedStoreContext.of(storeParameters) + ); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java index 5a641dcd29..b5b4ff4034 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java @@ -21,10 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.store; //~--- non-JDK imports -------------------------------------------------------- + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,9 +39,8 @@ import javax.xml.bind.Marshaller; /** * JAXB implementation of {@link ConfigurationStore}. * - * @author Sebastian Sdorra - * * @param + * @author Sebastian Sdorra */ public class JAXBConfigurationStore extends AbstractStore { @@ -49,28 +49,19 @@ public class JAXBConfigurationStore extends AbstractStore { */ private static final Logger LOG = LoggerFactory.getLogger(JAXBConfigurationStore.class); - private Class type; - - private File configFile; - - private JAXBContext context; - - public JAXBConfigurationStore(Class type, File configFile) { - this.type = type; + private final TypedStoreContext context; + private final Class type; + private final File configFile; - try { - context = JAXBContext.newInstance(type); - this.configFile = configFile; - } - catch (JAXBException ex) { - throw new StoreException("failed to create jaxb context", ex); - } + public JAXBConfigurationStore(TypedStoreContext context, Class type, File configFile) { + this.context = context; + this.type = type; + this.configFile = configFile; } /** * Returns type of stored object. * - * * @return type */ public Class getType() { @@ -82,38 +73,18 @@ public class JAXBConfigurationStore extends AbstractStore { protected T readObject() { LOG.debug("load {} from store {}", type, configFile); - T result = null; - if (configFile.exists()) { - try { - result = (T) context.createUnmarshaller().unmarshal(configFile); - } - catch (JAXBException ex) { - throw new StoreException("failed to unmarshall object", ex); - } + return context.unmarshall(configFile); } - - return result; + return null; } @Override protected void writeObject(T object) { - if (LOG.isDebugEnabled()) { - LOG.debug("store {} to {}", object.getClass().getName(), - configFile.getPath()); - } - - try { - Marshaller marshaller = context.createMarshaller(); - - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); - CopyOnWrite.withTemporaryFile( - temp -> marshaller.marshal(object, temp.toFile()), - configFile.toPath() - ); - } - catch (JAXBException ex) { - throw new StoreException("failed to marshall object", ex); - } + LOG.debug("store {} to {}", object.getClass().getName(), configFile.getPath()); + CopyOnWrite.withTemporaryFile( + temp -> context.marshal(object, temp.toFile()), + configFile.toPath() + ); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java index 65a81c8b7b..29cdf471a3 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.store; import com.google.inject.Inject; @@ -49,10 +49,13 @@ public class JAXBConfigurationStoreFactory extends FileBasedStoreFactory impleme @Override public JAXBConfigurationStore getStore(TypedStoreParameters storeParameters) { + TypedStoreContext context = TypedStoreContext.of(storeParameters); return new JAXBConfigurationStore<>( + context, storeParameters.getType(), getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), - storeParameters.getRepositoryId())); + storeParameters.getRepositoryId()) + ); } } 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 a4181c5c8d..4ab83ca961 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 @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.store; //~--- non-JDK imports -------------------------------------------------------- @@ -54,42 +54,22 @@ public class JAXBDataStore extends FileBasedStore implements DataStore /** * the logger for JAXBDataStore */ - private static final Logger LOG - = LoggerFactory.getLogger(JAXBDataStore.class); - - private final JAXBContext context; + private static final Logger LOG = LoggerFactory.getLogger(JAXBDataStore.class); private final KeyGenerator keyGenerator; + private final TypedStoreContext context; - JAXBDataStore(KeyGenerator keyGenerator, Class type, File directory) { + JAXBDataStore(KeyGenerator keyGenerator, TypedStoreContext context, File directory) { super(directory, StoreConstants.FILE_EXTENSION); this.keyGenerator = keyGenerator; - - try { - context = JAXBContext.newInstance(type); - this.directory = directory; - } - catch (JAXBException ex) { - throw new StoreException("failed to create jaxb context", ex); - } + this.directory = directory; + this.context = context; } @Override public void put(String id, T item) { LOG.debug("put item {} to store", id); - - File file = getFile(id); - - try { - Marshaller marshaller = context.createMarshaller(); - - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); - marshaller.marshal(item, file); - } - catch (JAXBException ex) { - throw new StoreException("could not write object with id ".concat(id), - ex); - } + context.marshal(item, getFile(id)); } @Override @@ -115,22 +95,11 @@ public class JAXBDataStore extends FileBasedStore implements DataStore } @Override - @SuppressWarnings("unchecked") protected T read(File file) { - T item = null; - if (file.exists()) { LOG.trace("try to read {}", file); - - try { - item = (T) context.createUnmarshaller().unmarshal(file); - } - catch (JAXBException ex) { - throw new StoreException( - "could not read object ".concat(file.getPath()), ex); - } + return context.unmarshall(file); } - - return item; + return null; } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java index d525b5c7a6..80e2d260d2 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.store; //~--- non-JDK imports -------------------------------------------------------- @@ -56,6 +56,6 @@ public class JAXBDataStoreFactory extends FileBasedStoreFactory public DataStore getStore(TypedStoreParameters storeParameters) { File storeLocation = getStoreLocation(storeParameters); IOUtil.mkdirs(storeLocation); - return new JAXBDataStore<>(keyGenerator, storeParameters.getType(), storeLocation); + return new JAXBDataStore<>(keyGenerator, TypedStoreContext.of(storeParameters), storeLocation); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java new file mode 100644 index 0000000000..5963804931 --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java @@ -0,0 +1,112 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.store; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import java.io.File; + +final class TypedStoreContext { + + private final JAXBContext jaxbContext; + private final TypedStoreParameters parameters; + + private TypedStoreContext(JAXBContext jaxbContext, TypedStoreParameters parameters) { + this.jaxbContext = jaxbContext; + this.parameters = parameters; + } + + static TypedStoreContext of(TypedStoreParameters parameters) { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(parameters.getType()); + return new TypedStoreContext<>(jaxbContext, parameters); + } catch (JAXBException e) { + throw new StoreException("failed to create context for store"); + } + } + + T unmarshall(File file) { + Unmarshaller unmarshaller = createUnmarshaller(); + try { + return parameters.getType().cast(unmarshaller.unmarshal(file)); + } catch (JAXBException e) { + throw new StoreException("failed to unmarshall " + file); + } + } + + void marshal(Object object, File file) { + Marshaller marshaller = createMarshaller(); + try { + marshaller.marshal(object, file); + } catch (JAXBException e) { + throw new StoreException("failed to marshall " + object + " to " + file); + } + } + + void withMarshaller(ThrowingConsumer consumer) { + Marshaller marshaller = createMarshaller(); + try { + consumer.consume(marshaller); + } catch (Exception e) { + throw new StoreException("failure during work with marshaller"); + } + } + + void withUnmarshaller(ThrowingConsumer consumer) { + Unmarshaller unmarshaller = createUnmarshaller(); + try { + consumer.consume(unmarshaller); + } catch (Exception e) { + throw new StoreException("failure during work with unmarshaller", e); + } + } + + private Marshaller createMarshaller() { + try { + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + return marshaller; + } catch (JAXBException e) { + throw new StoreException("could not create marshaller", e); + } + } + + private Unmarshaller createUnmarshaller() { + try { + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return unmarshaller; + } catch (JAXBException e) { + throw new StoreException("could not create unmarshaller", e); + } + } + + @FunctionalInterface + interface ThrowingConsumer { + void consume(T item) throws Exception; + } + +} diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java new file mode 100644 index 0000000000..a790bd51d9 --- /dev/null +++ b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java @@ -0,0 +1,101 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.store; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.TempDirectory; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +import java.io.File; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith({MockitoExtension.class, TempDirectory.class}) +class TypedStoreContextTest { + + @Test + void shouldMarshallAndUnmarshall(@TempDirectory.TempDir Path tempDir) { + TypedStoreContext context = context(Sample.class); + + File file = tempDir.resolve("test.xml").toFile(); + context.marshal(new Sample("awesome"), file); + Sample sample = context.unmarshall(file); + + assertThat(sample.value).isEqualTo("awesome"); + } + + @Test + void shouldWorkWithMarshallerAndUnmarshaller(@TempDirectory.TempDir Path tempDir) { + TypedStoreContext context = context(Sample.class); + + File file = tempDir.resolve("test.xml").toFile(); + + context.withMarshaller(marshaller -> { + marshaller.marshal(new Sample("wow"), file); + }); + + AtomicReference ref = new AtomicReference<>(); + + context.withUnmarshaller(unmarshaller -> { + Sample sample = (Sample) unmarshaller.unmarshal(file); + ref.set(sample); + }); + + assertThat(ref.get().value).isEqualTo("wow"); + } + + private TypedStoreContext context(Class type) { + return TypedStoreContext.of(params(type)); + } + + private TypedStoreParameters params(Class type) { + TypedStoreParameters params = mock(TypedStoreParameters.class); + when(params.getType()).thenReturn(type); + return params; + } + + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class Sample { + private String value; + + public Sample() { + } + + public Sample(String value) { + this.value = value; + } + } + +}