blob: 724e1c99952ce64e3dc7609c8946ac1e85fced56 [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
*
*
* This copyright notice shows up in the generated Java code
*
*/
package org.eclipse.osbp.xtext.datainterchange.jvmmodel
import com.vaadin.ui.Button
import com.vaadin.ui.CssLayout
import com.vaadin.ui.Label
import com.vaadin.ui.VerticalLayout
import java.io.OutputStream
import java.net.URL
import java.util.ArrayList
import java.util.HashMap
import java.util.Iterator
import java.util.Locale
import java.util.Map
import java.util.concurrent.ExecutorService
import javax.annotation.PostConstruct
import javax.annotation.PreDestroy
import javax.inject.Inject
import javax.persistence.EntityManager
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerFactory
import org.eclipse.e4.core.contexts.IEclipseContext
import org.eclipse.e4.core.services.events.IEventBroker
import org.eclipse.e4.ui.model.application.MApplication
import org.eclipse.osbp.dsl.entity.xtext.extensions.ModelExtensions
import org.eclipse.osbp.dsl.semantic.common.types.LReference
import org.eclipse.osbp.dsl.semantic.entity.LEntity
import org.eclipse.osbp.osgi.hybrid.api.AbstractHybridVaaclipseView
import org.eclipse.osbp.persistence.IPersistenceService
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService
import org.eclipse.osbp.ui.api.user.IUser
import org.eclipse.osbp.utils.constants.GeneratorConstants
import org.eclipse.osbp.utils.entitymock.IEntityImportInitializationListener
import org.eclipse.osbp.xtext.datainterchange.DataInterchange
import org.eclipse.osbp.xtext.datainterchange.DataInterchangeBean
import org.eclipse.osbp.xtext.datainterchange.DataInterchangeFileCSV
import org.eclipse.osbp.xtext.datainterchange.DataInterchangeFileEDI
import org.eclipse.osbp.xtext.datainterchange.DataInterchangeFileXML
import org.eclipse.osbp.xtext.datainterchange.DataInterchangePackage
import org.eclipse.osbp.xtext.datainterchange.common.WorkerThreadRunnable
import org.eclipse.osbp.xtext.i18n.DSLOutputConfigurationProvider
import org.eclipse.xtext.common.types.JvmDeclaredType
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmGenericType
import org.eclipse.xtext.common.types.JvmVisibility
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.milyn.Smooks
import org.milyn.SmooksFactory
import org.milyn.container.ExecutionContext
import org.osgi.framework.BundleEvent
import org.osgi.framework.BundleListener
import org.osgi.service.event.EventHandler
import org.slf4j.Logger
/**
* <p>
* Data Interchange Repository Domain Specific Language
* This inferrer infers models of extension .data and generates code to be used by any data interchanging process
* to facilitate the communication with external data sources and drains. Underlying components
* are from the smooks repository
* </p>
*
* @author Joerg Riegel
*/
class DataDSLJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
@Inject extension DataDSLModelGenerator dg
@Inject extension ModelExtensions
/* ramp up NTHREADS and threads are finished start more but up to this limit */
var NTHREADS = 10;
/**
* infer model on package base. Will be called for every defined package.
*
* @param dataInterchangePackage
* An instance of {@link DataInterchangePackage}
* @param acceptor
* the xtext acceptor interface
* @param isPreIndexingPhase
* true if in preindexing phase
*/
def dispatch void infer(DataInterchangePackage dataInterchangePackage, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
dataInterchangePackage.generatePckgName(acceptor)
// create a view
var cls = dataInterchangePackage.toClass(dataInterchangePackage.name.toString.concat("TriggerView"));
cls.simpleName = cls.simpleName.toFirstUpper
acceptor.accept(cls,
[
superTypes += _typeReferenceBuilder.typeRef(AbstractHybridVaaclipseView)
superTypes += _typeReferenceBuilder.typeRef(BundleListener)
superTypes += _typeReferenceBuilder.typeRef(IUser.UserLocaleListener)
documentation = GeneratorConstants.GENERATED_CLASSES_DOCUMENTATION
annotations += _annotationTypesBuilder.annotationRef(SuppressWarnings, "serial")
packageName = dataInterchangePackage.fullyQualifiedName.toString
it.toFields(dataInterchangePackage)
it.toConstructor(dataInterchangePackage)
it.toOperations(dataInterchangePackage)
])
// create smooks classes
for (dataInterchange : dataInterchangePackage.datInts) {
var clsName2 = dataInterchange.fullyQualifiedName
acceptor.accept(dataInterchange.toClass(clsName2),
[
superTypes += _typeReferenceBuilder.typeRef(WorkerThreadRunnable)
annotations += _annotationTypesBuilder.annotationRef(SuppressWarnings, "serial")
documentation = GeneratorConstants.GENERATED_CLASSES_DOCUMENTATION
it.toConstructor(dataInterchange)
it.toFields(dataInterchange)
it.toOperations(dataInterchange)
])
}
}
/**
* generate the fields in the inferred class.
*
* @param type
* the xtext generic types list
* @param pkg
* the current package inferred {@link DataInterchangePackage}
*/
def void toFields(JvmGenericType type, DataInterchangePackage pkg) {
var JvmField field = null
field = pkg.toField("sidebar", _typeReferenceBuilder.typeRef(VerticalLayout))
type.members += field
var name = pkg.name.toString.replace(".",":")
var String[] parts = name.split(":")
val String clsName = parts.get(parts.size-1).toFirstUpper
field = pkg.toField("log", _typeReferenceBuilder.typeRef(Logger)) [setInitializer([ append('''LoggerFactory.getLogger(«clsName»TriggerView.class)''') ])]
field.final = true
field.static = true
type.members += field
field = pkg.toField("menu", _typeReferenceBuilder.typeRef(CssLayout))
type.members += field
field = pkg.toField("workerInfo", _typeReferenceBuilder.typeRef(EventHandler))
type.members += field
field = pkg.toField("branding", _typeReferenceBuilder.typeRef(CssLayout))
type.members += field
field = pkg.toField("eventBroker", _typeReferenceBuilder.typeRef(IEventBroker)) [
annotations += _annotationTypesBuilder.annotationRef(Inject)
]
type.members += field
field = pkg.toField("persistenceService", _typeReferenceBuilder.typeRef(IPersistenceService)) [
annotations += _annotationTypesBuilder.annotationRef(Inject)
]
type.members += field
field = pkg.toField("progressBars", _typeReferenceBuilder.typeRef(Map, _typeReferenceBuilder.typeRef(String), _typeReferenceBuilder.typeRef(WorkerThreadRunnable)))
type.members += field
field = pkg.toField("executorService", _typeReferenceBuilder.typeRef(ExecutorService))
field.static = true
type.members += field
field = pkg.toField("dslMetadataService", _typeReferenceBuilder.typeRef(IDSLMetadataService)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("user", _typeReferenceBuilder.typeRef(IUser))[annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("logo", _typeReferenceBuilder.typeRef(Label))
type.members += field
field = pkg.toField("buttons",
_typeReferenceBuilder.typeRef(HashMap, _typeReferenceBuilder.typeRef(Button), _typeReferenceBuilder.typeRef(ArrayList)))
type.members += field
}
/**
* <p>build the constructors to be used by an e4 application.</p>
*
*/
def void toConstructor(JvmDeclaredType type, DataInterchangePackage pkg) {
type.members += pkg.toConstructor([
annotations += _annotationTypesBuilder.annotationRef(Inject)
parameters += pkg.toParameter("parent", _typeReferenceBuilder.typeRef(VerticalLayout))
parameters += pkg.toParameter("context", _typeReferenceBuilder.typeRef(IEclipseContext))
parameters += pkg.toParameter("app", _typeReferenceBuilder.typeRef(MApplication))
body = [ append('''super(parent,context,app);''')]
])
}
/**
* generate the fields in the inferred class.
*
* @param type
* the xtext generic types list
* @param pkg
* the current package inferred {@link DataInterchangePackage}
*/
def void toOperations(JvmGenericType type, DataInterchangePackage pkg) {
// create view
type.members += pkg.toMethod("createView", _typeReferenceBuilder.typeRef(Void::TYPE), [
parameters += pkg.toParameter("parent", _typeReferenceBuilder.typeRef(VerticalLayout))
body = [ append('''«pkg.createView»''')]
])
// create components
type.members += pkg.toMethod("createComponents", _typeReferenceBuilder.typeRef(Void::TYPE), [
body = [ append('''«pkg.createComponents»''')]
])
// unique name
type.members += pkg.toMethod("uniqueName", _typeReferenceBuilder.typeRef(String), [
parameters += pkg.toParameter("name", _typeReferenceBuilder.typeRef(String))
body = [ append('''«pkg.uniqueName»''')]
])
// is duplicate
type.members += pkg.toMethod("isDuplicate", _typeReferenceBuilder.typeRef(boolean), [
parameters += pkg.toParameter("name", _typeReferenceBuilder.typeRef(String))
body = [ append('''«pkg.isDuplicate»''')]
])
// find layout
type.members += pkg.toMethod("findButtonLayout", _typeReferenceBuilder.typeRef(VerticalLayout), [
parameters += pkg.toParameter("button", _typeReferenceBuilder.typeRef(Button))
body = [ append('''«pkg.findButtonLayout»''')]
])
// on bundle stopping - shutdown executorService
type.members += pkg.toMethod("bundleChanged", _typeReferenceBuilder.typeRef(Void::TYPE), [
annotations += _annotationTypesBuilder.annotationRef(Override)
parameters += pkg.toParameter("event", _typeReferenceBuilder.typeRef(BundleEvent))
body = [ append('''«pkg.bundleChanged»''')]
])
// subscribe to eventBroker
type.members += pkg.toMethod("subscribe", _typeReferenceBuilder.typeRef(Void::TYPE), [
visibility = JvmVisibility.PROTECTED
annotations += _annotationTypesBuilder.annotationRef(PostConstruct)
body = [append('''«pkg.subscribe»''')]
])
// unsubscribe from eventBroker
type.members += pkg.toMethod("unsubscribe", _typeReferenceBuilder.typeRef(Void::TYPE), [
visibility = JvmVisibility.PROTECTED
annotations += _annotationTypesBuilder.annotationRef(PreDestroy)
body = [append('''«pkg.unsubscribe»''')]
])
// locale notification
type.members += pkg.toMethod("localeChanged", _typeReferenceBuilder.typeRef(Void::TYPE),
[
visibility = JvmVisibility.PUBLIC
annotations += _annotationTypesBuilder.annotationRef(Override)
parameters += pkg.toParameter("locale", _typeReferenceBuilder.typeRef(Locale))
body = [append('''«pkg.localeChanged»''')]
])
}
/**
* handle the worker progress UI. provide the appropriate code.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String workerInfo(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
new EventHandler() {
@Override
public void handleEvent(Event event) {
// a worker notified this view that it is finished
String workername = (String)event.getProperty(EventUtils.DATA);
if (progressBars.containsKey(workername)) {
final WorkerThreadRunnable worker = progressBars.get(workername);
((VerticalLayout)worker.getProgressBarArea().getParent()).removeComponent(worker.getProgressBarArea());
progressBars.remove(workername);
}
}
};
'''
return body
}
/**
* subscribe the event broker for messages on changed locale and worker progress.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String subscribe(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
eventBroker.subscribe(EventBrokerMsg.WORKER_THREAD_INFO, workerInfo);
'''
return body
}
/**
* unsubscribe from event broker for messages.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String unsubscribe(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
eventBroker.unsubscribe(workerInfo);
'''
return body
}
/**
* normalize package name.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return normalized package name
*/
def String toEventID(DataInterchangePackage pkg) {
return pkg.fullyQualifiedName.toString.toUpperCase.replaceAll("(\\W)","_")
}
/**
* let the app wait for finishing the workers before allowing to stop bundle.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String bundleChanged(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
if (event.getType() == BundleEvent.STOPPING) {
log.debug("bundle is stopping");
// This will make the executorService accept no new threads
// and finish all existing threads in the queue
if (executorService != null) {
executorService.shutdown();
// Wait until all threads are finished
try {
executorService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("all executors finished");
}
'''
return body
}
/**
* find the right button layout for a given button.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String findButtonLayout(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
int count = menu.getComponentCount();
for(int i=0; i<count; i++) {
VerticalLayout buttonLayout = (VerticalLayout)menu.getComponent(i);
Button b = (Button)buttonLayout.getComponent(0);
if (b == button) {
return buttonLayout;
}
}
return null;
'''
return body
}
/**
* find out if a given thread-id is already running.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String isDuplicate(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
boolean found = false;
for(String threadName : progressBars.keySet()) {
if (name.equals(threadName)) {
found = true;
break;
}
}
return found;
'''
return body
}
/**
* make a name unique by concatenating a number.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String uniqueName(DataInterchangePackage pkg) {
var body = ""
body = '''
«body»
Integer cnt = 0;
String searchName;
do {
if (cnt > 0) {
searchName = name+cnt.toString();
} else {
searchName = name;
}
cnt ++;
}while(isDuplicate(searchName));
return searchName;
'''
return body
}
def String descriptionI18nKey(DataInterchange dataInterchange) {
if ((dataInterchange.descriptionValue == null) || dataInterchange.descriptionValue.isEmpty) {
dataInterchange.name
}
else {
dataInterchange.descriptionValue
}
}
/**
* build an e4 compatible view and create necessary components.
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String createView(DataInterchangePackage pkg) {
var body = '''
buttons = new HashMap<Button, ArrayList>();
workerInfo = «pkg.workerInfo»
Bundle bundle = FrameworkUtil.getBundle(getClass());
if (bundle != null) {
BundleContext ctx = bundle.getBundleContext();
if (ctx != null) {
ctx.addBundleListener(this);
}
}
FrameworkUtil.getBundle(getClass()).getBundleContext().addBundleListener(this);
executorService = Executors.newFixedThreadPool(«NTHREADS»);
progressBars = new HashMap<String,WorkerThreadRunnable>();
sidebar=new VerticalLayout();
menu=new CssLayout();
branding=new CssLayout();
parent.setPrimaryStyleName("osbp");
parent.setId("parent");
parent.setSizeFull();
sidebar.setSpacing(true);
sidebar.setId("sidebar");
parent.addComponent(sidebar);
parent.setExpandRatio(sidebar, 1.0f);
// create sidebar
sidebar.addStyleName("osbpsidebar");
sidebar.setSizeFull();
sidebar.addComponent(branding);
// branding title
branding.addStyleName("branding");
logo = new Label();
logo.setContentMode(ContentMode.HTML);
logo.setSizeUndefined();
branding.addComponent(logo);
// add menu
sidebar.addComponent(menu);
sidebar.setExpandRatio(menu, 1.0f);
'''
body = '''«body»
// add menu items
Button b;
VerticalLayout buttonLayout;
'''
for (dataInterchange : pkg.datInts) {
body = '''
«body»
b = new NativeButton();
b.setHtmlContentAllowed(true);
buttons.put(b, new ArrayList<String>(Arrays.asList(new String[] {"«DataDSLModelGenerator.CAPTION__REPFIX_I18NKEY_IMPORT»", "«dataInterchange.name»","«dataInterchange.descriptionI18nKey»"})));
b.addStyleName("icon-download");
b.addClickListener(new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.debug("pressed «dataInterchange.name» import");
String uniqueName = uniqueName("«dataInterchange.name»");
«dataInterchange.getBasicRunConfiguration(false, dataInterchange.getFileURL)»
«dataInterchange.defaultVariableName».setName(uniqueName);
«dataInterchange.defaultVariableName».setEventBroker(eventBroker);
«dataInterchange.defaultVariableName».setUi(UI.getCurrent());
«dataInterchange.defaultVariableName».setDirection(WorkerThreadRunnable.Direction.IMPORT);
«IF dataInterchange.progressBarStyle!=null»«dataInterchange.defaultVariableName».setProgressBarStyleName("«dataInterchange.progressBarStyle.literal»");«ENDIF»
findButtonLayout(event.getButton()).addComponent(«dataInterchange.defaultVariableName».getProgressBarArea());
progressBars.put(uniqueName, «dataInterchange.defaultVariableName»);
executorService.execute(«dataInterchange.defaultVariableName»);
log.debug("«dataInterchange.name» import added to executor queue");
}
});
buttonLayout = new VerticalLayout();
buttonLayout.addComponent(b);
menu.addComponent(buttonLayout);
'''
body = '''
«body»
b = new NativeButton();
b.setHtmlContentAllowed(true);
buttons.put(b, new ArrayList<String>(Arrays.asList(new String[] {"«DataDSLModelGenerator.CAPTION__REPFIX_I18NKEY_EXPORT»", "«dataInterchange.name»","«dataInterchange.descriptionI18nKey»"})));
b.addStyleName("icon-upload");
b.addClickListener(new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.debug("pressed «dataInterchange.name» export");
String uniqueName = uniqueName("«dataInterchange.name»");
«dataInterchange.getBasicRunConfiguration(false, dataInterchange.getFileURL)»
«dataInterchange.defaultVariableName».setName(uniqueName);
«dataInterchange.defaultVariableName».setEventBroker(eventBroker);
«dataInterchange.defaultVariableName».setUi(UI.getCurrent());
«dataInterchange.defaultVariableName».setDirection(WorkerThreadRunnable.Direction.EXPORT);
«IF dataInterchange.progressBarStyle!=null»«dataInterchange.defaultVariableName».setProgressBarStyleName("«dataInterchange.progressBarStyle.literal»");«ENDIF»
findButtonLayout(event.getButton()).addComponent(«dataInterchange.defaultVariableName».getProgressBarArea());
progressBars.put(uniqueName, «dataInterchange.defaultVariableName»);
executorService.execute(«dataInterchange.defaultVariableName»);
log.debug("«dataInterchange.name» export added to executor queue");
}
});
buttonLayout = new VerticalLayout();
buttonLayout.addComponent(b);
menu.addComponent(buttonLayout);
'''
}
body = '''
«body»
menu.addStyleName("menu");
menu.setHeight("100%");
user.addUserLocaleListener(this);
'''
return body
}
def String getDefaultVariableName(DataInterchange dataInterchange) {
return dataInterchange.name.toFirstLower
}
def String getBasicRunConfiguration(DataInterchange dataInterchange, boolean fqClass, String fileURL) {
var className = ""
if (fqClass) {
className = dataInterchange.fullyQualifiedName.toString
}
else {
className = dataInterchange.name
}
return '''
«className» «dataInterchange.getDefaultVariableName» = new «className»();
«dataInterchange.getDefaultVariableName».setFileURL("«fileURL»");
«dataInterchange.getDefaultVariableName».setPersistenceService(persistenceService);
'''
}
def String getFileURL(DataInterchange dataInterchange) {
switch(dataInterchange.fileEndpoint) {
DataInterchangeFileXML: return (dataInterchange.fileEndpoint as DataInterchangeFileXML).fileURL
DataInterchangeFileCSV: return (dataInterchange.fileEndpoint as DataInterchangeFileCSV).fileURL
DataInterchangeFileEDI: return (dataInterchange.fileEndpoint as DataInterchangeFileEDI).fileURL
}
return ""
}
/**
* create more components. this fragment is repainted if something changes (e.g. language)
*
* @param pkg
* the current package inferred {@link DataInterchangePackage}
* @return code fragment
*/
def String createComponents(DataInterchangePackage pkg) {
return ""
}
/**
* <p>build the constructor for each smooks class.</p>
*
* @param pkg
* the current datainterchange inferred {@link DataInterchange}
*/
def void toConstructor(JvmDeclaredType type, DataInterchange dataInterchange) {
type.members += dataInterchange.toConstructor([
body = [append('''
setName("«dataInterchange.name»");
''')]
])
}
/**
* <p>build the class variables.</p>
*
*/
def void toFields(JvmDeclaredType type, DataInterchange dataInterchange) {
var JvmField field = null
field = dataInterchange.toField("log", _typeReferenceBuilder.typeRef(Logger)) [setInitializer([ append('''LoggerFactory.getLogger("dataInterchange")''') ])]
field.final = true
field.static = true
type.members += field
field = dataInterchange.toField("smooksOSGIFactory", _typeReferenceBuilder.typeRef(SmooksFactory)) [setInitializer([ append('''null''') ])]
type.members += field
field = dataInterchange.toField("smooks", _typeReferenceBuilder.typeRef(Smooks))
type.members += field
// field = dataInterchange.toField("xmlBinding", _typeReferenceBuilder.typeRef(XMLBinding))
// type.members += field
field = dataInterchange.toField("em", _typeReferenceBuilder.typeRef(EntityManager))
type.members += field
field = dataInterchange.toField("fileURL", _typeReferenceBuilder.typeRef(URL))
type.members += field
field = dataInterchange.toField("executionContext", _typeReferenceBuilder.typeRef(ExecutionContext))
type.members += field
field = dataInterchange.toField("file", _typeReferenceBuilder.typeRef(OutputStream))
type.members += field
field = dataInterchange.toField("out", _typeReferenceBuilder.typeRef(OutputStream))
type.members += field
field = dataInterchange.toField("pollingInterval", _typeReferenceBuilder.typeRef(int)) [setInitializer([ append('''500''') ])]
type.members += field
field = dataInterchange.toField("transformerFactory", _typeReferenceBuilder.typeRef(TransformerFactory)) [setInitializer([ append('''TransformerFactory.newInstance()''') ])]
type.members += field
field = dataInterchange.toField("transformer", _typeReferenceBuilder.typeRef(Transformer))
type.members += field
}
/**
* <p>build the methods.</p>
*
*/
def void toOperations(JvmDeclaredType type, DataInterchange dataInterchange) {
type.members += dataInterchange.toMethod("run", _typeReferenceBuilder.typeRef(Void::TYPE), [
annotations += _annotationTypesBuilder.annotationRef(Override)
body = [ append('''run(null);''')]
])
type.members += dataInterchange.toMethod("run", _typeReferenceBuilder.typeRef(Void::TYPE), [
parameters += dataInterchange.toParameter("importListener", _typeReferenceBuilder.typeRef(IEntityImportInitializationListener))
body = [ append('''«dataInterchange.performSmooks»''')]
])
type.members += dataInterchange.toMethod("init", _typeReferenceBuilder.typeRef(boolean), [
visibility = JvmVisibility.PROTECTED
parameters += dataInterchange.toParameter("direction", _typeReferenceBuilder.typeRef(WorkerThreadRunnable.Direction))
body = [ append('''«dataInterchange.init»''')]
])
type.members += dataInterchange.toMethod("setFileURL", _typeReferenceBuilder.typeRef(Void::TYPE), [
parameters += dataInterchange.toParameter("filePath", _typeReferenceBuilder.typeRef(String))
body = [ append('''«dataInterchange.fileUrl»''')]
])
}
/**
* <p>create URL from filepath or other string.</p>
*
* @param pkg
* the current datainterchange inferred {@link DataInterchange}
* @return code fragment
*/
def fileUrl(DataInterchange dataInterchange)
'''
fileURL = null;
String path = filePath;
if (filePath.startsWith("file://") && !org.eclipse.osbp.utils.common.SystemInformation.isMacOS()) {
path = filePath.substring("file://".length());
}
try {
fileURL = new URL(path);
} catch (MalformedURLException e1) {
if(e1.getMessage().startsWith("unknown protocol")) {
try {
fileURL = Paths.get(path).toUri().toURL();
} catch (MalformedURLException e2) {
log.error(e2.getLocalizedMessage()+e2.getCause());
}
}
}
'''
/**
* init smooks factory to create the core import process.
* setup listeners for UI communication.
* setup persistence layer.
*
* @param pkg
* the current datainterchange inferred {@link DataInterchange}
* @return code fragment
*/
def String init(DataInterchange dataInterchange) {
var firstEntity = (dataInterchange.path.iterator.next as DataInterchangeBean)
var body =
'''
if(getEventBroker()!=null) {
pollingInterval = UI.getCurrent().getPollInterval();
UI.getCurrent().setPollInterval(500);
}
try {
transformerFactory.setAttribute("indent-number", 4);
transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
transformer.setOutputProperty(OutputKeys.MEDIA_TYPE, "text/xml");
// init smooks
setProgressBarEnabled(true);
setProgressIndeterminated(true);
if (log.isDebugEnabled()) log.debug("initializing smooks factory");
smooksOSGIFactory = new SmooksOSGIFactory(FrameworkUtil.getBundle(this.getClass()));
smooks = smooksOSGIFactory.createInstance(FrameworkUtil.getBundle(this.getClass()).getResource("«DSLOutputConfigurationProvider.SMOOKS_OUTPUT_DIRECTORY»/«dataInterchange.name»-"+direction.toString().toLowerCase()+".xml").openStream());
// prepare execution context
executionContext = smooks.createExecutionContext();
executionContext.setEventListener(this);
// get entity manager
if (log.isDebugEnabled()) log.debug("opening entity manager to persist smooks results");
getPersistenceService().registerPersistenceUnit("«firstEntity.entity.persistenceUnit»", «firstEntity.entity.fullyQualifiedName».class);
em = getPersistenceService().getEntityManagerFactory("«firstEntity.entity.persistenceUnit»").createEntityManager();
«IF dataInterchange.createReport»
// create a filtering report -- impacts performance
if (log.isDebugEnabled()) log.debug("reporting is on - impacting performance");
String location = FrameworkUtil.getBundle(this.getClass()).getLocation()+"«DSLOutputConfigurationProvider.SMOOKS_OUTPUT_DIRECTORY»/«dataInterchange.name»-"+direction.toString().toLowerCase()+"-report.html";
executionContext.setEventListener(new HtmlReportGenerator(location));
«ENDIF»
} catch (Exception e) {
if(getEventBroker()!=null) {
UI.getCurrent().setPollInterval(pollingInterval);
}
log.error(e.getLocalizedMessage()+e.getCause());
return false;
}
if(direction == Direction.EXPORT) {
int openTry = 0;
file = null;
URI uri = null;
try {
uri = fileURL.toURI();
} catch (URISyntaxException e) {
log.error(e.getLocalizedMessage()+e.getCause());
return false;
}
do {
Path path = null;
path = Paths.get(uri);
try {
// find a unique name - similar to given
file = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW);
} catch (FileAlreadyExistsException ae) {
openTry ++;
try {
uri = fileURL.toURI();
int pos = uri.getPath().lastIndexOf('.');
if(pos == -1) {
uri = new URI(uri.getScheme()+":"+uri.getPath()+openTry);
} else {
uri = new URI(uri.getScheme()+":"+uri.getPath().substring(0,pos)+openTry+"."+uri.getPath().substring(pos+1));
}
} catch (URISyntaxException e) {
log.error(e.getLocalizedMessage()+e.getCause());
return false;
}
} catch (IOException e) {
log.error(e.getLocalizedMessage()+e.getCause());
return false;
}
}while(file == null);
out = new BufferedOutputStream(file);
}
return true;
'''
return body
}
/**
* use smooks factory to create the core import process.
* setup listeners for UI communication.
* setup persistence layer.
*
* @param pkg
* the current datainterchange inferred {@link DataInterchange}
* @return code fragment
*/
def String performSmooks(DataInterchange dataInterchange) {
var firstEntity = (dataInterchange.path.iterator.next as DataInterchangeBean)
var body = '''
if(!init(getDirection())) {
return;
}
try {
if(getDirection()==WorkerThreadRunnable.Direction.IMPORT) {
'''
body = '''
«body»
if (importListener != null) {
importListener.notifyInitializationStep("datainterchange «dataInterchange.name.toFirstUpper» load.", 0.4, 0.45, 0, 0);
}
// execute the smooks filtering
JavaResult result = new JavaResult();
InputStream in = fileURL.openStream();
byte[] contents = StreamUtils.readStream(in);
setLength(contents.length);
setAvgElementSize(«IF dataInterchange.elementSize==0»10«ELSE»«dataInterchange.elementSize»«ENDIF»);
StreamSource source = new StreamSource(new ByteArrayInputStream(contents));
'''
for (path:dataInterchange.path) {
for (lookup:path.lookup) {
if (lookup.cached) {
body = '''
«body»
em.setProperty(PersistenceUnitProperties.CACHE_SIZE_+"«lookup.entity.fullyQualifiedName»", "«lookup.cacheSize»");
'''
}
}
}
body = '''
«body»
PersistenceUtil.setDAORegister(executionContext, new EntityManagerRegister(em));
// execute smooks filtering
if (log.isDebugEnabled()) log.debug("filtering starts");
setProgressIndeterminated(false);
if (importListener != null) {
importListener.notifyInitializationStep("datainterchange Warehouses load..", 0.4, 0.47, 0, 0);
}
smooks.filterSource(executionContext, source, result);
if (importListener != null) {
importListener.notifyInitializationStep("datainterchange Warehouses load...", 0.4, 0.48, 0, 0);
}
if (log.isDebugEnabled()) log.debug("smooks filtering finished");
'''
if (firstEntity.recordList) {
body = '''
«body»
// retrieve bean list
List<«firstEntity.entity.fullyQualifiedName»> «firstEntity.entity.name.toFirstLower»List = Arrays.asList((«firstEntity.entity.fullyQualifiedName»[]) result.getBean("«firstEntity.entity.name»List"));
// persist
em.getTransaction().begin();
if (log.isDebugEnabled()) log.debug("persisting results");
int total = «firstEntity.entity.name.toFirstLower»List.size();
int count = 0;
long lastStep = System.currentTimeMillis();
if (importListener != null) {
importListener.notifyInitializationStep("datainterchange «dataInterchange.name.toFirstUpper»", 0.4, 0.5, count, total);
}
for(«firstEntity.entity.fullyQualifiedName» «firstEntity.entity.name.toFirstLower»:«firstEntity.entity.name.toFirstLower»List) {
try {
em.persist(«firstEntity.entity.name.toFirstLower»);
}
catch (ConstraintViolationException cve) {
log.error("«firstEntity.entity.name.toFirstLower» #"+(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 ((importListener != null) && ((count % importListener.getInitializationSubStepNotifySize() == 0) || (thisStep-lastStep > 2500))) {
lastStep = System.currentTimeMillis();
importListener.notifyInitializationStep("datainterchange «dataInterchange.name.toFirstUpper»", 0.4, 0.5, count, total);
}
}
if (importListener != null) {
importListener.notifyInitializationStep("datainterchange «dataInterchange.name.toFirstUpper»", 0.4, 0.5, count, total);
}
if (log.isDebugEnabled()) log.debug("committing results");
em.getTransaction().commit();
'''
} else {
body = '''
«body»
// retrieve the root bean
«firstEntity.entity.fullyQualifiedName» «firstEntity.entity.name.toFirstLower» = («firstEntity.entity.fullyQualifiedName») result.getBean("«firstEntity.entity.name»");
// persist
em.getTransaction().begin();
if (log.isDebugEnabled()) log.debug("persisting results");
em.persist(«firstEntity.entity.name.toFirstLower»);
if (log.isDebugEnabled()) log.debug("committing results");
em.getTransaction().commit();
'''
}
if (firstEntity.markLatest) {
body = '''
«body»
if (log.isDebugEnabled()) log.debug("mark results as latest import");
em.setProperty(QueryHints.PESSIMISTIC_LOCK, PessimisticLock.Lock);
em.getTransaction().begin();
em.createQuery("update «firstEntity.entity.name» set «firstEntity.latestProperty.name» = 0").executeUpdate();
em.createQuery("update «firstEntity.entity.name» set «firstEntity.latestProperty.name» = 1 where «»id= :id").setParameter("id", «firstEntity.entity.name.toFirstLower».getId()).executeUpdate();
if (log.isDebugEnabled()) log.debug("committing mark");
em.getTransaction().commit();
'''
}
if (dataInterchange.refreshEnabled) {
body = '''
«body»
if(getEventBroker()!=null) {
getEventBroker().send(EventBrokerMsg.REFRESH_VIEW+"«dataInterchange.refresh»", "*");
}
'''
}
body = '''
«body»
if (log.isDebugEnabled()) log.debug("results persisted");
'''
var iter = dataInterchange.path.iterator
var root = (iter.next as DataInterchangeBean).entity
body = '''
«body»
} else {
if (log.isDebugEnabled()) log.debug("prepare export");
««« «IF dataInterchange.fileEndpoint instanceof DataInterchangeFileXML»
««« // Create and initilise the XMLBinding instance...
««« xmlBinding = new XMLBinding(smooks);
««« xmlBinding.intiailize();
««« xmlBinding.setOmitXMLDeclaration(true);
««« «ENDIF»
««« «dataInterchange.buildEntityGraph(iter, root)»
int pageNumber = 1;
int pageSize = 1000;
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
if (log.isDebugEnabled()) log.debug("evaluate root entity count");
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(countQuery.from(«firstEntity.entity.fullyQualifiedName».class)));
Long count = em.createQuery(countQuery).getSingleResult();
if (log.isDebugEnabled()) log.debug("root entity count is "+count.toString());
setLength(count*«IF dataInterchange.elementSize==0»10«ELSE»«dataInterchange.elementSize»«ENDIF»);
setAvgElementSize(1);
CriteriaQuery<«firstEntity.entity.fullyQualifiedName»> criteriaQuery = criteriaBuilder.createQuery(«firstEntity.entity.fullyQualifiedName».class);
Root<«firstEntity.entity.fullyQualifiedName»> from = criteriaQuery.from(«firstEntity.entity.fullyQualifiedName».class);
/* ... not necessary due to eager loading of entities via JPA ... «dataInterchange.buildJoins(iter, root)» ...*/
CriteriaQuery<«firstEntity.entity.fullyQualifiedName»> select = criteriaQuery.multiselect(from);
TypedQuery<«firstEntity.entity.fullyQualifiedName»> typedQuery = em.createQuery(select);
««« typedQuery.setHint(QueryHints.JPA_LOAD_GRAPH, «firstEntity.entity.name.toFirstLower»Graph);
setProgressIndeterminated(false);
while (pageNumber < count.intValue()) {
if (log.isDebugEnabled()) log.debug("fetch and process entry "+pageNumber+" to "+(pageNumber+pageSize));
typedQuery.setFirstResult(pageNumber - 1);
typedQuery.setMaxResults(pageSize);
List<«firstEntity.entity.fullyQualifiedName»> queryResults = typedQuery.getResultList();
««« «IF dataInterchange.fileEndpoint instanceof DataInterchangeFileCSV»
StringWriter writer = new StringWriter();
smooks.filterSource(executionContext, new JavaSource(queryResults), new StreamResult(writer));
out.write(writer.toString().getBytes());
««« «ELSEIF dataInterchange.fileEndpoint instanceof DataInterchangeFileXML»
««« for(«firstEntity.entity.fullyQualifiedName» row:queryResults) {
««« String outXML = xmlBinding.toXML(row);
««« out.write(outXML.getBytes());
««« }
««« JAXBContext jc = JAXBContext.newInstance(«firstEntity.entity.fullyQualifiedName».class);
««« Marshaller marshaller = jc.createMarshaller();
««« marshaller.marshal(queryResults, out);
««« «ENDIF»
pageNumber += pageSize;
}
if (log.isDebugEnabled()) log.debug("export finished");
}
'''
body = '''
«body»
} catch (Exception e) {
log.error("«dataInterchange.name»: "+e.getLocalizedMessage()+e.getCause(), e);
} finally {
if(file != null) {
try {
out.close();
file.close();
} catch (IOException e) {
log.error(e.getLocalizedMessage()+e.getCause());
}
}
smooks.close();
if(getEventBroker()!=null) {
UI.getCurrent().setPollInterval(pollingInterval);
}
// close everything
if (em != null) {
em.close();
}
if (log.isDebugEnabled()) log.debug("datainterchange finished");
}
'''
return body
}
/**
* This seems to be not necessary, because JPA seems to load the entities eager itself!
* With this functionality active, the sql result set would contain all root entities multiple times!
*/
@Deprecated
def buildJoins(DataInterchange interchange, Iterator<DataInterchangeBean> iter, LEntity rootEntity) {
var root = rootEntity
var body = ""
if(iter.hasNext) {
body = '''«body»from'''
}
while(iter.hasNext) {
var entity = (iter.next as DataInterchangeBean).entity
for(f:root.features) {
if (f instanceof LReference && f.toMany && f.type instanceof LEntity && f.type.toName.equals(entity.toName)) {
// one to many for the entity in sequence is found
body = '''«body».fetch("«f.name»", JoinType.LEFT)'''
}
}
root = entity
}
if(!body.empty) {
body = body + ";"
}
return body
}
def localeChanged(DataInterchangePackage pkg)
'''
«IF pkg.title != null»
if(logo != null) {
logo.setValue(dslMetadataService.translate(locale.toLanguageTag(), "«pkg.title»"));
}
«ENDIF»
if(buttons != null) {
for(Button button: buttons.keySet()) {
ArrayList i18nKeys = buttons.get(button);
button.setCaption(dslMetadataService.translate(locale.toLanguageTag(),(String)i18nKeys.get(1)));
button.setDescription(dslMetadataService.translate(locale.toLanguageTag(),(String)i18nKeys.get(0))+" "+dslMetadataService.translate(locale.toLanguageTag(),(String)i18nKeys.get(2)));
}
}'''
}