r35 - in trunk: tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities tutti-persistence/src/main/resources tutti-persistence/src/main/resources/META-INF tutti-persistence/src/main/resources/META-INF/services tutti-persistence-dev/src/main/java/fr/ifremer/tutti/persistence tutti-persistence-dev/src/test/java/fr/ifremer/tutti/persistence
Author: tchemit Date: 2012-12-08 11:50:28 +0100 (Sat, 08 Dec 2012) New Revision: 35 Url: http://forge.codelutin.com/projects/tutti/repository/revisions/35 Log: Improve persistence layer Added: trunk/tutti-persistence/src/main/resources/META-INF/ trunk/tutti-persistence/src/main/resources/META-INF/services/ trunk/tutti-persistence/src/main/resources/META-INF/services/fr.ifremer.tutti.persistence.entities.AbstractTuttiEntity Modified: trunk/tutti-persistence-dev/src/main/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImpl.java trunk/tutti-persistence-dev/src/test/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImplTest.java trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/AbstractTuttiEntity.java trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/TuttiEntities.java Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/AbstractTuttiEntity.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/AbstractTuttiEntity.java 2012-12-07 14:48:55 UTC (rev 34) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/AbstractTuttiEntity.java 2012-12-08 10:50:28 UTC (rev 35) @@ -36,6 +36,8 @@ private static final long serialVersionUID = 1L; + public static final String PROPERTY_ID = "id"; + protected String id; public String getId() { Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/TuttiEntities.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/TuttiEntities.java 2012-12-07 14:48:55 UTC (rev 34) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/entities/TuttiEntities.java 2012-12-08 10:50:28 UTC (rev 35) @@ -32,6 +32,7 @@ import com.google.common.collect.Maps; import fr.ifremer.tutti.persistence.entities.data.TraitAware; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -118,11 +119,33 @@ } public static <B extends AbstractTuttiEntity> B newEntity(B entity) { + return newEntity((Class<B>) entity.getClass()); + } + + public static <B extends AbstractTuttiEntity> B newEntity(Class<B> type) { try { - Class<B> aClass = (Class<B>) entity.getClass(); - return aClass.newInstance(); + return type.newInstance(); } catch (Exception e) { throw Throwables.propagate(e); } } + + public static <K, V> void fillEntries(Map<K, V> map, + Collection<K> keys, + Function<K, V> function) { + + for (K key : keys) { + V value = function.apply(key); + map.put(key, value); + } + } + + public static <K,V> Function<K,V> newConstantFunction(final V value) { + return new Function<K, V>() { + @Override + public V apply(K input) { + return value; + } + }; + } } Added: trunk/tutti-persistence/src/main/resources/META-INF/services/fr.ifremer.tutti.persistence.entities.AbstractTuttiEntity =================================================================== --- trunk/tutti-persistence/src/main/resources/META-INF/services/fr.ifremer.tutti.persistence.entities.AbstractTuttiEntity (rev 0) +++ trunk/tutti-persistence/src/main/resources/META-INF/services/fr.ifremer.tutti.persistence.entities.AbstractTuttiEntity 2012-12-08 10:50:28 UTC (rev 35) @@ -0,0 +1,20 @@ +fr.ifremer.tutti.persistence.entities.data.AccidentelBatch +fr.ifremer.tutti.persistence.entities.data.BenthosBatch +fr.ifremer.tutti.persistence.entities.data.Campaign +fr.ifremer.tutti.persistence.entities.data.MacroDechetBatch +fr.ifremer.tutti.persistence.entities.data.PlanctonBatch +fr.ifremer.tutti.persistence.entities.data.SpeciesBatch +fr.ifremer.tutti.persistence.entities.data.SpeciesBatchFrequency +fr.ifremer.tutti.persistence.entities.data.Strata +fr.ifremer.tutti.persistence.entities.data.Survey +fr.ifremer.tutti.persistence.entities.data.Trait +fr.ifremer.tutti.persistence.entities.referential.BeaufortScale +fr.ifremer.tutti.persistence.entities.referential.Country +fr.ifremer.tutti.persistence.entities.referential.Gear +fr.ifremer.tutti.persistence.entities.referential.SeaState +fr.ifremer.tutti.persistence.entities.referential.Sex +fr.ifremer.tutti.persistence.entities.referential.Species +fr.ifremer.tutti.persistence.entities.referential.TuttiUser +fr.ifremer.tutti.persistence.entities.referential.Vessel +fr.ifremer.tutti.persistence.entities.referential.WeightCategory +fr.ifremer.tutti.persistence.entities.referential.Zone \ No newline at end of file Property changes on: trunk/tutti-persistence/src/main/resources/META-INF/services/fr.ifremer.tutti.persistence.entities.AbstractTuttiEntity ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: svn:eol-style + native Modified: trunk/tutti-persistence-dev/src/main/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImpl.java =================================================================== --- trunk/tutti-persistence-dev/src/main/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImpl.java 2012-12-07 14:48:55 UTC (rev 34) +++ trunk/tutti-persistence-dev/src/main/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImpl.java 2012-12-08 10:50:28 UTC (rev 35) @@ -30,6 +30,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import fr.ifremer.tutti.persistence.config.TuttiPersistenceDevConfig; import fr.ifremer.tutti.persistence.entities.AbstractTuttiEntity; import fr.ifremer.tutti.persistence.entities.TuttiEntities; @@ -59,6 +60,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuiton.util.ApplicationConfig; +import org.nuiton.util.beans.Binder; import org.nuiton.util.beans.BinderFactory; import java.io.File; @@ -69,6 +71,7 @@ import java.io.ObjectOutputStream; import java.util.List; import java.util.Map; +import java.util.ServiceLoader; import java.util.UUID; /** @@ -83,32 +86,46 @@ private static final Log log = LogFactory.getLog(TuttiPersistenceDevImpl.class); - protected final ArrayListMultimap<Class<? extends AbstractTuttiEntity>, AbstractTuttiEntity> cache = ArrayListMultimap.create(); + /** + * All entity types. + * + * @since 0.1 + */ + public static final List<Class<? extends AbstractTuttiEntity>> TYPES; - public static final List<Class<? extends AbstractTuttiEntity>> TYPES = Lists.newArrayList( - BeaufortScale.class, - Campaign.class, - Country.class, - Gear.class, - SeaState.class, - Sex.class, - Species.class, - Survey.class, - Trait.class, - BenthosBatch.class, - SpeciesBatch.class, - SpeciesBatchFrequency.class, - PlanctonBatch.class, - MacroDechetBatch.class, - AccidentelBatch.class, - TuttiUser.class, - Vessel.class, - WeightCategory.class, - Zone.class - ); + static { + TYPES = Lists.newArrayList(); + for (AbstractTuttiEntity e : ServiceLoader.load(AbstractTuttiEntity.class)) { + TYPES.add(e.getClass()); + } + + if (log.isInfoEnabled()) { + log.info("Found " + TYPES.size() + " entity types."); + } + } + + /** + * Persistence config. + * + * @since 0.1 + */ protected TuttiPersistenceDevConfig config; + /** + * Cache of data. + * + * @since 0.1 + */ + protected final ArrayListMultimap<Class<? extends AbstractTuttiEntity>, AbstractTuttiEntity> cache = ArrayListMultimap.create(); + + /** + * Cache of binder. + * + * @since 0.2 + */ + protected final Map<Class<? extends AbstractTuttiEntity>, Binder<? extends AbstractTuttiEntity, ? extends AbstractTuttiEntity>> binderCache = Maps.newHashMap(); + //------------------------------------------------------------------------// //-- Technical methods --// //------------------------------------------------------------------------// @@ -118,7 +135,6 @@ return "Persistence Dev implementation"; } - @Override public void open(ApplicationConfig config) throws IOException { @@ -142,6 +158,8 @@ @Override public void close() throws IOException { + binderCache.clear(); + for (Class<? extends AbstractTuttiEntity> entityType : cache.keySet()) { persistToFile(entityType); } @@ -219,7 +237,6 @@ return result; } - //------------------------------------------------------------------------// //-- Survey methods --// //------------------------------------------------------------------------// @@ -248,7 +265,6 @@ return result; } - //------------------------------------------------------------------------// //-- Campaign methods --// //------------------------------------------------------------------------// @@ -359,37 +375,14 @@ public List<SpeciesBatchFrequency> saveSpeciesBatchFrequency(String speciesBatchId, List<SpeciesBatchFrequency> frequencies) { - // get back existing data + // get existing data List<SpeciesBatchFrequency> existingData = getAllSpeciesBatchFrequency(speciesBatchId); - // index them by id - Map<String, SpeciesBatchFrequency> existingById = - TuttiEntities.splitById(existingData); - - List<SpeciesBatchFrequency> result = Lists.newArrayList(); - - for (SpeciesBatchFrequency toSave : frequencies) { - - SpeciesBatchFrequency saved; - - if (TuttiEntities.isNew(toSave)) { - - // must create it - saved = new SpeciesBatchFrequency(); - } else { - - // get existing data - saved = existingById.get(toSave.getId()); - } - - // persist it - persist(SpeciesBatchFrequency.class, toSave, saved); - - // store it back in result - result.add(saved); - } - + List<SpeciesBatchFrequency> result = + persistList(SpeciesBatchFrequency.class, + existingData, + frequencies); return result; } @@ -431,7 +424,6 @@ List<PlanctonBatch> result = getAllTraitFilterBatches( PlanctonBatch.class, traitId); return result; - } @Override @@ -514,18 +506,87 @@ //-- Internal methods --// //------------------------------------------------------------------------// + protected <B extends AbstractTuttiEntity> Binder<B, B> getBinder(Class<B> type) { + Binder<B, B> result = (Binder<B, B>) binderCache.get(type); + if (result == null) { + result = BinderFactory.newBinder(type); + binderCache.put(type, result); + } + return result; + } + protected <B extends AbstractTuttiEntity> void persist(Class<B> beanType, - B source, B target) { + B source, + B target, + boolean synchFile) { - BinderFactory.newBinder(beanType).copyExcluding( - source, target, "id"); + getBinder(beanType).copyExcluding(source, target, AbstractTuttiEntity.PROPERTY_ID); if (source.getId() == null) { target.setId(UUID.randomUUID().toString()); } if (!cache.containsValue(target)) { cache.put(beanType, target); } + if (synchFile) { + persistToFile(beanType); + } + } + + protected <B extends AbstractTuttiEntity> List<B> persistList(Class<B> beanType, + List<B> existingData, + List<B> newData) { + + Map<B, B> toPersist = Maps.newHashMap(); + + // mark all old entry to be dettach + TuttiEntities.fillEntries(toPersist, + existingData, + TuttiEntities.<B, B>newConstantFunction(null)); + + // index them by id + Map<String, B> existingById = TuttiEntities.splitById(existingData); + + for (B toSave : newData) { + + B saved; + + if (TuttiEntities.isNew(toSave)) { + + // must create it + saved = TuttiEntities.newEntity(beanType); + } else { + + // get existing data + saved = existingById.get(toSave.getId()); + } + + // store it to be persist in one time + toPersist.put(toSave, saved); + } + + // persist (entry with a value) and dettach (entry with no value) + + List<B> result = Lists.newArrayList(); + + for (Map.Entry<B, B> entry : toPersist.entrySet()) { + B source = entry.getKey(); + B target = entry.getValue(); + if (target == null) { + + // dettach entity + cache.remove(beanType, source); + } else { + + // persist entity + persist(beanType, source, target, false); + + // store it back in result + result.add(target); + } + } persistToFile(beanType); + + return result; } protected <B extends AbstractTuttiEntity> List<B> loadEntities(Class<B> entityType, @@ -583,7 +644,6 @@ result = (List<B>) oos.readObject(); oos.close(); - return result; } catch (ClassNotFoundException e) { throw new RuntimeException("Could not find a class ", e); } finally { @@ -651,7 +711,7 @@ protected <B extends AbstractTuttiEntity> B create(Class<B> type, B bean) { Preconditions.checkNotNull(bean, "Can't persist a null bean"); B result = TuttiEntities.newEntity(bean); - persist(type, bean, result); + persist(type, bean, result, true); return result; } @@ -660,7 +720,7 @@ String id = bean.getId(); Preconditions.checkNotNull(id, "Can't save a bean with null id"); B result = getBean(type, id); - persist(type, bean, result); + persist(type, bean, result, true); return result; } Modified: trunk/tutti-persistence-dev/src/test/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImplTest.java =================================================================== --- trunk/tutti-persistence-dev/src/test/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImplTest.java 2012-12-07 14:48:55 UTC (rev 34) +++ trunk/tutti-persistence-dev/src/test/java/fr/ifremer/tutti/persistence/TuttiPersistenceDevImplTest.java 2012-12-08 10:50:28 UTC (rev 35) @@ -24,11 +24,15 @@ * #L% */ +import com.google.common.collect.Lists; import fr.ifremer.tutti.persistence.config.TuttiPersistenceDevConfig; +import fr.ifremer.tutti.persistence.entities.data.SpeciesBatch; +import fr.ifremer.tutti.persistence.entities.data.SpeciesBatchFrequency; import fr.ifremer.tutti.persistence.entities.data.Survey; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -37,6 +41,8 @@ import org.nuiton.util.ApplicationConfig; import java.io.File; +import java.io.IOException; +import java.util.List; /** * @author tchemit <chemit@codelutin.com> @@ -74,6 +80,13 @@ persistence = new TuttiPersistenceDevImpl(); } + @After + public void tearDown() throws IOException { + if (persistence != null) { + persistence.close(); + } + } + @Test public void open() throws Exception { @@ -101,7 +114,8 @@ persistence.close(); Assert.assertTrue(storageDirectory.exists()); - Assert.assertEquals(fixtures.seaState().size(), persistence.getAllSeaState().size()); + Assert.assertEquals(fixtures.seaState().size(), + persistence.getAllSeaState().size()); } @Test @@ -131,6 +145,67 @@ Assert.assertEquals(size + 1, persistence.getAllSurvey().size()); } + @Test + public void persistList() throws Exception { + + persistence.open(config.getConfig()); + + SpeciesBatch batch = persistence.createSpeciesBatch(new SpeciesBatch()); + String batchId = batch.getId(); + + SpeciesBatchFrequency f; + + f = new SpeciesBatchFrequency(); + f.setBatch(batch); + f.setLengthStep(10f); + f.setNumber(10); + + List<SpeciesBatchFrequency> frequencyList = Lists.newArrayList(); + frequencyList.add(f); + + Assert.assertEquals(0, persistence.getAllSpeciesBatchFrequency(batchId).size()); + + persistence.saveSpeciesBatchFrequency(batchId, frequencyList); + + frequencyList = persistence.getAllSpeciesBatchFrequency(batchId); + Assert.assertEquals(1, frequencyList.size()); + + f = frequencyList.get(0); + Assert.assertEquals(10, f.getNumber(), 0); + Assert.assertEquals(10f, f.getLengthStep(), 0); + Assert.assertNull(f.getWeight()); + + f.setLengthStep(12f); + f.setNumber(20); + f.setWeight(10f); + + f = new SpeciesBatchFrequency(); + f.setBatch(batch); + f.setLengthStep(30f); + f.setNumber(3); + f.setWeight(3f); + + frequencyList.add(f); + + persistence.saveSpeciesBatchFrequency(batchId, frequencyList); + frequencyList = persistence.getAllSpeciesBatchFrequency(batchId); + Assert.assertEquals(2, frequencyList.size()); + + f = frequencyList.get(0); + Assert.assertEquals(12f, f.getLengthStep(), 0); + Assert.assertEquals(20, f.getNumber(), 0); + Assert.assertEquals(10f, f.getWeight(), 0); + + f = frequencyList.get(1); + Assert.assertEquals(30f, f.getLengthStep(), 0); + Assert.assertEquals(3, f.getNumber(), 0); + Assert.assertEquals(3f, f.getWeight(), 0); + + frequencyList.clear(); + persistence.saveSpeciesBatchFrequency(batchId, frequencyList); + Assert.assertEquals(0, persistence.getAllSpeciesBatchFrequency(batchId).size()); + } + public static File getTestSpecificDirectory(Class<?> testClassName, String methodName, String classifier) {
participants (1)
-
tchemit@users.forge.codelutin.com