package eu.aniketos.serviceruntime.remote;
import java.util.Dictionary;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.aniketos.data.ICompositionPlan;
import eu.aniketos.data.IConsumerPolicy;
import eu.aniketos.marketplace.IMarketplace;
import eu.aniketos.marketplace.MarketplaceAnnouncement;
import eu.aniketos.marketplace.MarketplaceSearchParams;
import eu.aniketos.marketplace.ServiceDescriptor;
import eu.aniketos.marketplace.ServiceOperation;
import eu.aniketos.scpm.ICompositionPlanner;
import eu.aniketos.serviceruntime.bpmn.BpmnUtils;
import eu.aniketos.serviceruntime.data.DataAdapter;
import eu.aniketos.serviceruntime.data.entities.ServiceTask;
import eu.aniketos.serviceruntime.eventlistener.AlertMessage;
import eu.aniketos.serviceruntime.eventlistener.IEventListener;
import eu.aniketos.serviceruntime.notification.NotificationImpl;
import eu.aniketos.serviceruntime.remote.CompositeServiceFactory.Config;
import eu.aniketos.serviceruntime.remote.activiti.ActivitiClient;
import eu.aniketos.serviceruntime.remote.activiti.models.ProcessDefinitionData;
import eu.aniketos.serviceruntime.rules.ActionType;
import eu.aniketos.serviceruntime.rules.Rule;
import eu.aniketos.serviceruntime.rules.RulesParser;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Implementation of an IRemoteImpl
* @author Kostas Giannakakis
*
*/
public class RuntimeRemoteImpl implements IRuntimeRemote, IEventListener {
/** Logger */
private static Logger logger = LoggerFactory.getLogger(RuntimeRemoteImpl.class);
/** Composite Service Factory configuration */
private Config config;
/** Composite Service Factory */
private CompositeServiceFactory compositeServiceFactory;
/** Activiti Client */
ActivitiClient activitiClient;
/** Marketplace service */
public IMarketplace marketplace;
/** Notification service */
private NotificationImpl notification;
/** DataAdapter service */
DataAdapter dataAdapter;
/** ScpmClient service */
private ICompositionPlanner scpmClient;
private ScpmActions scpmActions;
/**
* Activate method called by Declarative Services when the RemoteImpl is initialized
* @param context The context of the service
*/
protected void activate(ComponentContext context) {
logger.debug("*** Activating RemoteImpl");
notification = new NotificationImpl();
notification.activate(context);
notification.setEventListener(this);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/bingmap/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/googlemap/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/hotelbooking/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/hotelinfo/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/poi/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/webpage/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/mail/service?wsdl", null);
notification.subscribe("http://demo-aniketoswp6.rhcloud.com/smsService/service?wsdl", null);
notification.subscribe("http://hestia.atc.gr/an_00253_bookingHotelCloser/service?wsdl", null);
notification.subscribe("http://83.235.133.36/AniketosAlternateWS/MapsServiceSoapHttpPort?wsdl", null);
notification.subscribe("http://83.235.133.36/AniketosWS/MapsServiceSoapHttpPort?wsdl", null);
notification.subscribe("http://services-aniketoswp7.rhcloud.com/poiMap/service?wsdl", null);
notification.subscribe("http://services-aniketoswp7.rhcloud.com/poiAltMap/service?wsdl", null);
configure(context);
}
/**
* Configures the Config structure from contect properties
* @param context The context of the service
*/
private void configure(ComponentContext context) {
@SuppressWarnings({ "rawtypes" })
Dictionary properties = context.getProperties();
config = new Config();
config.compositionsDir = properties.get("compositions.dir").toString();
config.activitiAddress = properties.get("activiti.address").toString();
config.activitiUsername = properties.get("activiti.username").toString();
config.activitiPassword = properties.get("activiti.password").toString();
config.tomcatAddress = properties.get("tomcat.address").toString();
config.tomcatPublicAddress = properties.get("tomcat.publicAddress").toString();
config.tomcatUsername = properties.get("tomcat.username").toString();
config.tomcatPassword = properties.get("tomcat.password").toString();
if (config.activitiAddress != null && !config.activitiAddress.endsWith("/")) {
config.activitiAddress += "/";
}
if (config.tomcatAddress != null && !config.tomcatAddress.endsWith("/")) {
config.tomcatAddress += "/";
}
if (config.tomcatPublicAddress != null && !config.tomcatPublicAddress.endsWith("/")) {
config.tomcatPublicAddress += "/";
}
String tomcatVersionStr = properties.get("tomcat.version").toString();
int tomcatVersion = 6;
try {
tomcatVersion = Integer.parseInt(tomcatVersionStr);
}
catch (NumberFormatException ex) {
}
logger.debug("Configured Composite Service Factory and Activiti Client {} {} {} {} {} {} {} {}",
config.compositionsDir, config.activitiAddress, config.activitiUsername,
config.activitiPassword, config.tomcatAddress,
config.tomcatUsername, config.tomcatPassword, config.tomcatVersion);
config.tomcatVersion = tomcatVersion;
compositeServiceFactory = new CompositeServiceFactory(config);
activitiClient = new ActivitiClient(config.activitiAddress,
config.activitiUsername,
config.activitiPassword);
try {
Map<String, String> deploymentsMap = activitiClient.getDeployments();
for(String id: deploymentsMap.keySet()) {
String name = deploymentsMap.get(id);
logger.debug("{} {}", name, id);
/*if (name.contains("compositionPlan1")) {
boolean result = activitiClient.deleteDeployment(id);
logger.debug("Deleted deployment {} {}: {}",
name, id, result);
}*/
}
} catch(Exception ex) {
logger.error("Error getting deployments: {}", ex.getMessage());
}
try {
activitiClient.getProcessDefinitions();
ProcessDefinitionData definition =
activitiClient.getProcessDefinitionByName("an_00082_bookingMail");
if (definition != null) {
logger.debug("Found {}, {}, {}", definition.id, definition.key, definition.version);
}
} catch(Exception ex) {
logger.error("Error getting definitions: {}", ex.getMessage());
}
}
/**
* De-activate method called by Declarative Services when the service is de-activated
* @param context The service context
*/
protected void deactivate(ComponentContext context) {
logger.debug("*** Deactivating RemoteImpl");
}
/**
* Modified method called by Declarative Services when the service is modified
* @param context The context of the service
*/
protected void modified(ComponentContext context) {
logger.debug("*** Modifying RemoteImpl");
configure(context);
}
/**
* Generates an id according to the Aniketos Composition Id convention
* @param deployDetails The deployment details
* @return The generated id
*/
private String getAniketosCompositionId(DeploymentDetails deployDetails) {
String serviceName = "";
if (deployDetails != null && deployDetails.getServiceName() != null) {
serviceName = deployDetails.getServiceName();
}
return dataAdapter.addService(serviceName, "");
}
/**
* Deploys a web service in Tomcat
* @param compositionName The name of the composition
* @param processId The process id of the Activiti process
* @param methodName The name of the method that invokes the composite service
* @return the endpoint of the deployed web service or null, if the deployment failed
*/
private String deployWar(String compositionName, String processId, String methodName) {
String result = null;
logger.debug("Deploying war for {},{}", compositionName, processId);
try {
compositeServiceFactory.createServiceInstance(compositionName,
processId, methodName);
compositeServiceFactory.createWar(compositionName);
result = compositeServiceFactory.deployWarTomcat(compositionName);
}
catch (Exception ex) {
//ex.printStackTrace();
logger.error("Exception while deploying WAR: {}", ex.getMessage());
}
return result;
}
private Pattern pattern = Pattern.compile("<process id=\"(.+)\" name=\"(.+)\">");
private String updateBpmn(String bpmn, String processId) {
Matcher matcher = pattern.matcher(bpmn);
if (matcher.find()) {
String oldProcessId = matcher.group(1);
String oldName = matcher.group(2);
String updated = bpmn.replaceAll(oldProcessId, processId);
updated = updated.replaceAll(oldName, processId);
return updated;
}
return bpmn;
}
/**
* Deploys a composition plan
* @param compositionPlan The composition plan
* @param agreementTemplate The agreement template serialized
* @param consumerPolicy The consumer policy serialized
* @return the id of the deployment, if the deployment succeeds, null otherwise
*/
String deployComposition(ICompositionPlan compositionPlan,
String agreementTemplate, IConsumerPolicy consumerPolicy) {
String bpmn = compositionPlan.getBPMNXML();
String processName = compositionPlan.getCompositionPlanID();
logger.debug("Deploying process: {}", processName);
bpmn = updateBpmn(bpmn, processName);
compositionPlan.setBPMNXML(bpmn);
//logger.debug(bpmn);
if (activitiClient == null) {
logger.error("Activiti client is null!");
return null;
}
return activitiClient.upload(processName, bpmn);
}
/**
* Deploys a composite service. A web service is deployed in Tomcat and a process is
* uploaded in Activiti
* @param compositionPlans The composition plans of the service. The first is the
* default one. Others are alternatives
* @param agreementTemplate The agreement template serialized
* @param consumerPolicy The consumer policy serialized
* @param rules The rules associated with the service in XML format
* @param deployDetails The deployment details
* @return the result of the operation
*/
@Override
public DeployResult deploy(ICompositionPlan[] compositionPlans,
String agreementTemplate, IConsumerPolicy consumerPolicy,
String rules, DeploymentDetails deployDetails) {
DeployResult result = new DeployResult();
result.setOk(false);
if (rules != null) {
logger.debug(rules);
}
if (dataAdapter == null) {
logger.info("Data Adapter is null!");
result.setErrorMessage("Data Adapter is null");
return result;
}
if (compositionPlans == null || compositionPlans.length == 0) {
logger.info("No composition plans provided!");
result.setErrorMessage("No composition plans provided!");
return result;
}
ICompositionPlan compositionPlan = compositionPlans[0];
Map<String, String> serviceTaskIdsMap = null;
try {
serviceTaskIdsMap =
BpmnUtils.getServiceTasks(compositionPlan.getBPMNXML());
} catch (Exception ex) {
logger.warn("Can't parse BPMN file: {}", ex.getMessage());
}
if (serviceTaskIdsMap == null || serviceTaskIdsMap.isEmpty()) {
result.setErrorMessage("BPMN file doesn't contain service locations!");
return result;
}
String compositionId = getAniketosCompositionId(deployDetails);
if (compositionId == null) {
result.setErrorMessage("Failed to persist new service in database");
return result;
}
compositionPlan.setCompositionPlanID(compositionId);
String deploymentId = deployComposition(compositionPlan, agreementTemplate,
consumerPolicy);
if (deploymentId != null) {
ProcessDefinitionData definition =
activitiClient.getProcessDefinitionByName(compositionId);
String processId = compositionId;
if (definition != null) {
processId = definition.id;
dataAdapter.updateService(compositionId, processId, deploymentId);
}
String methodName = "start";
if (deployDetails != null && deployDetails.getOperationName() != null) {
String m = deployDetails.getOperationName();
if (m.matches("^[a-zA-Z_]{1}[a-zA-Z0-9_]*$")) {
methodName = m;
}
}
String endpoint = deployWar(compositionId, processId, methodName);
if (endpoint != null) {
result.setOk(true);
result.setServiceAddress(endpoint);
// Store the service tasks ids in the database
storeServiceTasks(serviceTaskIdsMap,
compositionPlan.getBPMNXML(),
compositionId);
// Subscribe to notifications
if (notification != null) {
logger.debug("Start subscriptions...");
List<String> subscribedServices = new ArrayList<String>();
for(String serviceTaskId: serviceTaskIdsMap.keySet()) {
String atomicService = serviceTaskIdsMap.get(serviceTaskId);
if (subscribedServices.contains(atomicService)) {
continue;
}
notification.subscribe(atomicService, rules);
subscribedServices.add(atomicService);
}
}
else {
logger.error("Notification component not found!");
}
// Announce service to the Marketplace
if (marketplace != null) {
MarketplaceAnnouncement marketplaceAnnouncement =
new MarketplaceAnnouncement();
marketplaceAnnouncement.setCompositionPlans(compositionPlans);
marketplaceAnnouncement.setSender("SCF");
marketplaceAnnouncement.setRules(rules);
ServiceDescriptor serviceDescriptor =
new ServiceDescriptor();
serviceDescriptor.setName(compositionId);
serviceDescriptor.setBinding(endpoint);
serviceDescriptor.setId(endpoint);
serviceDescriptor.setDescription("Composite service");
if (deployDetails != null && deployDetails.getTags() != null) {
List<ServiceOperation> serviceOperations = new ArrayList<ServiceOperation>();
for(String tag: deployDetails.getTags()) {
ServiceOperation so = new ServiceOperation();
so.setTag(tag);
serviceOperations.add(so);
}
serviceDescriptor.setOperations(
serviceOperations.toArray(new ServiceOperation [0]));
}
marketplaceAnnouncement.setServiceDescriptor(serviceDescriptor);
marketplace.announceService(null, marketplaceAnnouncement);
}
}
else {
logger.error("Endpoint is null!");
// TODO: Undeploy process here
result.setErrorMessage("Failed to deploy war");
}
}
else {
result.setErrorMessage("Failed to install process");
}
return result;
}
void storeServiceTasks(Map<String, String> serviceTaskIdsMap,
String bpmn, String compositeServiceName) {
try {
for(String serviceTaskId: serviceTaskIdsMap.keySet()) {
String location = serviceTaskIdsMap.get(serviceTaskId);
logger.debug(serviceTaskId + " -> " + location);
dataAdapter.addServiceTask(location, serviceTaskId,
bpmn, compositeServiceName);
}
}
catch (Exception ex) {
logger.warn("Couldn't persist service tasks: {}", ex.getMessage());
}
}
/**
* Sets the Marketplace service.
* @param service The Marketplace service
*/
public synchronized void setMarketplaceService(IMarketplace service) {
logger.debug("Marketplace Service was set!");
this.marketplace = service;
//TODO: Remove this
/*try {
testAlertRulematching();
}
catch (Throwable t) {
t.printStackTrace();
}*/
}
/**
* Unsets the Marketplace service.
* @param service The Marketplace service
*/
public synchronized void unsetMarketplaceService(IMarketplace service) {
logger.debug("Marketplace Service was unset.");
if (this.marketplace == service) {
this.marketplace = null;
}
}
/**
* Sets the DataAdapter service.
* @param service The DataAdapter service
*/
public synchronized void setDataAdapter(DataAdapter service) {
logger.debug("DataAdapter Service was set!");
this.dataAdapter = service;
}
/**
* Unsets the DataManager service.
* @param service The DataManager service
*/
public synchronized void unsetDataAdapter(DataAdapter service) {
logger.debug("DataAdapter Service was unset.");
if (this.dataAdapter == service) {
this.dataAdapter = null;
}
}
/**
* Sets the ScpmClient service.
* @param service The ScpmClient service
*/
public synchronized void setScpmClient(ICompositionPlanner service) {
System.out.println("ScpmClient Service was set!");
logger.debug("ScpmClient Service was set!");
this.scpmClient = service;
scpmActions = new ScpmActions(scpmClient);
/*String atomicService = "http://83.235.133.36/AniketosWS/MapsServiceSoapHttpPort?wsdl";
AlertMessage alertMessage = new AlertMessage();
alertMessage.setServiceId(atomicService);
alertMessage.setAlertType("ContextChange");
alertMessage.setValue("5");
logger.debug("Dummy alert sent");
onServiceEvent(atomicService, alertMessage);*/
}
/**
* Unsets the DataManager service.
* @param service The DataManager service
*/
public synchronized void unsetScpmClient(ICompositionPlanner service) {
logger.debug("ScpmClient Service was unset.");
if (this.scpmClient == service) {
this.scpmClient = null;
scpmActions = null;
}
}
private void testAlertRulematching() {
String rulesXml = marketplace.getRules("http://hestia.atc.gr/an_00157_HBS/service?wsdl");
/*<?xml version="1.0" encoding="UTF-8" standalone="no"?><rules>
<rule id="0">
<service taskId="servicetask9" taskName="Map"/>
<type>Trust level change</type>
<value comparison="<">7</value>
<action>
<changeRuntime>
<recomposition allowStop="0"/>
</changeRuntime>
</action>
</rule>
</rules>*/
List<Rule> rules = null;
try {
RulesParser rulesParser = new RulesParser();
rules = rulesParser.parse(rulesXml);
} catch (Exception ex) {
System.out.println("Failed to parse rules: " + ex.getMessage());
}
System.out.println("Parsed rules...");
AlertMessage alertMessage = new AlertMessage();
alertMessage.setAlertType("Trust level change");
alertMessage.setValue("5.3");
System.out.println("Alert1 matches rule: " + doesAlertMatchesRule(alertMessage, rules.get(0)));
alertMessage.setValue("8");
System.out.println("Alert1 matches rule: " + doesAlertMatchesRule(alertMessage, rules.get(0)));
}
private boolean doesAlertMatchesRule(AlertMessage alertMessage, Rule rule) {
String type = rule.getEvent().getType();
type = type.replaceAll("\\s","");
logger.debug("Rule type: {}, Alert type {}", type, alertMessage.getAlertType());
if (type.compareToIgnoreCase(alertMessage.getAlertType())!=0) {
return false;
}
String threatId = alertMessage.getThreatId();
String ruleThreatId = rule.getEvent().getThreatId();
if (threatId == null || threatId.trim().length() == 0) {
threatId = null;
}
if (ruleThreatId == null || ruleThreatId.trim().length() == 0) {
ruleThreatId = null;
}
if ((threatId != null && !threatId.equals(ruleThreatId)) ||
(threatId == null && ruleThreatId != null)){
logger.debug("Alert doesn't match rule (different threat ids {}, {})",
threatId, ruleThreatId);
return false;
}
String value = alertMessage.getValue();
String ruleValue = rule.getEvent().getValue();
String comparison = rule.getEvent().getComparison();
if (value == null && ruleValue == null) {
logger.debug("Alert matches rules (both values null)");
return true;
}
if (value == null || ruleValue == null) {
logger.debug("Alert doesn't match rule (one value null)");
return false;
}
float dvalue;
float drvalue;
try {
dvalue = Float.valueOf(value);
drvalue = Float.valueOf(ruleValue);
} catch (Exception ex) {
logger.warn("Can't parse values {}, {}: {}", value, ruleValue, ex.getMessage());
return false;
}
if ("=".equals(comparison)) {
return value.equals(ruleValue);
}
if (">=".equals(comparison)) {
return dvalue >= drvalue;
}
if ("<=".equals(comparison)) {
return dvalue <= drvalue;
}
if (">".equals(comparison)) {
return dvalue > drvalue;
}
if ("<".equals(comparison)) {
return dvalue < drvalue;
}
return false;
}
private void findService(String service, String bpmn) {
logger.debug("Testing service: {}", service);
if (bpmn != null) {
logger.debug("S1 {}", bpmn.contains(ScpmActions.caseStudyABmpn1Description));
logger.debug("S2 {}", bpmn.contains(ScpmActions.caseStudyABmpn2Description));
}
}
@Override
public void onServiceEvent(String atomicService, AlertMessage alertMessage) {
System.out.println("Received alert for: " + atomicService);
if (scpmActions == null) {
logger.warn("Scpm is null!");
}
try {
logger.debug("Event received for service: {}" , atomicService);
List<ServiceTask> serviceTasks =
dataAdapter.getServiceTasksByAtomicService(atomicService);
if (serviceTasks != null && !serviceTasks.isEmpty()) {
List<String> recompositionedServices = new ArrayList<String>();
for(ServiceTask serviceTask: serviceTasks) {
String compositeService = serviceTask.getCompositeServiceName();
if (recompositionedServices.contains(compositeService)) {
continue;
}
String rulesXml = marketplace.getRulesByServiceName(compositeService);
if (rulesXml != null && rulesXml.length() > 0) {
logger.debug("Parsing rules of {}", compositeService);
List<Rule> rules = null;
try {
RulesParser rulesParser = new RulesParser();
rules = rulesParser.parse(rulesXml);
} catch (Exception ex) {
logger.info("Failed to parse rules of {}: {} ", compositeService, ex.getMessage());
}
if (rules != null) {
for(Rule rule: rules) {
if (doesAlertMatchesRule(alertMessage, rule)) {
logger.debug("Alert matches rule!");
if (rule.getAction().getActionType() == ActionType.Recomposition ||
rule.getAction().getActionType() == ActionType.RecompositionAndReconfiguration) {
logger.debug("Trying recomposition");
if (scpmActions != null &&
scpmActions.doRecompositionMock(this, serviceTask)) {
recompositionedServices.add(compositeService);
break;
}
}
else if (rule.getAction().getActionType() == ActionType.Reconfiguration) {
logger.debug("Trying reconfiguration");
MarketplaceSearchParams params = new MarketplaceSearchParams();
params.setName(compositeService);
List<ServiceDescriptor> services = marketplace.discoverService(null, params);
if (services != null && services.size() == 1) {
String compositeServiceUrl = services.get(0).getId();
String bpmn = marketplace.getBpmnDiagram(compositeServiceUrl);
scpmActions.doReconfigurationMock(this, compositeServiceUrl, bpmn);
}
else {
logger.debug("Didn't find service {}", compositeService);
}
break;
}
}
}
}
}
else {
logger.debug("Rules for {} are empty!", compositeService);
}
}
}
else {
logger.debug("Found no service tasks for service: {}", atomicService);
String bpmn = marketplace.getBpmnDiagram(atomicService);
boolean isCompositeService = bpmn != null && bpmn.length() > 10;
if (isCompositeService) {
logger.debug("{} is a composite service. Checking for reconfiguration - recomposition",
atomicService);
findService(atomicService, bpmn);
String rulesXml = marketplace.getRules(atomicService);
if (rulesXml != null && rulesXml.length() > 0) {
logger.debug("Parsing rules of {}", atomicService);
List<Rule> rules = null;
try {
RulesParser rulesParser = new RulesParser();
rules = rulesParser.parse(rulesXml);
} catch (Exception ex) {
logger.info("Failed to parse rules of {}: {} ", atomicService, ex.getMessage());
}
if (rules != null) {
for(Rule rule: rules) {
if (doesAlertMatchesRule(alertMessage, rule)) {
logger.debug("Alert matches rule!");
if (rule.getAction().getActionType() == ActionType.Reconfiguration) {
logger.debug("Trying reconfiguration");
if (scpmActions != null &&
scpmActions.doReconfigurationMock(this, atomicService, bpmn)) {
}
break;
}
}
}
}
}
else {
logger.debug("Rules for {} are empty!", atomicService);
}
}
else {
logger.debug("{} is not composite {}", atomicService, bpmn);
}
}
}
catch (Throwable t) {
t.printStackTrace();
}
}
}