/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openengsb.core.services.internal; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.openengsb.core.api.LinkingSupport; import org.openengsb.core.api.model.ModelDescription; import org.openengsb.core.api.model.ModelWrapper; import org.openengsb.core.api.model.OpenEngSBModel; import org.openengsb.core.api.model.QueryRequest; import org.openengsb.core.api.xlink.exceptions.DomainNotLinkableException; import org.openengsb.core.api.xlink.model.ModelViewMapping; import org.openengsb.core.api.xlink.model.XLinkConnectorRegistration; import org.openengsb.core.api.xlink.model.XLinkConnectorView; import org.openengsb.core.api.xlink.model.XLinkConstants; import org.openengsb.core.api.xlink.model.XLinkObject; import org.openengsb.core.api.xlink.service.XLinkConnectorManager; import org.openengsb.core.ekb.api.QueryInterface; import org.openengsb.core.ekb.api.TransformationEngine; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public class XLinkConnectorManagerImpl extends ConnectorManagerImpl implements XLinkConnectorManager { private static Logger log = LoggerFactory.getLogger(XLinkConnectorManagerImpl.class); private final Map<String, XLinkConnectorRegistration> xLinkRegistrations = new HashMap<>(); private String xLinkBaseUrl; private TransformationEngine transformationEngine; private QueryInterface queryService; @Override public void registerWithXLink(String connectorId, String remoteHostId, String toolName, ModelViewMapping... modelViewMappings) { // TODO: check that connector exists // TODO: overwrite delete connector s.t. it also removes xlink registration // if (!connectorExists(connectorId)) { // throw new IllegalArgumentException("unknown connector id"); // } Map<ModelDescription, XLinkConnectorView[]> modelViewsMap = convertToMapWithModelDescriptionAsKey(modelViewMappings); synchronized (xLinkRegistrations) { xLinkRegistrations.put(connectorId, new XLinkConnectorRegistration(remoteHostId, connectorId, toolName, modelViewsMap)); } } @Override public void unregisterFromXLink(String connectorId) { synchronized (xLinkRegistrations) { xLinkRegistrations.remove(connectorId); } } @Override public List<XLinkConnectorRegistration> getXLinkRegistrations(String hostId) { List<XLinkConnectorRegistration> registrationsOnHost = new ArrayList<>(); synchronized (xLinkRegistrations) { for (XLinkConnectorRegistration registration : xLinkRegistrations.values()) { if (registration.getHostId().equals(hostId)) { registrationsOnHost.add(registration); } } } return registrationsOnHost; } @Override public String requestXLinkSwitch(String connectorId, String context, Object modelObject, boolean hostOnly) { XLinkConnectorRegistration registration = xLinkRegistrations.get(connectorId); Collection<XLinkConnectorRegistration> registrations = hostOnly ? getXLinkRegistrations(registration.getHostId()) : xLinkRegistrations.values(); ModelDescription modelDescription = ModelWrapper.wrap(modelObject).getModelDescription(); List<XLinkObject> xLinkObjects = new ArrayList<>(); for (XLinkConnectorRegistration r : registrations) { xLinkObjects.addAll(collectXLinkObjects(modelObject, modelDescription, r)); } if (!xLinkObjects.isEmpty()) { Object connector = getUtilsService().getService("(service.pid=" + connectorId + ")", 100L); if (connector == null) { throw new IllegalStateException("requestor connector not there"); } try { LinkingSupport service = (LinkingSupport) connector; service.showXLinks(xLinkObjects.toArray(new XLinkObject[xLinkObjects.size()])); } catch (ClassCastException e) { throw new DomainNotLinkableException(); } } return generateXLink(connectorId, context, modelObject); } @Override public void openXLink(String connectorId, ModelDescription modelDescription, Object modelObject, XLinkConnectorView view) { String filter = "(" + Constants.SERVICE_PID + "=" + connectorId + ")"; LinkingSupport connector = (LinkingSupport) getUtilsService().getService(filter); connector.openXLink(modelDescription, modelObject, view); } private List<XLinkObject> collectXLinkObjects(Object modelObject, ModelDescription modelDescription, XLinkConnectorRegistration registration) { List<XLinkObject> xLinkObjects = new ArrayList<>(); for (Entry<ModelDescription, XLinkConnectorView[]> entry : registration.getModelsToViews().entrySet()) { if (modelDescription.equals(entry.getKey())) { xLinkObjects.add(new XLinkObject(registration.getConnectorId(), registration.getToolName(), modelObject, modelDescription, Arrays.asList(entry.getValue()))); } else if (transformationEngine.isTransformationPossible(modelDescription, entry.getKey())) { Object transformedObject = transformAndMerge(modelDescription, entry.getKey(), modelObject); xLinkObjects.add(new XLinkObject(registration.getConnectorId(), registration.getToolName(), transformedObject, entry.getKey(), Arrays.asList(entry.getValue()))); } } return xLinkObjects; } private Object transformAndMerge(ModelDescription sourceModel, ModelDescription targetModel, Object modelObject) { Object transformedObject = transformationEngine.performTransformation(sourceModel, targetModel, modelObject); if (transformedObject instanceof OpenEngSBModel && ((OpenEngSBModel) transformedObject).retrieveInternalModelId() != null) { List<?> result = getQueryInterface().query(transformedObject.getClass(), QueryRequest.query(((OpenEngSBModel) transformedObject).retrieveInternalModelIdName(), ((OpenEngSBModel) transformedObject).retrieveInternalModelId())); if (!result.isEmpty()) { transformedObject = transformationEngine.performTransformation(sourceModel, targetModel, modelObject, result.get(0)); } } return transformedObject; } private QueryInterface getQueryInterface() { if (queryService == null) { BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext(); ServiceReference<QueryInterface> serviceReference = bundleContext.getServiceReference(QueryInterface.class); if (serviceReference != null) { queryService = bundleContext.getService(serviceReference); } } return queryService; } @Override public String generateXLink(String connectorId, String context, Object modelObject) { StringBuilder sb = new StringBuilder(xLinkBaseUrl); appendXLinkProperty(sb, '?', XLinkConstants.XLINK_CONTEXTID_KEY, context); ModelDescription modelDescription = ModelWrapper.wrap(modelObject).getModelDescription(); appendXLinkProperty(sb, '&', XLinkConstants.XLINK_MODELCLASS_KEY, modelDescription.getModelClassName()); appendXLinkProperty(sb, '&', XLinkConstants.XLINK_VERSION_KEY, modelDescription.getVersionString()); ObjectMapper mapper = new ObjectMapper(); String objectString = null; try { objectString = mapper.writeValueAsString(modelObject); } catch (JsonProcessingException e) { log.warn("Could not create xLink values.", e); } if (objectString != null) { appendXLinkProperty(sb, '&', XLinkConstants.XLINK_IDENTIFIER_KEY, urlEncodeParameter(objectString)); } return sb.toString(); } private StringBuilder appendXLinkProperty(StringBuilder sb, char separator, String propertyName, String propertyValue) { sb.append(separator); sb.append(propertyName); sb.append('='); sb.append(propertyValue); return sb; } private Map<ModelDescription, XLinkConnectorView[]> convertToMapWithModelDescriptionAsKey( ModelViewMapping[] modelViewMappings) { Map<ModelDescription, XLinkConnectorView[]> convertedMap = new HashMap<>(); for (ModelViewMapping mapping : modelViewMappings) { convertedMap.put(mapping.getDescription(), mapping.getViews()); } return convertedMap; } /** * URLEncodes the given Parameter in UTF-8 */ private static String urlEncodeParameter(String parameter) { try { return URLEncoder.encode(parameter, "UTF-8"); } catch (UnsupportedEncodingException ex) { log.warn("Could not encode parameter.", ex); } return parameter; } public String getxLinkBaseUrl() { return xLinkBaseUrl; } public void setxLinkBaseUrl(String xLinkBaseUrl) { this.xLinkBaseUrl = xLinkBaseUrl; } public void setTransformationEngine( TransformationEngine transformationEngine) { this.transformationEngine = transformationEngine; } }