blob: a9a580516e35b93c9ffc175629a26c49bb671e78 [file] [log] [blame]
/**
*
* 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;
}
}