blob: 0310632c9c047bd578c813f9ea4108641d632890 [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.api.odsadapter;
import java.util.HashMap;
import java.util.Map;
import org.asam.ods.AoException;
import org.asam.ods.AoFactory;
import org.asam.ods.AoFactoryHelper;
import org.asam.ods.AoSession;
import org.eclipse.mdm.api.base.ConnectionException;
import org.eclipse.mdm.api.dflt.ApplicationContext;
import org.eclipse.mdm.api.dflt.ApplicationContextFactory;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.InvalidName;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.highqsoft.corbafileserver.generated.CORBAFileServerIF;
import com.highqsoft.corbafileserver.generated.CORBAFileServerIFHelper;
/**
* ASAM ODS implementation of the {@link ApplicationContextFactory} interface.
*
* @since 1.0.0
* @author Viktor Stoehr, Gigatronik Ingolstadt GmbH
*/
public class ODSContextFactory implements ApplicationContextFactory {
public static final String PARAM_NAMESERVICE = "nameservice";
public static final String PARAM_SERVICENAME = "servicename";
public static final String PARAM_USER = "user";
public static final String PARAM_PASSWORD = "password";
private static final String PARAM_FOR_USER = "for_user";
private static final Logger LOGGER = LoggerFactory.getLogger(ODSContextFactory.class);
private static final ORB orb = ORB.init(new String[] {}, System.getProperties());
public ODSContextFactory() {
}
/**
* {@inheritDoc}
*
* <p>
* <b>Note:</b> Given parameters {@code Map} must contain values for each of the
* following keys:
*
* <ul>
* <li>{@value #PARAM_NAMESERVICE}</li>
* <li>{@value #PARAM_SERVICENAME}</li>
* <li>{@value #PARAM_USER}</li>
* <li>{@value #PARAM_PASSWORD}</li>
* </ul>
*
* Listed names are available via public fields of this class.
*/
@Override
public ApplicationContext connect(Map<String, String> parameters) throws ConnectionException {
AoSession aoSession = null;
try (ServiceLocator serviceLocator = new ServiceLocator(orb, getParameter(parameters, PARAM_NAMESERVICE))) {
String nameOfService = getParameter(parameters, PARAM_SERVICENAME).replace(".ASAM-ODS", "");
AoFactory aoFactory = serviceLocator.resolveFactory(nameOfService);
String aoFactoryName = aoFactory.getName();
LOGGER.info("Connecting to ODS Server (name: {}, description: {}, interface version: {}, type: {}) ...",
aoFactoryName, aoFactory.getDescription(), aoFactory.getInterfaceVersion(), aoFactory.getType());
// Create a parameters map without password (which should not be visible from
// this point onwards),
// leaving the original map untouched:
Map<String, String> parametersWithoutPassword = new HashMap<>(parameters);
if (LOGGER.isDebugEnabled()) {
parametersWithoutPassword.put(PARAM_PASSWORD, "****");
LOGGER.debug("Connecting to ODS using the connection parameters: {}",
sessionParametersAsString(parametersWithoutPassword));
}
parametersWithoutPassword.remove(PARAM_PASSWORD);
aoSession = aoFactory.newSession(sessionParametersAsString(parameters));
LOGGER.info("Connection to ODS server '{}' established.", aoFactoryName);
CORBAFileServerIF fileServer = serviceLocator.resolveFileServer(nameOfService);
return new ODSContext(orb, aoSession, fileServer, parametersWithoutPassword);
} catch (AoException e) {
closeSession(aoSession);
throw new ConnectionException("Unable to connect to ODS server due to: " + e.reason, e);
}
}
private String sessionParametersAsString(Map<String, String> parameters) throws ConnectionException {
ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder()
.put("USER", getParameter(parameters, PARAM_USER))
.put("PASSWORD", getParameter(parameters, PARAM_PASSWORD)).put("CREATE_COSESSION_ALLOWED", "TRUE");
String forUserName = parameters.get(PARAM_FOR_USER);
if (!Strings.isNullOrEmpty(forUserName)) {
builder.put("FOR_USER", forUserName);
}
return Joiner.on(",").withKeyValueSeparator("=").join(builder.build());
}
/**
* Closes given {@link AoSession} with catching and logging errors.
*
* @param aoSession The {@code AoSession} that shall be closed.
*/
private static void closeSession(AoSession aoSession) {
if (aoSession == null) {
return;
}
try {
aoSession.close();
} catch (AoException e) {
LOGGER.warn("Unable to close sesssion due to: " + e.reason, e);
}
}
/**
* Reads the property identified by given property name.
*
* @param parameters The properties {@code Map}.
* @param name The property name.
* @return The property value is returned.
* @throws ConnectionException Thrown if property does not exist or is empty.
*/
private static String getParameter(Map<String, String> parameters, String name) throws ConnectionException {
String value = parameters.get(name);
if (value == null || value.isEmpty()) {
throw new ConnectionException("Connection parameter with name '" + name + "' is either missing or empty.");
}
return value;
}
/**
* Used to resolve CORBA service object by ID and kind.
*/
private static final class ServiceLocator implements AutoCloseable {
private NamingContextExt namingContext;
/**
* Constructor.
*
* @param orb The {@link ORB} singleton instance.
* @param path The naming context path.
* @throws ConnectionException Thrown if unable to resolve the naming context.
*/
public ServiceLocator(ORB orb, String path) throws ConnectionException {
namingContext = NamingContextExtHelper.narrow(orb.string_to_object(path));
if (namingContext == null) {
throw new ConnectionException("Unable to resolve NameService '" + path + "'.");
}
}
/**
* Resolves and returns the {@link AoFactory} service for given ID.
*
* @param id Used as identifier.
* @return The {@code AoFactory} is returned.
* @throws ConnectionException Thrown if unable to resolve the {@code
* AoFactory} .
*/
public AoFactory resolveFactory(String id) throws ConnectionException {
return AoFactoryHelper.narrow(resolve(id, "ASAM-ODS"));
}
/**
* Resolves and returns the {@link CORBAFileServerIF} service for given ID.
*
* @param id Used as identifier.
* @return The {@code CORBAFileServerIF} or null, if none found, is returned.
*/
public CORBAFileServerIF resolveFileServer(String id) {
try {
return CORBAFileServerIFHelper.narrow(resolve(id, "CORBA-FT"));
} catch (ConnectionException e) {
LOGGER.warn(e.getMessage());
return null;
}
}
/**
* Resolves a CORBA service object for given id and kind.
*
* @param id Used as identifier.
* @param kind Used as qualifier.
* @return The resolved CORBA service object is returned.
* @throws ConnectionException Thrown in case of errors.
*/
public Object resolve(String id, String kind) throws ConnectionException {
try {
return namingContext.resolve(new NameComponent[] { new NameComponent(id, kind) });
} catch (NotFound | CannotProceed | InvalidName e) {
throw new ConnectionException("Unable to resolve service '" + id + "." + kind + "'.", e);
}
}
@Override
public void close() throws ConnectionException {
namingContext._release();
}
}
}