package fi.otavanopisto.pyramus.util; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.persistence.Entity; import javax.validation.ConstraintViolation; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.xpath.XPathAPI; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.w3c.dom.traversal.NodeIterator; import org.xml.sax.SAXException; import fi.internetix.smvc.SmvcRuntimeException; import fi.otavanopisto.pyramus.dao.DAOFactory; import fi.otavanopisto.pyramus.dao.SystemDAO; import fi.otavanopisto.pyramus.util.dataimport.DataImportUtils; import fi.otavanopisto.pyramus.util.dataimport.ValueInterpreter; public class DataImporter { public void importDataFromStream(InputStream stream, Collection<String> entities) { try { DocumentBuilder db = documentBuilderFactory.newDocumentBuilder(); Document initialDataDocument = db.parse(stream); importDataFromDocument(initialDataDocument, entities); } catch (ParserConfigurationException e) { throw new SmvcRuntimeException(e); } catch (SAXException e) { throw new SmvcRuntimeException(e); } catch (IOException e) { throw new SmvcRuntimeException(e); } } @SuppressWarnings({ "rawtypes" }) public void importDataFromDocument(Document initialDataDocument, Collection<String> entities) { SystemDAO systemDAO = DAOFactory.getInstance().getSystemDAO(); try { NodeIterator storeIterator = XPathAPI.selectNodeIterator(initialDataDocument.getDocumentElement(), "store"); Node storeNode; while ((storeNode = storeIterator.nextNode()) != null) { if (storeNode instanceof Element) { Element storeElement = (Element) storeNode; String variableName = storeElement.getAttribute("storeVariable"); String hql = storeElement.getAttribute("hql"); Object result; System.out.println("Processing " + variableName); if (!StringUtils.isBlank(hql)) { result = systemDAO.createJPQLQuery(hql).getSingleResult(); if (result == null) throw new SmvcRuntimeException(new Exception("storeVariable hql=\"" + hql + "\" returned null")); } else { Class variableClass; try { variableClass = Class.forName(storeElement.getAttribute("class")); StringBuilder jpqlBuilder = new StringBuilder(); jpqlBuilder.append("from "); jpqlBuilder.append(variableClass); NodeList criteriaList = storeElement.getChildNodes(); if (criteriaList.getLength() > 0) { jpqlBuilder.append(" where "); for (int i = 0; i < criteriaList.getLength(); i++) { if (criteriaList.item(i) instanceof Element) { Element criteriaElement = (Element) criteriaList.item(i); StoreVariableCriteria criteria = StoreVariableCriteria.getCriteria(criteriaElement.getNodeName()); switch (criteria) { case Equals: String property = criteriaElement.getAttribute("name"); String value = ((Text) criteriaElement.getFirstChild()).getData(); jpqlBuilder.append(property + " = " + value); break; } } if (i < (criteriaList.getLength() - 1)) { jpqlBuilder.append(" AND "); } } } result = systemDAO.createJPQLQuery(jpqlBuilder.toString()).getSingleResult(); if (result == null) throw new SmvcRuntimeException(new Exception("storeVariable class=\"" + variableClass.getName() + "\" returned null")); } catch (ClassNotFoundException e) { throw new SmvcRuntimeException(e); } } System.out.println("Storing " + variableName); storeValue(variableName, getPojoId(result)); } } NodeIterator entityIterator = XPathAPI.selectNodeIterator(initialDataDocument.getDocumentElement(), "entity"); Node node; while ((node = entityIterator.nextNode()) != null) { if (node instanceof Element) { Element element = (Element) node; String entityPackageName = element.getAttribute("package"); String entityClassName = element.getAttribute("class"); String className = entityPackageName + '.' + entityClassName; if (entities == null ||entities.contains(className)) { Class<?> entityClass = Class.forName(entityPackageName + "." + entityClassName); NodeIterator entryIterator = XPathAPI.selectNodeIterator(element, "e"); Element entry; while ((entry = (Element) entryIterator.nextNode()) != null) { processEntryTag(null, entityClass, entry, null); System.out.println(" >> added entity " + className); } } } } } catch (TransformerException e) { throw new SmvcRuntimeException(e); } catch (ClassNotFoundException e) { throw new SmvcRuntimeException(e); } } private Object processEntryTag(Object parent, Class<?> entityClass, Element entry, Element parentListElement) { SystemDAO systemDAO = DAOFactory.getInstance().getSystemDAO(); System.out.println("Processing entity: " + entityClass.getName()); try { Constructor<?> defaultConstructor = entityClass.getDeclaredConstructor(new Class[] {}); defaultConstructor.setAccessible(true); Object pojo = defaultConstructor.newInstance(new Object[] {}); NamedNodeMap attributes = entry.getAttributes(); for (int i = 0, len = attributes.getLength(); i < len; i++) { Node attribute = attributes.item(i); String propertyName = attribute.getNodeName(); if (!"storeVariable".equals(propertyName)) { String propertyValue = attribute.getNodeValue(); if (propertyValue.startsWith("{") && propertyValue.endsWith("}")) { propertyValue = String.valueOf(getStoredValue(propertyValue.substring(1, propertyValue.length() - 1))); } System.out.println(" >> property: " + propertyName + " to " + propertyValue); DataImportUtils.setValue(pojo, propertyName, propertyValue); } } NodeList entryChildren = entry.getChildNodes(); for (int j = 0, len = entryChildren.getLength(); j < len; j++) { Node n = entryChildren.item(j); if (n instanceof Element) { Element element = (Element) n; String nodeName = element.getTagName(); EntityDirective entityDirective = EntityDirective.getDirective(nodeName); if (entityDirective == null) throw new SmvcRuntimeException(new Exception("Unknown entity directive '" + nodeName + "'")); switch (entityDirective) { case Map: String mapName = element.getAttribute("name"); String methodName = element.getAttribute("method"); NodeIterator itemIterator = XPathAPI.selectNodeIterator(element, "item"); Element itemElement; while ((itemElement = (Element) itemIterator.nextNode()) != null) { Element keyElement = (Element) XPathAPI.selectSingleNode(itemElement, "key"); Element valueElement = (Element) XPathAPI.selectSingleNode(itemElement, "value"); if (keyElement == null||valueElement == null) throw new SmvcRuntimeException(new Exception("Malformed map item")); String keyValue = ((Text) keyElement.getFirstChild()).getData(); String valueValue = ((Text) valueElement.getFirstChild()).getData(); Field mapField = DataImportUtils.getField(pojo, mapName); ParameterizedType genericType = (ParameterizedType) mapField.getGenericType(); Class<?> mapKeyTypeClass = (Class<?>) genericType.getActualTypeArguments()[0]; Class<?> mapValueTypeClass = (Class<?>) genericType.getActualTypeArguments()[1]; Object key; Object value; if (!isHibernateClass(mapKeyTypeClass)) { ValueInterpreter<?> valueInterpreter = DataImportUtils.getValueInterpreter(mapKeyTypeClass); if (valueInterpreter != null) key = valueInterpreter.interpret(keyValue); else throw new SmvcRuntimeException(new Exception("Value interpreter for " + mapKeyTypeClass + " is not implemented yet")); } else { key = getPojo(mapKeyTypeClass, keyValue); } if (!isHibernateClass(mapValueTypeClass)) { ValueInterpreter<?> valueInterpreter = DataImportUtils.getValueInterpreter(mapValueTypeClass); if (valueInterpreter != null) value = valueInterpreter.interpret(valueValue); else throw new SmvcRuntimeException(new Exception("Value interpreter for " + mapValueTypeClass + " is not implemented yet")); } else { value = getPojo(mapValueTypeClass, valueValue); } Class<?>[] params = {key.getClass(), value.getClass()}; Object[] paramValues = {key, value}; Method method = DataImportUtils.getMethod(pojo, methodName, params); method.invoke(pojo, paramValues); } break; case List: String listName = element.getAttribute("name"); String listClass = element.getAttribute("class"); Class<?> listTypeClass; if (StringUtils.isBlank(listClass)) { Field listField = DataImportUtils.getField(pojo, listName); ParameterizedType genericType = (ParameterizedType) listField.getGenericType(); listTypeClass = (Class<?>) genericType.getActualTypeArguments()[0]; } else { listTypeClass = Class.forName(listClass); } NodeIterator listEntryIterator = XPathAPI.selectNodeIterator(element, "e"); Element listEntry; while ((listEntry = (Element) listEntryIterator.nextNode()) != null) { Object listEntity = processEntryTag(pojo, listTypeClass, listEntry, element); System.out.println(" >> added list entity " + listEntity.getClass().toString()); } break; case Join: String className = element.getAttribute("class"); String idField = ((Text) element.getFirstChild()).getData(); Field joinField = DataImportUtils.getField(pojo, element.getAttribute("name")); if ("PARENT".equals(idField)) { String parentListMethod = parentListElement.getAttribute("method"); AccessType parentListAccessType = AccessType.Field; if (!StringUtils.isEmpty(parentListMethod)) parentListAccessType = AccessType.Method; if (parentListAccessType == AccessType.Method) { Class<?>[] params = {pojo.getClass()}; Method method = DataImportUtils.getMethod(parent, parentListMethod, params); method.invoke(parent, pojo); } else { DataImportUtils.setFieldValue(pojo, joinField, parent); } } else { Object joinedPojo = getPojo(className, idField); if (joinedPojo != null) { DataImportUtils.setFieldValue(pojo, joinField, joinedPojo); } else { throw new SmvcRuntimeException(new Exception(className + " #" + idField + " could not be found")); } } break; } } } Set<ConstraintViolation<Object>> constraintViolations = systemDAO.validateEntity(pojo); if (constraintViolations.isEmpty()) { systemDAO.persistEntity(pojo); } else { String message = ""; for (ConstraintViolation<Object> constraintViolation : constraintViolations) { message += constraintViolation.getMessage() + '\n'; } throw new SmvcRuntimeException(new Exception("Validation failure: " + message)); } Long id = getPojoId(pojo); if (id != null) { String storeVariable = entry.getAttribute("storeVariable"); if (!StringUtils.isBlank(storeVariable)) { storeValue(storeVariable, id); System.out.println(" >> # " + id + " stored as " + storeVariable); } } return id; } catch (Exception e) { throw new SmvcRuntimeException(new Exception("Error while processing entity: " + entityClass.getName() + " " + e.getMessage(), e)); } } private Long getPojoId(Object pojo) { if (pojo != null) { try { Method getIdMethod = DataImportUtils.getMethod(pojo, "getId", new Class<?>[] {}); if (getIdMethod != null) { return (Long) getIdMethod.invoke(pojo, new Object[] {}); } } catch (Exception e) { throw new SmvcRuntimeException(new Exception("getId failed for " + pojo.getClass().getName())); } } return null; } private Object getPojo(Class<?> clazz, String identifier) throws ClassNotFoundException { SystemDAO systemDAO = DAOFactory.getInstance().getSystemDAO(); Long id; if (identifier.startsWith("{") && identifier.endsWith("}")) { id = getStoredValue(identifier.substring(1, identifier.length() - 1)); if (id == null) throw new SmvcRuntimeException(new Exception("Could not resolve: " + identifier)); } else { id = NumberUtils.createLong(identifier); } return systemDAO.findEntityById(clazz, id); } private Object getPojo(String className, String identifier) throws ClassNotFoundException { Class<?> pojoClass = Class.forName(className); return getPojo(pojoClass, identifier); } private boolean isHibernateClass(Class<?> clazz) { return clazz.isAnnotationPresent(Entity.class); } private Long getStoredValue(String name) { return storedValues.get(name); } private void storeValue(String name, Long id) { storedValues.put(name, id); } private enum AccessType { Field, Method } private Map<String, Long> storedValues = new HashMap<>(); private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); public enum EntityDirective { Join ("j"), List ("list"), Map ("map"); private EntityDirective(String name) { this.name = name; } private String name; public static EntityDirective getDirective(String name) { for (EntityDirective entityDirectiveNode : values()) { if (entityDirectiveNode.name.equals(name)) return entityDirectiveNode; } return null; } } public enum StoreVariableCriteria { Equals ("eq"); private StoreVariableCriteria(String name) { this.name = name; } private String name; public static StoreVariableCriteria getCriteria(String name) { for (StoreVariableCriteria criteria : values()) { if (criteria.name.equals(name)) return criteria; } return null; } } }