blob: f090501703ba9aefd12e63cdfcefb7c283c53d87 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.apicopy.boundary;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.eclipse.mdm.api.base.ConnectionException;
import org.eclipse.mdm.api.base.ServiceNotProvidedException;
import org.eclipse.mdm.api.base.model.Channel;
import org.eclipse.mdm.api.base.model.ChannelGroup;
import org.eclipse.mdm.api.base.model.Entity;
import org.eclipse.mdm.api.base.model.Measurement;
import org.eclipse.mdm.api.base.model.Test;
import org.eclipse.mdm.api.base.model.TestStep;
import org.eclipse.mdm.api.base.query.DataAccessException;
import org.eclipse.mdm.api.dflt.ApplicationContext;
import org.eclipse.mdm.api.dflt.ApplicationContextFactory;
import org.eclipse.mdm.api.dflt.EntityManager;
import org.eclipse.mdm.api.dflt.model.Pool;
import org.eclipse.mdm.api.dflt.model.Project;
import org.eclipse.mdm.apicopy.control.ApiCopyException;
import org.eclipse.mdm.apicopy.control.ExportTask;
import org.eclipse.mdm.connector.boundary.ConnectorService;
import org.eclipse.mdm.shoppingbasket.entity.MDMItem;
import org.eclipse.mdm.shoppingbasket.entity.ShoppingBasket;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
/**
* Service for importing an ATFX file into a MDM datasource.
*
*/
@Stateless
public class ExportService {
// TODO make temp path configurable
private static final Path TMP = Paths.get(System.getProperty("java.io.tmpdir"));
@Inject
private ConnectorService connectorService;
private final String atfxContextFactoryClassname = "org.eclipse.mdm.api.atfxadapter.ATFXContextFactory";
/**
* Default public constructor
*/
public ExportService() {
}
/**
* Imports an ATFX file into the provided datasource.
*
* @param atfxFileSupplier returns a {@link Path} to an ATFX file. Accompanying
* binary files are expected to be on the same folder.
* @return
*/
public Path exportAtfx(ShoppingBasket basket) {
try {
Path tmpDir = Files.createTempDirectory(TMP, "atfxexport");
Path atfxFile = tmpDir.resolve("export.atfx");
writeToFile(atfxFile, ExportTask.class.getResourceAsStream("/emptyAtfx.xml"));
Map<String, String> params = ImmutableMap.of("atfxfile", atfxFile.toFile().getAbsolutePath(),
"freetext.active", "false", "includeCatalog", "true");
ApplicationContext contextDst = connectContext(atfxContextFactoryClassname, params);
Map<String, List<MDMItem>> map = basket.getItems().stream().map(i -> i.getRestURI().getPath())
.map(p -> split(p)).collect(Collectors.groupingBy(p -> p.getSource()));
if (map.size() != 1) {
throw new ApiCopyException("Currently only exports from exactly one application context are allowed!");
}
String srcContextName = map.keySet().iterator().next();
ApplicationContext contextSrc = connectorService.getContextByName(srcContextName);
List<? extends Entity> entities = loadEntities(contextSrc, basket);
/*
* Copy the catalog (which involves creating the ContextComponts).
*/
ExportTask task = new ExportTask(contextSrc, contextDst);
task.copyCatalog();
contextDst.close();
/*
* After creating the ContextComponents, the application model has to be
* reloaded. This is normally done in a co-session, but openATFX does not
* support co-sessions. Thus we have to create a new session by creating a new
* application context.
*/
contextDst = connectContext(atfxContextFactoryClassname, params);
task = new ExportTask(contextSrc, contextDst);
task.copy(entities);
contextDst.close();
return tmpDir;
} catch (ServiceNotProvidedException e) {
throw new ApiCopyException("Could not retrive all required services from ATFX application context!", e);
} catch (ConnectionException e) {
throw new ApiCopyException("Could not create ATFX application context!", e);
} catch (IOException e) {
throw new ApiCopyException("Cold not create temporary directory!", e);
}
}
private List<? extends Entity> loadEntities(ApplicationContext contextSrc, ShoppingBasket basket)
throws ServiceNotProvidedException, DataAccessException {
EntityManager em = contextSrc.getEntityManager()
.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class));
return basket.getItems().stream().map(i -> i.getRestURI().getPath()).map(p -> split(p))
.map(i -> em.load(typeToClass(i.getType()), i.getId())).collect(Collectors.toList());
}
private Class<? extends Entity> typeToClass(String s) {
return ENTITY2FRAGMENT_URI.inverse().get(s);
}
private static final BiMap<Class<? extends Entity>, String> ENTITY2FRAGMENT_URI = ImmutableBiMap
.<Class<? extends Entity>, String>builder().put(Project.class, "projects").put(Pool.class, "pools")
.put(Test.class, "tests").put(TestStep.class, "teststeps").put(Measurement.class, "measurements")
.put(ChannelGroup.class, "channelgroups").put(Channel.class, "channels").build();
private MDMItem split(String path) {
String[] splitted = path.split("/");
MDMItem item = new MDMItem();
item.setId(splitted[splitted.length - 1]);
item.setType(splitted[splitted.length - 2]);
item.setSource(splitted[splitted.length - 3]);
return item;
}
/**
* Connects to a {@link ApplicationContext}.
*
* @param contextFactoryClassname classname of the
* {@link ApplicationContextFactory}
* @param parameters connection parameters
* @return connected {@link ApplicationContext}
* @throws ConnectionException
*/
private ApplicationContext connectContext(String contextFactoryClassname, Map<String, String> parameters)
throws ConnectionException {
try {
Class<? extends ApplicationContextFactory> contextFactoryClass = Thread.currentThread()
.getContextClassLoader().loadClass(contextFactoryClassname)
.asSubclass(ApplicationContextFactory.class);
ApplicationContextFactory contextFactory = contextFactoryClass.newInstance();
return contextFactory.connect(parameters);
} catch (Exception e) {
throw new ConnectionException(
"failed to initialize entity manager using factory '" + contextFactoryClassname + "'", e);
}
}
private static void writeToFile(Path resolve, InputStream entityAs) throws IOException {
try (OutputStream out = new FileOutputStream(resolve.toFile())) {
int read = 0;
byte[] bytes = new byte[1024];
while ((read = entityAs.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
}
}
}