/*
* Copyright (c) 2015 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.io.appschema.writer;
import static eu.esdihumboldt.hale.io.appschema.AppSchemaIO.getFirstElementByTagName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.commons.io.FileUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.google.common.io.ByteStreams;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.align.io.AlignmentIO;
import eu.esdihumboldt.hale.common.align.io.AlignmentReader;
import eu.esdihumboldt.hale.common.align.io.impl.JaxbAlignmentReader;
import eu.esdihumboldt.hale.common.align.model.Alignment;
import eu.esdihumboldt.hale.common.core.HalePlatform;
import eu.esdihumboldt.hale.common.core.io.IOProvider;
import eu.esdihumboldt.hale.common.core.io.IOProviderConfigurationException;
import eu.esdihumboldt.hale.common.core.io.PathUpdate;
import eu.esdihumboldt.hale.common.core.io.extension.IOProviderDescriptor;
import eu.esdihumboldt.hale.common.core.io.extension.IOProviderExtension;
import eu.esdihumboldt.hale.common.core.io.impl.ComplexValue;
import eu.esdihumboldt.hale.common.core.io.impl.LogProgressIndicator;
import eu.esdihumboldt.hale.common.core.io.project.ProjectInfo;
import eu.esdihumboldt.hale.common.core.io.project.ProjectInfoAware;
import eu.esdihumboldt.hale.common.core.io.project.impl.ArchiveProjectReader;
import eu.esdihumboldt.hale.common.core.io.project.model.IOConfiguration;
import eu.esdihumboldt.hale.common.core.io.project.model.Project;
import eu.esdihumboldt.hale.common.core.io.project.model.ProjectFileInfo;
import eu.esdihumboldt.hale.common.core.io.report.IOReport;
import eu.esdihumboldt.hale.common.core.io.supplier.DefaultInputSupplier;
import eu.esdihumboldt.hale.common.core.io.supplier.FileIOSupplier;
import eu.esdihumboldt.hale.common.instance.io.InstanceReader;
import eu.esdihumboldt.hale.common.schema.io.SchemaIO;
import eu.esdihumboldt.hale.common.schema.io.SchemaReader;
import eu.esdihumboldt.hale.common.schema.model.Schema;
import eu.esdihumboldt.hale.common.schema.model.impl.DefaultSchemaSpace;
import eu.esdihumboldt.hale.io.appschema.AppSchemaIO;
import eu.esdihumboldt.hale.io.appschema.impl.internal.generated.app_schema.SourceDataStoresPropertyType.DataStore;
import eu.esdihumboldt.hale.io.appschema.impl.internal.generated.app_schema.SourceDataStoresPropertyType.DataStore.Parameters;
import eu.esdihumboldt.hale.io.appschema.impl.internal.generated.app_schema.SourceDataStoresPropertyType.DataStore.Parameters.Parameter;
@SuppressWarnings("javadoc")
public class AppSchemaFileWriterTest {
private static final ALogger log = ALoggerFactory.getLogger(AppSchemaFileWriterTest.class);
private static final String PROJECT_LOCATION = "/data/landcover.halez";
private static final String MAPPING_SCHEMA = "/data/AppSchemaDataAccess.xsd";
private static DefaultSchemaSpace sourceSchemaSpace;
private static DefaultSchemaSpace targetSchemaSpace;
private static Project project;
private static Alignment alignment;
private static File tempDir;
@BeforeClass
public static void loadTestProject() {
try {
URL archiveLocation = AppSchemaFileWriterTest.class.getResource(PROJECT_LOCATION);
ArchiveProjectReader projectReader = new ArchiveProjectReader();
projectReader.setSource(new DefaultInputSupplier(archiveLocation.toURI()));
IOReport report = projectReader.execute(new LogProgressIndicator());
if (!report.isSuccess()) {
throw new RuntimeException("project reader execution failed");
}
tempDir = projectReader.getTemporaryFiles().iterator().next();
project = projectReader.getProject();
assertNotNull(project);
sourceSchemaSpace = new DefaultSchemaSpace();
targetSchemaSpace = new DefaultSchemaSpace();
// load schemas
List<IOConfiguration> resources = project.getResources();
for (IOConfiguration resource : resources) {
String actionId = resource.getActionId();
String providerId = resource.getProviderId();
// get provider
IOProvider provider = null;
IOProviderDescriptor descriptor = IOProviderExtension.getInstance().getFactory(
providerId);
if (descriptor == null) {
throw new RuntimeException("Could not load I/O provider with ID: "
+ resource.getProviderId());
}
provider = descriptor.createExtensionObject();
provider.loadConfiguration(resource.getProviderConfiguration());
prepareProvider(provider, project, tempDir.toURI());
IOReport providerReport = provider.execute(new LogProgressIndicator());
if (!providerReport.isSuccess()) {
throw new RuntimeException("I/O provider execution failed");
}
// handle results
// TODO: could (should?) be done by an advisor
if (provider instanceof SchemaReader) {
Schema schema = ((SchemaReader) provider).getSchema();
if (actionId.equals(SchemaIO.ACTION_LOAD_SOURCE_SCHEMA)) {
sourceSchemaSpace.addSchema(schema);
}
else if (actionId.equals(SchemaIO.ACTION_LOAD_TARGET_SCHEMA)) {
targetSchemaSpace.addSchema(schema);
}
}
}
// load alignment
List<ProjectFileInfo> projectFiles = project.getProjectFiles();
for (ProjectFileInfo projectFile : projectFiles) {
if (projectFile.getName().equals(AlignmentIO.PROJECT_FILE_ALIGNMENT)) {
AlignmentReader alignReader = new JaxbAlignmentReader();
alignReader.setSource(new DefaultInputSupplier(projectFile.getLocation()));
alignReader.setSourceSchema(sourceSchemaSpace);
alignReader.setTargetSchema(targetSchemaSpace);
alignReader.setPathUpdater(new PathUpdate(null, null));
IOReport alignReport = alignReader.execute(new LogProgressIndicator());
if (!alignReport.isSuccess()) {
throw new RuntimeException("alignment reader execution failed");
}
alignment = alignReader.getAlignment();
assertNotNull(alignment);
break;
}
}
} catch (Exception e) {
log.error("Exception occurred", e);
fail("Test project could not be loaded: " + e.getMessage());
}
}
// adapted from DefaultIOAdvisor and subclasses
private static void prepareProvider(IOProvider provider, ProjectInfo projectInfo,
URI projectLocation) {
if (provider instanceof ProjectInfoAware) {
ProjectInfoAware pia = (ProjectInfoAware) provider;
pia.setProjectInfo(projectInfo);
pia.setProjectLocation(projectLocation);
}
if (provider instanceof InstanceReader) {
InstanceReader ir = (InstanceReader) provider;
ir.setSourceSchema(sourceSchemaSpace);
}
}
@AfterClass
public static void cleanUp() throws IOException {
if (tempDir != null && tempDir.exists()) {
FileUtils.deleteDirectory(tempDir);
}
}
@Test
public void testProject() {
assertNotNull(project);
assertNotNull(alignment);
assertEquals(2, alignment.getTypeCells().size());
}
@Test
public void testWriteMappingFile() throws Exception {
final File targetFile = File.createTempFile(Long.toString(System.currentTimeMillis()),
".xml");
try {
writeAlignment(targetFile, AppSchemaIO.CONTENT_TYPE_MAPPING);
assertTrue(targetFile.exists());
assertTrue(targetFile.length() > 0);
assertTrue(isMappingValid(targetFile));
} finally {
if (targetFile != null)
targetFile.delete();
}
}
@Test
public void testWriteArchive() throws Exception {
final File targetFile = File.createTempFile(Long.toString(System.currentTimeMillis()),
".zip");
try {
writeAlignment(targetFile, AppSchemaIO.CONTENT_TYPE_ARCHIVE);
assertTrue(targetFile.exists());
assertTrue(targetFile.length() > 0);
checkArchive(targetFile);
} finally {
if (targetFile != null)
targetFile.delete();
}
}
private void writeAlignment(File targetFile, String contentType) throws IOException,
IOProviderConfigurationException {
AbstractAppSchemaConfigurator alignWriter = new AppSchemaMappingFileWriter();
prepareProvider(alignWriter, project, tempDir.toURI());
alignWriter.setAlignment(alignment);
alignWriter.setSourceSchema(sourceSchemaSpace);
alignWriter.setTargetSchema(targetSchemaSpace);
alignWriter.setTarget(new FileIOSupplier(targetFile));
DataStore dataStoreParam = createDataStoreParam();
alignWriter.setParameter(AppSchemaIO.PARAM_DATASTORE, new ComplexValue(dataStoreParam));
alignWriter
.setContentType(HalePlatform.getContentTypeManager().getContentType(contentType));
IOReport report = alignWriter.execute(new LogProgressIndicator());
assertNotNull(report);
assertTrue(report.isSuccess());
}
private DataStore createDataStoreParam() {
DataStore dataStoreParam = new DataStore();
dataStoreParam.setParameters(new Parameters());
Parameter hostParam = new Parameter();
hostParam.setName("host");
hostParam.setValue("localhost");
Parameter dbParam = new Parameter();
dbParam.setName("database");
dbParam.setValue("postgres");
Parameter userParam = new Parameter();
userParam.setName("user");
userParam.setValue("postgres");
Parameter passwdParam = new Parameter();
passwdParam.setName("passwd");
passwdParam.setValue("postgres");
Parameter dbtypeParam = new Parameter();
dbtypeParam.setName("dbtype");
dbtypeParam.setValue("postgis");
dataStoreParam.getParameters().getParameter()
.addAll(Arrays.asList(hostParam, dbParam, dbtypeParam, userParam, passwdParam));
return dataStoreParam;
}
private boolean isMappingValid(File mappingFile) throws IOException {
URL mappingSchema = getClass().getResource(MAPPING_SCHEMA);
Source xmlFile = new StreamSource(mappingFile);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
javax.xml.validation.Schema schema = null;
try {
schema = schemaFactory.newSchema(mappingSchema);
} catch (SAXException e) {
fail("Exception parsing mapping schema: " + e.getMessage());
}
@SuppressWarnings("null")
Validator validator = schema.newValidator();
try {
validator.validate(xmlFile);
return true;
} catch (SAXException e) {
log.error("Mapping file validation failed", e);
return false;
}
}
private void checkArchive(File archive) throws IOException {
final File tempFile = File
.createTempFile(Long.toString(System.currentTimeMillis()), ".xml");
ZipInputStream zis = null;
Document doc = null;
try {
zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(archive)));
ZipEntry lcvWsDir = zis.getNextEntry();
checkDirEntry(lcvWsDir, "lcv/");
zis.closeEntry();
ZipEntry lcvWsFile = zis.getNextEntry();
checkFileEntry(lcvWsFile, "lcv/" + AppSchemaIO.WORKSPACE_FILE);
doc = readDocument(zis);
checkWorkspaceDocument(doc, "lcv_workspace", "lcv");
zis.closeEntry();
ZipEntry lcvNsFile = zis.getNextEntry();
checkFileEntry(lcvNsFile, "lcv/" + AppSchemaIO.NAMESPACE_FILE);
doc = readDocument(zis);
checkNamespaceDocument(doc, "lcv_namespace", "lcv",
"http://inspire.ec.europa.eu/schemas/lcv/3.0");
zis.closeEntry();
ZipEntry lcvDataStoreDir = zis.getNextEntry();
checkDirEntry(lcvDataStoreDir, "lcv/LandCoverVector/");
zis.closeEntry();
ZipEntry lcvDataStoreFile = zis.getNextEntry();
checkFileEntry(lcvDataStoreFile, "lcv/LandCoverVector/" + AppSchemaIO.DATASTORE_FILE);
doc = readDocument(zis);
assertNotNull(doc);
assertEquals("LandCoverVector_datastore",
getFirstElementByTagName(doc.getDocumentElement(), "id").getTextContent());
assertEquals("LandCoverVector",
getFirstElementByTagName(doc.getDocumentElement(), "name").getTextContent());
Element wsEl = getFirstElementByTagName(doc.getDocumentElement(), "workspace");
assertNotNull(wsEl);
assertEquals("lcv_workspace", getFirstElementByTagName(wsEl, "id").getTextContent());
NodeList paramEntries = doc.getDocumentElement().getElementsByTagName("entry");
assertNotNull(paramEntries);
assertEquals(3, paramEntries.getLength());
for (int i = 0; i < paramEntries.getLength(); i++) {
Node param = paramEntries.item(i);
Node key = param.getAttributes().getNamedItem("key");
if ("dbtype".equals(key.getTextContent())) {
assertEquals("app-schema", param.getTextContent());
}
else if ("namespace".equals(key.getTextContent())) {
assertEquals("http://inspire.ec.europa.eu/schemas/lcv/3.0",
param.getTextContent());
}
else if ("url".equals(key.getTextContent())) {
assertEquals("file:./workspaces/lcv/LandCoverVector/LandCoverVector.xml",
param.getTextContent());
}
else {
fail("Unknown connection parameter found: " + key.getTextContent());
}
}
zis.closeEntry();
ZipEntry mappingFile = zis.getNextEntry();
checkFileEntry(mappingFile, "lcv/LandCoverVector/LandCoverVector.xml");
ByteStreams.copy(zis, new FileOutputStream(tempFile));
assertTrue(isMappingValid(tempFile));
zis.closeEntry();
ZipEntry unitFtDir = zis.getNextEntry();
checkDirEntry(unitFtDir, "lcv/LandCoverVector/LandCoverUnit/");
zis.closeEntry();
ZipEntry unitFtFile = zis.getNextEntry();
checkFileEntry(unitFtFile, "lcv/LandCoverVector/LandCoverUnit/"
+ AppSchemaIO.FEATURETYPE_FILE);
doc = readDocument(zis);
checkFeatureTypeDocument(doc, "LandCoverUnit");
zis.closeEntry();
ZipEntry unitLayerFile = zis.getNextEntry();
checkFileEntry(unitLayerFile, "lcv/LandCoverVector/LandCoverUnit/"
+ AppSchemaIO.LAYER_FILE);
doc = readDocument(zis);
checkLayerDocument(doc, "LandCoverUnit");
zis.closeEntry();
ZipEntry datasetFtDir = zis.getNextEntry();
checkDirEntry(datasetFtDir, "lcv/LandCoverVector/LandCoverDataset/");
zis.closeEntry();
ZipEntry datasetFtFile = zis.getNextEntry();
checkFileEntry(datasetFtFile, "lcv/LandCoverVector/LandCoverDataset/"
+ AppSchemaIO.FEATURETYPE_FILE);
doc = readDocument(zis);
checkFeatureTypeDocument(doc, "LandCoverDataset");
zis.closeEntry();
ZipEntry datasetLayerFile = zis.getNextEntry();
checkFileEntry(datasetLayerFile, "lcv/LandCoverVector/LandCoverDataset/"
+ AppSchemaIO.LAYER_FILE);
doc = readDocument(zis);
checkLayerDocument(doc, "LandCoverDataset");
zis.closeEntry();
ZipEntry baseWsDir = zis.getNextEntry();
checkDirEntry(baseWsDir, "base/");
zis.closeEntry();
ZipEntry baseWsFile = zis.getNextEntry();
checkFileEntry(baseWsFile, "base/" + AppSchemaIO.WORKSPACE_FILE);
doc = readDocument(zis);
checkWorkspaceDocument(doc, "base_workspace", "base");
zis.closeEntry();
ZipEntry baseNsFile = zis.getNextEntry();
checkFileEntry(baseNsFile, "base/" + AppSchemaIO.NAMESPACE_FILE);
doc = readDocument(zis);
checkNamespaceDocument(doc, "base_namespace", "base",
"http://inspire.ec.europa.eu/schemas/base/3.3");
zis.closeEntry();
ZipEntry gmlWsDir = zis.getNextEntry();
checkDirEntry(gmlWsDir, "gml/");
zis.closeEntry();
ZipEntry gmlWsFile = zis.getNextEntry();
checkFileEntry(gmlWsFile, "gml/" + AppSchemaIO.WORKSPACE_FILE);
doc = readDocument(zis);
checkWorkspaceDocument(doc, "gml_workspace", "gml");
zis.closeEntry();
ZipEntry gmlNsFile = zis.getNextEntry();
checkFileEntry(gmlNsFile, "gml/" + AppSchemaIO.NAMESPACE_FILE);
doc = readDocument(zis);
checkNamespaceDocument(doc, "gml_namespace", "gml", "http://www.opengis.net/gml/3.2");
zis.closeEntry();
ZipEntry xlinkWsDir = zis.getNextEntry();
checkDirEntry(xlinkWsDir, "xlink/");
zis.closeEntry();
ZipEntry xlinkWsFile = zis.getNextEntry();
checkFileEntry(xlinkWsFile, "xlink/" + AppSchemaIO.WORKSPACE_FILE);
doc = readDocument(zis);
checkWorkspaceDocument(doc, "xlink_workspace", "xlink");
zis.closeEntry();
ZipEntry xlinkNsFile = zis.getNextEntry();
checkFileEntry(xlinkNsFile, "xlink/" + AppSchemaIO.NAMESPACE_FILE);
doc = readDocument(zis);
checkNamespaceDocument(doc, "xlink_namespace", "xlink", "http://www.w3.org/1999/xlink");
zis.closeEntry();
ZipEntry xsiWsDir = zis.getNextEntry();
checkDirEntry(xsiWsDir, "xsi/");
zis.closeEntry();
ZipEntry xsiWsFile = zis.getNextEntry();
checkFileEntry(xsiWsFile, "xsi/" + AppSchemaIO.WORKSPACE_FILE);
doc = readDocument(zis);
checkWorkspaceDocument(doc, "xsi_workspace", "xsi");
zis.closeEntry();
ZipEntry xsiNsFile = zis.getNextEntry();
checkFileEntry(xsiNsFile, "xsi/" + AppSchemaIO.NAMESPACE_FILE);
doc = readDocument(zis);
checkNamespaceDocument(doc, "xsi_namespace", "xsi", AppSchemaMappingUtils.XSI_URI);
zis.closeEntry();
assertNull(zis.getNextEntry());
} catch (ZipException e) {
fail("Exception reading generated ZIP archive: " + e.getMessage());
} finally {
if (tempFile != null)
tempFile.delete();
if (zis != null)
zis.close();
}
}
private void checkDirEntry(ZipEntry entry, final String dirName) {
assertNotNull(entry);
assertEquals(dirName, entry.getName());
assertTrue(entry.isDirectory());
}
private void checkFileEntry(ZipEntry entry, String fileName) {
assertNotNull(entry);
assertEquals(fileName, entry.getName());
assertFalse(entry.isDirectory());
}
private Document readDocument(InputStream is) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ByteStreams.copy(is, bos);
DocumentBuilderFactory dbFac = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder docBuilder = dbFac.newDocumentBuilder();
return docBuilder.parse(new ByteArrayInputStream(bos.toByteArray()));
} catch (Exception e) {
throw new IOException(e);
}
}
private void checkWorkspaceDocument(Document doc, final String id, final String name) {
assertNotNull(doc);
assertEquals("workspace", doc.getDocumentElement().getNodeName());
Element idEl = getFirstElementByTagName(doc.getDocumentElement(), "id");
assertNotNull(idEl);
assertEquals(id, idEl.getTextContent());
Element nameEl = getFirstElementByTagName(doc.getDocumentElement(), "name");
assertNotNull(nameEl);
assertEquals(name, nameEl.getTextContent());
}
private void checkNamespaceDocument(Document doc, final String id, final String prefix,
final String uri) {
assertNotNull(doc);
assertEquals("namespace", doc.getDocumentElement().getNodeName());
Element idEl = getFirstElementByTagName(doc.getDocumentElement(), "id");
assertNotNull(idEl);
assertEquals(id, idEl.getTextContent());
Element prefixEl = getFirstElementByTagName(doc.getDocumentElement(), "prefix");
assertNotNull(prefixEl);
assertEquals(prefix, prefixEl.getTextContent());
Element uriEl = getFirstElementByTagName(doc.getDocumentElement(), "uri");
assertNotNull(uriEl);
assertEquals(uri, uriEl.getTextContent());
}
private void checkFeatureTypeDocument(Document doc, final String featureTypeName) {
assertNotNull(doc);
assertEquals(featureTypeName + "_featureType",
getFirstElementByTagName(doc.getDocumentElement(), "id").getTextContent());
assertEquals(featureTypeName, getFirstElementByTagName(doc.getDocumentElement(), "name")
.getTextContent());
assertEquals(featureTypeName,
getFirstElementByTagName(doc.getDocumentElement(), "nativeName").getTextContent());
assertEquals(featureTypeName, getFirstElementByTagName(doc.getDocumentElement(), "title")
.getTextContent());
assertEquals(featureTypeName,
getFirstElementByTagName(doc.getDocumentElement(), "abstract").getTextContent());
Element nsEl = getFirstElementByTagName(doc.getDocumentElement(), "namespace");
assertNotNull(nsEl);
assertEquals("lcv_namespace", getFirstElementByTagName(nsEl, "id").getTextContent());
Element storeEl = getFirstElementByTagName(doc.getDocumentElement(), "store");
assertNotNull(storeEl);
assertEquals("LandCoverVector_datastore", getFirstElementByTagName(storeEl, "id")
.getTextContent());
}
private void checkLayerDocument(Document doc, final String featureTypeName) {
assertNotNull(doc);
assertEquals(featureTypeName + "_layer",
getFirstElementByTagName(doc.getDocumentElement(), "id").getTextContent());
assertEquals(featureTypeName, getFirstElementByTagName(doc.getDocumentElement(), "name")
.getTextContent());
Element resourceEl = getFirstElementByTagName(doc.getDocumentElement(), "resource");
assertNotNull(resourceEl);
assertEquals(featureTypeName + "_featureType", getFirstElementByTagName(resourceEl, "id")
.getTextContent());
}
}