All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.tsl2.nano.replication.EntityReplication Maven / Gradle / Ivy

There is a newer version: 2.5.1
Show newest version
package de.tsl2.nano.replication;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import de.tsl2.nano.replication.serializer.SerializeBytes;
import de.tsl2.nano.replication.serializer.SerializeJAXB;
import de.tsl2.nano.replication.serializer.SerializeXML;
import de.tsl2.nano.replication.serializer.Serializer;
import de.tsl2.nano.replication.util.H2Util;
import de.tsl2.nano.replication.util.ULog;
import de.tsl2.nano.replication.util.Util;


/** 
 * Replicates entities through hibernatesession.replicate(). EntityManagers will be load through their persistence unit names. 
 * To use it, 
 *   - create a META-INF/persistence.xml in your classpath
 *   - describe the two persistence-units (source and destination)
 *   - call: new EntityReplication(mySourceUnit, myDestUnit).replicate(new HibReplication(myDestUnit)::strategyHibReplicate, myBeans)
 * or through serliazition:
 *   - EntityReplication.setPersistableID(e -> (IPersistable)e).getId)
 *   - new EntityReplication().replicate(myBeans)
 *   - ...
 *   - new EntityReplication().load(myID, myBeanType.class)
 * 
* @author Tom, Thomas Schneider * @version $Revision$ */ @SuppressWarnings({"unchecked", "rawtypes"}) public class EntityReplication { public static final String CONFIG_DIR = "REPL-INF/"; private static final String PERS_JNDI = "JNDI"; static final String DEFAULT_PERSISTENCE_XML = "META-INF/persistence.xml"; static final List serializer = new LinkedList<>(); static { serializer.add(new SerializeJAXB()); serializer.add(new SerializeXML()); serializer.add(new SerializeBytes()); } private EntityManager src; private EntityManager dest; AtomicReference tmp = new AtomicReference(); private static String persistenceXmlPath = DEFAULT_PERSISTENCE_XML; private static Function persistableID; private JndiLookup.FindByIdAccess jndiEJBSession; public EntityReplication(String srcPersistenceUnit, String destPersistenceUnit) { if (srcPersistenceUnit.equals(PERS_JNDI)) jndiEJBSession = JndiLookup.createSessionBeanFromJndi(); if (!isKeywordOrNull(srcPersistenceUnit)) { src = createEntityManager(srcPersistenceUnit, true); } if (!isKeywordOrNull(destPersistenceUnit)) { dest = createEntityManager(destPersistenceUnit, false); } assert src != null || dest != null : "at least one persistence-unit-name must be given!"; } private ClassLoader definePersitenceXmlPath() { if (System.getProperty("persistencexml.path") != null) persistenceXmlPath = System.getProperty("persistencexml.path"); if (persistenceXmlPath != null) { ClassLoader orgin = Util.linkResourcePath(DEFAULT_PERSISTENCE_XML, persistenceXmlPath); return orgin; } return null; } protected EntityManager createEntityManager(String punit, boolean threadScope) { long start = System.currentTimeMillis(); if (threadScope) { ULog.log("creating EntityManager for '" + punit + "' in new thread scope...", false); Thread thread = new Thread(new Runnable() { @Override public void run() { ClassLoader cl = definePersitenceXmlPath(); Properties pers = new Properties(); // pers.setProperty("hibernate.current_session_context_class", "thread"); // pers.setProperty("hibernate.connection.pool_size", "10"); EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(punit); tmp.set(entityManagerFactory.createEntityManager(pers)); if (cl != null) Thread.currentThread().setContextClassLoader(cl); } }); thread.start(); try { thread.join(); } catch (InterruptedException e) { throw new IllegalStateException(e); } } else { ULog.log("creating EntityManager for '" + punit + "...", false); EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(punit); tmp.set(entityManagerFactory.createEntityManager()); } ULog.log((System.currentTimeMillis() - start) + " ms"); return tmp.get(); } public EntityReplication(EntityManager src, EntityManager dest) { this.src = src; this.dest = dest; } public EntityReplication() { } protected boolean isKeywordOrNull(String name) { return name == null || getSerializer(name) != null || name.equals(PERS_JNDI); } protected static Serializer getSerializer(String key) { return serializer.stream().filter(s -> s.getKey().equals(key)).findFirst().orElse(null); } public void replicateFromIDs(Class entityClass, Serializer ser, Object...ids) { T[] entities = fromIDs(entityClass, ids); //TODO: involve serializer instead of static using JAXB replicate((Consumer)null, (Consumer)EntityReplication::strategySerializeJAXB, entities); } protected T[] fromIDs(Class entityClass, Object... ids) { long start = System.currentTimeMillis(); ULog.log("loading entities for " + ids.length + " ids"); T[] entities = Arrays.stream(ids). map(id -> src.find(entityClass, id, readOnlyHints())).filter(e -> e != null). toArray(s -> (T[])java.lang.reflect.Array.newInstance(entityClass, s)); if (ids.length > entities.length) ULog.log("WARNING: not all ids (" + ids.length + ") were found: " + entities.length); ULog.log("loading entities finshed " + (System.currentTimeMillis() - start) + " ms"); return entities; } protected T[] fromIDs(JndiLookup.FindByIdAccess ejbSession, Class entityClass, Object... ids) { long start = System.currentTimeMillis(); ULog.log("loading entities for " + ids.length + " ids"); T[] entities = Arrays.stream(ids). map(id -> ejbSession.call(entityClass, (Serializable)id)).filter(o -> o != null). toArray(s -> (T[])java.lang.reflect.Array.newInstance(entityClass, s)); if (ids.length > entities.length) ULog.log("WARNING: not all ids (" + ids.length + ") were found: " + entities.length); ULog.log("loading entities finshed " + (System.currentTimeMillis() - start) + " ms"); return entities; } public void replicate(T...entities) { replicate((Consumer)EntityReplication::strategySerializeJAXB, entities); } public void replicate(Consumer strategy, T...entities) { replicate((Consumer)null, strategy, entities); } public void replicate(Consumer transformer, Consumer strategy, T...entities) { long start = System.currentTimeMillis(); Integer transactionBlock = Util.getProperty("transaction.block", Integer.class); log("replicating with transformer " + transformer + ", strategy " + strategy + " on " + entities.length + " entities"); if (transactionBlock == null) beginTransation(); int i[] = {0}, f[] = {0}; Arrays.stream(entities).forEach(e -> { try { if (transformer != null) transformer.accept(e); if (strategy != null) { boolean doTransaction = transactionBlock != null && (i[0] %transactionBlock) == 0; if (doTransaction) beginTransation(); ULog.log("\b" + i[0], false); strategy.accept(e); if (doTransaction) commitTransaction(); i[0]++; } else { ULog.log("WARN: no strategy defined --> nothing to do"); } } catch (Exception ex) { f[0]++; if (dest != null && dest.getTransaction().isActive()) dest.getTransaction().rollback(); Util.handleException(ex); } }); if (transactionBlock == null) { commitTransaction(); ULog.log(""); } log("replication finished " + (f[0] == 0 ? "successfull" : "with " + f[0] + " errors") + "! " + "(Entities: " + i[0] + ", "+ (System.currentTimeMillis() - start) + " ms)"); } private void beginTransation() { if (dest != null) { ULog.log("--> begin transaction...", false); dest.getTransaction().begin(); } } private void commitTransaction() { if (dest != null) { ULog.log("--> commit transaction...", false); H2Util.disableReferentialIntegrity(dest); dest.flush(); dest.getTransaction().commit(); } } public static void strategySerializeJAXB(Object entity) { strategySerialize(entity, getSerializer(SerializeJAXB.KEY)); } public static void strategySerializeBytes(Object entity) { strategySerialize(entity, getSerializer(SerializeBytes.KEY)); } public static void strategySerializeXML(Object entity) { strategySerialize(entity, getSerializer(SerializeXML.KEY)); } public static void strategySerialize(Object entity, Serializer serializer) { File file = getFile(entity.getClass(), getID(entity), serializer.getExtension()); log("serializing (" + serializer.getKey() + ") " + entity.getClass() + " to " + file); try { Files.write(Paths.get(file.getPath()), serializer.serialize(entity).toByteArray()); } catch (IOException e) { Util.handleException(e); } } public void strategyPersist(Object entity) { log("persisting " + entity.getClass() + " to " + dest); dest.merge(entity); } public static void setPersistenceXmlPath(String persistenceXmlPath) { EntityReplication.persistenceXmlPath = persistenceXmlPath; } public static void setPersistableID(Function persistableID) { log("setting persistableID=" + persistableID); EntityReplication.persistableID = persistableID; } private static Object getID(Object entity) { Object id; if (persistableID != null) { id = persistableID.apply(entity); } else { String idMethod = Util.getProperty("peristableid.access", "getId", ""); try { id = entity.getClass().getMethod(idMethod, new Class[0]).invoke(entity); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalArgumentException("ERROR: Please define a peristableid either through calling setPersistableID() or system-property 'peristableid.access' (default: getId <- reflaction!)", e); } } log("handling " + entity.getClass() + ":" + id); return id; } public static List load(Class entityClass, Serializer serializer, Object...ids) { ArrayList entities = new ArrayList(ids.length); for (int i = 0; i < ids.length; i++) { entities.add(load(ids[i], entityClass, serializer)); } return entities; } public static T load(Object id, Class entityClass, Serializer serializer) { File file = getFile(entityClass, id, serializer.getExtension()); try { return serializer.deserialize(Files.newInputStream(Paths.get(file.getPath())), entityClass); } catch (IOException | ClassNotFoundException e) { Util.handleException(e); return null; } } private static File getFile(Class entityClass, Object id, String extension) { return new File(entityClass.getSimpleName() + "-" + id + "." + extension); } private Map readOnlyHints() { HashMap hints = new HashMap<>(); hints.put("org.hibernate.readOnly", true); log("setting entity-manager hints: " + hints); return hints; } public static void checkContent(String srcPersistenceUnit, String destPersistenceUnit, Class entityClass, Object...ids) throws IOException { EntityReplication repl = new EntityReplication(srcPersistenceUnit, destPersistenceUnit); SerializeBytes s = (SerializeBytes) getSerializer(SerializeBytes.KEY); Object srcObj, dstObj; for (int i = 0; i < ids.length; i++) { srcObj = repl.src.find(entityClass, ids[i]); dstObj = repl.dest.find(entityClass, ids[i]); assert srcObj != null : ids[i] + " not found in " + repl.src; assert dstObj != null : ids[i] + " not found in " + repl.dest; assert Arrays.equals(s.serialize(srcObj).toByteArray(), s.serialize(dstObj).toByteArray()) : "objects for " + ids[i] + " differ between both persistences!"; } } private static void log(String txt, Object...args) { ULog.log(EntityReplication.class.getSimpleName() + ": " + txt, true, args); } public static void main(String[] args) throws ClassNotFoundException { System.out.println("==============================================================================="); Util.printLogo("repl-logo.txt"); final int MINARGS = 4; Properties props = Util.loadPropertiesToSystem(CONFIG_DIR + EntityReplication.class.getSimpleName().toLowerCase() + ".properties"); args = Util.mergeArgsAndProps(args, MINARGS, props); if (args.length < MINARGS || args[0].matches("[-/]+[?h].*")) { System.out.println("usage : {|XML|JAXB|BYTES} {||XML|JAXB|BYTES} {} {, ...}" ); System.out.println(" exam : mypersistentunit1 XML my.pack.MyClass 1 2 3"); System.out.println(" exam : XML mypersistentunit2 my.pack.MyClass 1"); System.out.println(" exam : mypersistentunit1 mypersistentunit2 my.pack.MyClass 1 2 3"); System.out.println(" exam : -Dperistableid.access=getMyID mypersistentunit1 XML my.pack.MyClass 1 2 3"); System.out.println(" exam : -Dpersistencexml.path=REPL-INF/persistence.xml mypersistentunit1 xml my.pack.MyClass 1 2 3"); System.out.println(" exam : -Duse.hibernate.replication=true -Djndi.prefix=ejb:/myapp/ -Djndi.sessionbean=MySessionBean -Djndi.sessioninterface=MySessionInterface -Djndi.find.method=myFindByID JNDI mypersistentunit2 my.pack.MyClass 1 2 3"); System.out.println("you can provide properties through system call or through file 'REPL-INF/entityreplication.properties"); System.out.println("the properties may contain the main args instead: syntax: args0={||XML|JAXB|BYTES}, args1={||XML|JAXB|BYTES}, args2={}, args3={, ...}"); return; } String pers1 = args[0]; String pers2 = args[1]; String call; try { if ((call = Util.getProperty("on.start.call", null, null)) != null) Util.invoke(call); EntityReplication repl = new EntityReplication(pers1, pers2); Class cls = Thread.currentThread().getContextClassLoader().loadClass(args[2]); boolean useHibernateReplication = Util.getProperty("use.hibernate.replication", Boolean.class); assert pers2 != PERS_JNDI : "persistence-unit-2 must not be " + PERS_JNDI; Object[] ids = Arrays.copyOfRange(args, 3, args.length); if (ids.length == 1 && ids[0].getClass().equals(String.class)) ids = ((String)ids[0]).split("[,;| ]"); log("starting replication with:" + "\n\tpersistence-unit-1: " + pers1 + "\n\tpersistence-unit-2: " + pers2 + "\n\tentity-class : " + cls + "\n\tentity-ids : " + Arrays.toString(ids)); Serializer ser; if ((ser = getSerializer(pers1)) != null) { repl.replicate(repl::strategyPersist, load(cls, ser, ids).toArray()); } else { if ((ser = getSerializer(pers2)) != null) { repl.replicateFromIDs(cls, ser, ids); } else if (pers1.equals(PERS_JNDI)){ if (useHibernateReplication) repl.replicate((Consumer)null, (Consumer)new HibReplication<>(repl.dest)::strategyHibReplicate, repl.fromIDs(repl.jndiEJBSession, cls, ids)); else repl.replicate((Consumer)null, (Consumer)repl::strategyPersist, repl.fromIDs(repl.jndiEJBSession, cls, ids)); } else { repl.replicate((Consumer)null, (Consumer)repl::strategyPersist, repl.fromIDs(cls, ids)); } } } catch (Throwable e) { ULog.log("STOPPED WITH ERROR: " + Util.toString(e)); throw new RuntimeException(e); } finally { ULog.log("\nconsumed properties: " + Util.getConsumedProperties()); //TODO: store consumed properties System.out.println("==============================================================================="); if (Util.getProperty("wait.on.finish", Boolean.class)) { if (System.console() != null) System.console().readLine("Please press ENTER to shutdown Java VM: "); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy