| /** |
| * |
| * Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation |
| */ |
| package org.eclipse.osbp.utils.entitymock.dbfill.ui; |
| |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| import javax.persistence.EntityManager; |
| import javax.persistence.EntityManagerFactory; |
| import javax.validation.ConstraintViolation; |
| import javax.validation.ConstraintViolationException; |
| |
| import org.eclipse.osbp.dsl.dto.lib.impl.DtoServiceAccess; |
| import org.eclipse.osbp.dsl.dto.lib.services.IDTOService; |
| import org.eclipse.osbp.dsl.dto.lib.services.IDTOServiceWithMutablePersistence; |
| import org.eclipse.osbp.persistence.IPersistenceService; |
| import org.eclipse.osbp.preferences.ProductConfiguration; |
| import org.eclipse.osbp.ui.initialization.AbstractInitializationListener; |
| import org.eclipse.osbp.ui.initialization.AbstractInitializationProvider; |
| import org.eclipse.osbp.ui.initialization.IInitializationProvider; |
| import org.eclipse.osbp.ui.initialization.InitializationNotification; |
| import org.eclipse.osbp.utils.entitymock.IEntityMockDataDbFiller; |
| import org.eclipse.osbp.utils.entitymock.IEntityMockDataGenerator; |
| import org.eclipse.osbp.utils.entitymock.MockedEntityDto; |
| import org.eclipse.persistence.config.PessimisticLock; |
| import org.eclipse.persistence.config.QueryHints; |
| import org.osgi.service.component.ComponentContext; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.osgi.service.component.annotations.ReferencePolicy; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| @Component(service = IInitializationProvider.class, immediate = true) |
| public class EntityMockGeneratorConsumer extends AbstractInitializationProvider implements IEntityMockDataDbFiller { |
| |
| /** |
| * Map using the generator as index and a boolean as a value.<br> |
| * The index (generator) defines the position inside the map by |
| * <ul> |
| * <li>the run priority</li> |
| * <li>the simple class name for identical run priorities</li> |
| * </ul> |
| */ |
| public final class PriorityGeneratorMap extends TreeMap<IEntityMockDataGenerator, Boolean> implements Comparator<IEntityMockDataGenerator> { |
| |
| private static final long serialVersionUID = 1951758628543477475L; |
| |
| @Override |
| public int compare(IEntityMockDataGenerator arg0, IEntityMockDataGenerator arg1) { |
| return arg0.compareTo(arg1); |
| } |
| } |
| |
| private final static Logger log = LoggerFactory |
| .getLogger(EntityMockGeneratorConsumer.class); |
| |
| /** |
| * map of existing generators. The boolean value defines |
| * <ul> |
| * <li><code>false</code> if the generator must run</li> |
| * <li><code>true</code> if the generator has been done already</li> |
| * </ul> |
| */ |
| private final PriorityGeneratorMap generatorDoneStates = new PriorityGeneratorMap(); |
| private IPersistenceService persistenceService; |
| private String mockDataGeneratorFilter = null; |
| private boolean alreadyRun = false; |
| private int totalSteps = 0; |
| private int stepsDone = 0; |
| |
| private ComponentContext context; |
| |
| public void forceReRun() { |
| if (alreadyRun) { |
| log.info("force re-run, even though it has been running already"); |
| alreadyRun = false; |
| } |
| } |
| |
| @Override |
| public void setMockDataGeneratorFilter(String filter) { |
| if (filter == null) { |
| mockDataGeneratorFilter = ""; |
| } else { |
| mockDataGeneratorFilter = filter; |
| } |
| triggerAllKnownFills(); |
| } |
| |
| public EntityMockGeneratorConsumer() { |
| log.debug("EntityMockConsumer constructed"); |
| } |
| |
| @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) |
| public synchronized void bindMockDataGenerator( |
| final IEntityMockDataGenerator generator) { |
| generatorDoneStates.put(generator, false); |
| calculateInitializationTotalSteps(); |
| log.debug("IEntityMockDataGenerator '{}' bound", generator.getClass() |
| .getCanonicalName()); |
| if (context != null) { |
| try { |
| triggerSingleFill(generator); |
| } catch (Exception e) { |
| log.error(e.toString()); |
| } |
| } |
| } |
| |
| public synchronized void unbindMockDataGenerator( |
| final IEntityMockDataGenerator generator) { |
| generatorDoneStates.remove(generator); |
| calculateInitializationTotalSteps(); |
| log.debug("IEntityMockDataGenerator '{}' unbound", generator.getClass() |
| .getCanonicalName()); |
| } |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.STATIC) |
| public synchronized void bindPersistenceService(final IPersistenceService persistenceService) { |
| setMockDataGeneratorFilter(System |
| .getProperty("mockDataGeneratorFilter")); |
| this.persistenceService = persistenceService; |
| log.debug("PersistenceService '{}' bound", persistenceService.getClass().getCanonicalName()); |
| } |
| |
| @Override |
| public void addInitializationListener(AbstractInitializationListener listener) { |
| super.addInitializationListener(listener); |
| try { |
| triggerAllKnownFills(); |
| } catch (Exception e) { |
| log.error(e.toString()); |
| } |
| } |
| |
| public void activate(ComponentContext context) { |
| this.context = context; |
| } |
| |
| public synchronized void unbindPersistenceService( |
| final IPersistenceService persistenceService) { |
| this.persistenceService = null; |
| log.debug("PersistenceService '{}' unbound", persistenceService.getClass().getCanonicalName()); |
| } |
| |
| private static ArrayList<Method> findGetters(Class<?> c) { |
| ArrayList<Method> list = new ArrayList<Method>(); |
| Method[] methods = c.getDeclaredMethods(); |
| for (Method method : methods) { |
| if (isGetter(method)) { |
| list.add(method); |
| } |
| } |
| return list; |
| } |
| |
| private static boolean isGetter(Method method) { |
| if (Modifier.isPublic(method.getModifiers()) |
| && method.getParameterTypes().length == 0) { |
| if (method.getName().matches("^get[A-Z].*") |
| && !method.getReturnType().equals(void.class)) { |
| return true; |
| } |
| if (method.getName().matches("^is[A-Z].*") |
| && method.getReturnType().equals(boolean.class)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void triggerDbFill() { |
| if (persistenceService != null) { |
| if (alreadyRun) { |
| log.info("ignore, because it has been running already"); |
| } else { |
| alreadyRun = true; |
| log.info("MockDataGenerator Filter=" + mockDataGeneratorFilter); |
| triggerAllKnownFills(); |
| } |
| } |
| } |
| |
| protected void triggerAllKnownFills() { |
| if (persistenceService != null) { |
| Thread thread = new Thread() { |
| @Override |
| public synchronized void run() { |
| int countFillRuns = 0; |
| int totalDone = 0; |
| do { |
| log.info("STEP START..."); |
| countFillRuns = 0; |
| for (IEntityMockDataGenerator generator : generatorDoneStates.keySet()) { |
| boolean run = triggerSingleFill(generator); |
| log.info(generator.getClass().getCanonicalName()+" -> "+run); |
| if (run) { |
| countFillRuns += 1; |
| totalDone += 1; |
| } |
| } |
| log.info("STEP done with "+countFillRuns+" done"); |
| } while (countFillRuns > 0); |
| if (totalDone > 0) { |
| notifyInitializationDone(); |
| } |
| log.info("TRIGGER DONE..."); |
| } |
| }; |
| thread.start(); |
| } |
| } |
| |
| protected boolean triggerSingleFill(IEntityMockDataGenerator generator) { |
| boolean singleFillRun = false; |
| try { |
| singleFillRun = _triggerSingleFill(generator); |
| } |
| catch (Exception e) { |
| log.error("triggerSingleFill: " + generator.getClass().getCanonicalName(), e); |
| } |
| return singleFillRun; |
| } |
| |
| private boolean _triggerSingleFill(IEntityMockDataGenerator generator) { |
| boolean singleFillDone; |
| if ((mockDataGeneratorFilter != null) && (persistenceService != null) |
| && generatorDoneStates.containsKey(generator) |
| && !generatorDoneStates.get(generator)) { |
| if (mockDataGeneratorFilter.contains("*") |
| || mockDataGeneratorFilter.contains(generator.getClass() |
| .getCanonicalName())) { |
| |
| String lastDone = ProductConfiguration |
| .isMockDataGeneratorDone(generator); |
| |
| if (mockDataGeneratorFilter.contains("NEVER-*") |
| || mockDataGeneratorFilter.contains("NEVER-" |
| + generator.getClass().getCanonicalName())) { |
| lastDone = "NEVER " + lastDone; |
| } |
| |
| if (mockDataGeneratorFilter.contains("FORCE-*") |
| || mockDataGeneratorFilter.contains("FORCE-" |
| + generator.getClass().getCanonicalName())) { |
| if (lastDone != null && !lastDone.isEmpty()) { |
| log.debug("force generator: " |
| + generator.getClass().getCanonicalName() |
| + " even already done at " + lastDone |
| + " because of option FORCE"); |
| } |
| lastDone = null; |
| } |
| |
| if (lastDone != null && !lastDone.isEmpty()) { |
| log.debug("ignore generator: " |
| + generator.getClass().getCanonicalName() |
| + " because already done at " + lastDone); |
| generatorDoneStates.put(generator, true); |
| singleFillDone = false; |
| } else { |
| log.debug("running generator: " |
| + generator.getClass().getCanonicalName()); |
| log.debug("run data interchanges"); |
| generator.runDataInterchanges(persistenceService, this); |
| |
| log.debug("generate mock data"); |
| Map<MockedEntityDto, List<Object>> result = generator |
| .getMockData(this); |
| |
| // --- just preview of mock data --- |
| if (log.isTraceEnabled()) { |
| for (MockedEntityDto mockedEntityDto : result.keySet()) { |
| List<Object> resultEntities = result |
| .get(mockedEntityDto); |
| Class<?> resultClass = mockedEntityDto |
| .getEntityDtoClass(); |
| String persistenceUnit = mockedEntityDto |
| .getPersistenceUnit(); |
| log.trace("--> " + resultClass.getCanonicalName() |
| + " @ " + persistenceUnit + ": " |
| + resultEntities.size() + " entities"); |
| } |
| } |
| |
| // --- |
| for (MockedEntityDto mockedEntityDto : result.keySet()) { |
| |
| List<Object> resultEntities = result |
| .get(mockedEntityDto); |
| Class<?> resultClass = mockedEntityDto |
| .getEntityDtoClass(); |
| String persistenceUnit = mockedEntityDto |
| .getPersistenceUnit(); |
| log.trace("------ " + resultClass.getCanonicalName() |
| + " @ " + persistenceUnit + ": " |
| + resultEntities.size() + " entities"); |
| String[] tokens = resultClass.getName().replace("Dto", "").split("\\."); |
| String shortClassName = ""; |
| for (int i = 0; i < tokens.length-1; i++) { |
| shortClassName += tokens[i].charAt(0)+"."; |
| } |
| shortClassName += tokens[tokens.length-1]; |
| notifyInitializationStep("persist "+shortClassName, 1, 0.3); |
| IDTOService dtoService = null; |
| EntityManager em = null; |
| try { |
| persistenceService.registerPersistenceUnit(persistenceUnit, resultClass); |
| EntityManagerFactory emf = persistenceService.getEntityManagerFactory(persistenceUnit); |
| if (emf == null) { |
| log.error( |
| "maybe EMF is not set?!? - while updating instance of " |
| + resultClass.getCanonicalName() |
| + " in persistence " |
| + persistenceUnit); |
| } |
| em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| em.setProperty(QueryHints.PESSIMISTIC_LOCK, PessimisticLock.Lock); |
| // IDTOServiceWithMutablePersistence<Object> |
| // dtoService = |
| // (IDTOServiceWithMutablePersistence<Object>) |
| // DtoServiceAccess.getService(resultClass); |
| dtoService = (IDTOService) DtoServiceAccess |
| .getService(resultClass); |
| if (dtoService instanceof IDTOServiceWithMutablePersistence) { |
| ((IDTOServiceWithMutablePersistence) dtoService) |
| .setPersistenceId(persistenceUnit); |
| } |
| // TODO hint! as of 20.05.2015 in the model services |
| // file, EVERY dtoservice has to contain 'mutable |
| // persistenceID foodmart' |
| else { |
| log.error(resultClass.getCanonicalName() |
| + " in persistence " |
| + persistenceUnit |
| + ": as of 20.05.2015 in the model services file, EVERY dtoservice has to contain 'mutable persistenceID foodmart'"); |
| } |
| |
| if (dtoService == null) { |
| log.error("NO DTO SERVICE FOUND for " |
| + resultClass.getCanonicalName() |
| + " in persistence " + persistenceUnit); |
| } else { |
| int total = resultEntities.size(); |
| int count = 0; |
| long lastStep = System.currentTimeMillis(); |
| notifyInitializationStep("persist "+shortClassName, 0, 0.4, count, total); |
| for (Object dtoObject : resultEntities) { |
| try { |
| dtoService.update(resultClass |
| .cast(dtoObject)); |
| } |
| catch (ConstraintViolationException cve) { |
| log.error(shortClassName+" #"+(count+1)+"/"+total+": "+cve.getLocalizedMessage()); |
| for (ConstraintViolation violation : cve.getConstraintViolations()) { |
| Object value = violation.getInvalidValue(); |
| if (value == null) { |
| value = "<null>"; |
| } |
| log.error("- property:" |
| +violation.getLeafBean().toString()+"."+violation.getPropertyPath().toString() |
| +" value:'"+value.toString() |
| +" violation:"+violation.getMessage()); |
| } |
| } |
| count++; |
| long thisStep = System.currentTimeMillis(); |
| if ((count % getInitializationSubStepNotifySize() == 0) || (thisStep-lastStep > 2500)) { |
| lastStep = System.currentTimeMillis(); |
| notifyInitializationStep("persist "+shortClassName, 0.4, 0.5, count, total); |
| } |
| } |
| notifyInitializationStep("persist "+shortClassName, 0, 0.9, count, total); |
| } |
| em.getTransaction().commit(); |
| |
| logTraceEntities(resultEntities, resultClass); |
| |
| } catch (NullPointerException npe) { |
| if (em != null) { |
| em.getTransaction().rollback(); |
| } |
| log.error( |
| "maybe EMF is not set?!? - while updating instance of " |
| + resultClass.getCanonicalName() |
| + " in persistence " |
| + persistenceUnit, npe); |
| } catch (Exception e) { |
| if (em != null) { |
| em.getTransaction().rollback(); |
| } |
| log.error("while updating instance of " |
| + resultClass.getCanonicalName() |
| + " in persistence " + persistenceUnit, e); |
| } |
| if (em != null) { |
| em.close(); |
| } |
| } |
| generatorDoneStates.put(generator, true); |
| ProductConfiguration.setMockDataGeneratorDone(generator); |
| singleFillDone = true; |
| } |
| } else { |
| log.info("ignore generator: " |
| + generator.getClass().getCanonicalName()); |
| singleFillDone = false; |
| } |
| } |
| else { |
| singleFillDone = false; |
| } |
| return singleFillDone; |
| } |
| |
| private void logTraceEntities(List<Object> resultEntities, |
| Class<?> resultClass) { |
| if (log.isTraceEnabled()) { |
| List<Method> getters = findGetters(resultClass); |
| Map<String, Integer> columns = new HashMap<String, Integer>(); |
| for (Method getter : getters) { |
| String column = getter.getName().substring(3); |
| columns.put(column, column.length()); |
| } |
| for (Object entity : resultEntities) { |
| for (Method getter : getters) { |
| try { |
| String column = getter.getName().substring(3); |
| Object value = getter.invoke(entity, (Object[]) null); |
| String text = (value == null ? "<null>" : value.toString()); |
| text = text.replace("\n", "\\n"); |
| if (text.length() > columns.get(column)) { |
| columns.put(column, text.length()); |
| } |
| } catch (Exception e) { |
| } |
| } |
| } |
| { |
| String line = "ID "; |
| for (Method getter : getters) { |
| String column = getter.getName().substring(3); |
| line += "|" + String.format("%-" + columns.get(column) + "s", column); |
| } |
| log.trace(line); |
| } |
| int id = 0; |
| for (Object entity : resultEntities) { |
| id += 1; |
| String line = String.format("%-4d", id); |
| for (Method getter : getters) { |
| try { |
| String column = getter.getName().substring(3); |
| Object value = getter.invoke(entity,(Object[]) null); |
| String text = (value == null ? "<null>" : value.toString()); |
| text = text.replace("\n", "\\n"); |
| line += "|" + String.format("%-"+ columns.get(column)+ "s", text); |
| } catch (Exception e) { |
| } |
| } |
| log.trace(line); |
| } |
| } |
| } |
| |
| @Override |
| public boolean hasInitializationToBeDone() { |
| for (IEntityMockDataGenerator generator : generatorDoneStates.keySet()) { |
| if (!generatorDoneStates.get(generator)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void calculateInitializationTotalSteps() { |
| totalSteps = 0; |
| stepsDone = 0; |
| for (IEntityMockDataGenerator generator : generatorDoneStates.keySet()) { |
| int steps = |
| // --- each data interchange |
| generator.getDataInterchangeSteps() |
| // --- each entity mock generating |
| + generator.getEntityMockSteps() |
| // --- each entity persisting |
| + generator.getEntityMockSteps(); |
| totalSteps += steps; |
| if (generatorDoneStates.get(generator)) { |
| stepsDone += steps; |
| } |
| } |
| } |
| |
| @Override |
| public int getInitializationTotalSteps() { |
| return totalSteps; |
| } |
| |
| @Override |
| public String getInitializationProviderTitle() { |
| return "Mock"; |
| } |
| |
| @Override |
| public void notifyInitializationDone() { |
| if (log.isDebugEnabled()) { log.debug("initialization done"); } |
| super.notifyInitializationDone(); |
| } |
| |
| @Override |
| public void notifyInitializationStep(String stepTitle, int addSteps, double progressOfStep) { |
| stepsDone += addSteps; |
| InitializationNotification notification = new InitializationNotification(this, stepTitle, stepsDone, progressOfStep); |
| if (log.isDebugEnabled()) { log.debug("initialization: "+notification.getRecommendedProgressTitle(true)); } |
| notifyInitializationStep(notification); |
| } |
| |
| @Override |
| public void notifyInitializationStep(String stepTitle, double progressOfStep, double sizeOfSubProgress, int count, int total) { |
| notifyInitializationStep(stepTitle+" "+count+"/"+total, 0, (progressOfStep + sizeOfSubProgress*(double)count/(double)total)); |
| } |
| |
| @Override |
| public int getInitializationSubStepNotifySize() { |
| return 100; |
| } |
| } |