/** * Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET * (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije * informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE * COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVA��O, SA (PTIN), IBM ISRAEL * SCIENCE AND TECHNOLOGY LTD (IBM), INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA * PERIORISMENIS EFTHINIS (AMITEC), TELECOM ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD * (NEC)) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.societies.platform.servicelifecycle.servicediscovery; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.eclipse.jetty.util.log.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.cis.management.ICis; import org.societies.api.cis.management.ICisManager; import org.societies.api.cis.management.ICisOwned; import org.societies.api.comm.xmpp.interfaces.ICommManager; import org.societies.api.identity.IIdentity; import org.societies.api.identity.INetworkNode; import org.societies.api.identity.InvalidFormatException; import org.societies.api.internal.servicelifecycle.IServiceDiscovery; import org.societies.api.internal.servicelifecycle.IServiceDiscoveryRemote; import org.societies.api.internal.servicelifecycle.ServiceDiscoveryException; import org.societies.api.internal.servicelifecycle.ServiceModelUtils; import org.societies.api.internal.servicelifecycle.serviceRegistry.IServiceRegistry; import org.societies.api.internal.servicelifecycle.serviceRegistry.exception.ServiceRetrieveException; import org.societies.api.schema.servicelifecycle.model.Service; import org.societies.api.schema.servicelifecycle.model.ServiceResourceIdentifier; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; /** * The implementation of Service Discovery * * @author <a href="mailto:sanchocsa@gmail.com">Sancho Rêgo</a> (PTIN) * */ public class ServiceDiscovery implements IServiceDiscovery { static final Logger logger = LoggerFactory.getLogger(ServiceDiscovery.class); private IServiceRegistry serviceReg; private ICommManager commMngr; private IServiceDiscoveryRemote serviceDiscoveryRemote; private ICisManager cisManager; private ExecutorService executor; public ICisManager getCisManager(){ return cisManager; } public void setCisManager(ICisManager cisManager){ this.cisManager = cisManager; } /** * @return the commMngr */ public ICommManager getCommMngr() { return commMngr; } /** * @return the serviceDiscoveryRemote */ public IServiceDiscoveryRemote getServiceDiscoveryRemote() { return serviceDiscoveryRemote; } /** * @param serviceDiscoveryRemote the serviceDiscoveryRemote to set */ public void setServiceDiscoveryRemote( IServiceDiscoveryRemote serviceDiscoveryRemote) { this.serviceDiscoveryRemote = serviceDiscoveryRemote; } /** * @param commMngr the commMngr to set */ public void setCommMngr(ICommManager commMngr) { this.commMngr = commMngr; } public IServiceRegistry getServiceReg() { return serviceReg; } public void setServiceReg(IServiceRegistry serviceReg) { this.serviceReg = serviceReg; } public ServiceDiscovery(){ logger.info("Service Discovery Created!"); executor = Executors.newCachedThreadPool(); } @Override @Async public Future<List<Service>> getLocalServices() { INetworkNode currentNode = commMngr.getIdManager().getThisNetworkNode(); logger.debug("Retrieving services for this *specific* CSS Node: {} , jid: {}", currentNode.getNodeIdentifier(),currentNode.getJid()); List<Service> result = null; try { result = getServiceReg().retrieveServicesInCSSNode(currentNode.getJid()); } catch (Exception e) { logger.error("Exception occurred while trying to get local services: {}", e); e.printStackTrace(); } return new AsyncResult<List<Service>>(result); } @Override @Async public Future<List<Service>> getServices(String jid) throws ServiceDiscoveryException { Future<List<Service>> asyncResult = null; List<Service> result = null; try { asyncResult = this.getServices(commMngr.getIdManager().fromJid(jid)); result = asyncResult.get(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvalidFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } return new AsyncResult<List<Service>>(result); } @Override @Async public Future<List<Service>> getServices(IIdentity node) throws ServiceDiscoveryException { List<Service> serviceList = new ArrayList<Service>(); if(logger.isDebugEnabled()) logger.debug("getServices(Identity node) for node: " + node.getJid()); boolean myNode; boolean myCIS = false; INetworkNode currentNode = commMngr.getIdManager().getThisNetworkNode(); if (!currentNode.getJid().contentEquals(node.getJid())) myNode = false; else myNode = true; try { // Is it our node? If so, local search if(myNode){ if(logger.isDebugEnabled()) logger.debug("We're dealing with our own CSS!"); serviceList = getServiceReg().retrieveServicesInCSS(node.getBareJid()); } else{ //Is it one of my CIS? If so, local search ICisOwned localCis = getCisManager().getOwnedCis(node.getJid()); if(localCis != null){ if(logger.isDebugEnabled()) logger.debug("We're dealing with our CIS! Local search!"); serviceList = getServiceReg().retrieveServicesSharedByCIS(node.getJid()); myCIS = true; } } }catch (ServiceRetrieveException e) { //TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if (serviceList == null || serviceList.isEmpty()) { if(logger.isDebugEnabled()) logger.debug("No services retrieved from local node..."); //IIdentity currentNode = commMngr.getIdManager().getThisNetworkNode(); if (!myNode && !myCIS) { if(logger.isDebugEnabled()) logger.debug("Attempting to retrieve services from remote node: " + node.getJid()); ServiceDiscoveryRemoteClient callback = new ServiceDiscoveryRemoteClient(); getServiceDiscoveryRemote().getServices(node, callback); serviceList = callback.getResultList(); } } // Quick log message if(logger.isDebugEnabled()){ if(serviceList == null || serviceList.isEmpty()) logger.debug("getServices: no services found!"); else{ Iterator<Service> it = serviceList.iterator(); String logStuff = "getServices: "; while(it.hasNext()){ logStuff += it.next().getServiceName() + "; \n"; } logger.debug(logStuff); } } return new AsyncResult<List<Service>>(serviceList); } @Override @Async public Future<Service> getService(ServiceResourceIdentifier serviceId) throws ServiceDiscoveryException { if(logger.isDebugEnabled()) logger.debug("Service Discovery::getService() for" + serviceId); Service result = null; try{ // First we check the local repository result = getServiceReg().retrieveService(serviceId); // Did we find it there? if(result == null){ if(logger.isDebugEnabled()) logger.debug("Didn't find service on local repository, now checking if it's a remote service!"); String myLocalJid = getCommMngr().getIdManager().getThisNetworkNode().getJid(); String serviceJid = ServiceModelUtils.getJidFromServiceIdentifier(serviceId); // Is it supposed to be local? if(!myLocalJid.equals(serviceJid)){ if(logger.isDebugEnabled()) logger.debug("It's a remote service from node: " + serviceJid); INetworkNode node = getCommMngr().getIdManager().fromFullJid(serviceJid); // We call the other network node to get the information on the service ServiceDiscoveryRemoteClient callback = new ServiceDiscoveryRemoteClient(); getServiceDiscoveryRemote().getService(serviceId, node, callback); List<Service> resultList = callback.getResultList(); // Only one service should be returned. If not, we're dealing with some sort of problem if(resultList.size() == 1){ result = resultList.get(0); if(logger.isDebugEnabled()) logger.debug("Found service remotely!"); } } } } catch(Exception ex){ ex.printStackTrace(); logger.error("getService():: Exception getting Service: " + ex); throw new ServiceDiscoveryException("getService():: Exception getting Service",ex); } if(result == null) return null; else return new AsyncResult<Service>(result); } @Override @Async public Future<List<Service>> searchServices(Service filter) throws ServiceDiscoveryException { if(logger.isDebugEnabled()) logger.debug("Searching local repository for a given service"); List<Service> result; try{ result = getServiceReg().findServices(filter); if(logger.isDebugEnabled()) logger.debug("Found "+ result.size() + " services that fulfill the criteria"); } catch(Exception ex){ ex.printStackTrace(); logger.error("Searching for services: Exception! : " + ex); throw new ServiceDiscoveryException("Exception while searching for services",ex); } return new AsyncResult<List<Service>>(result); } @Override @Async public Future<List<Service>> searchServicesAll(Service filter) throws ServiceDiscoveryException { if(logger.isDebugEnabled()) logger.debug("Searching all repositories for a given service"); HashMap<String,Service> result = new HashMap<String,Service>(); try{ if(logger.isDebugEnabled()) logger.debug("Searching all our CIS..."); List<ICis> cisList = getCisManager().getCisList(); /*HashMap<String,Future<List<Service>>> searchList = new HashMap<String,Future<List<Service>>>(cisList.size()+1); for(ICis cis : cisList){ logger.debug("Searching in CIS {}", cis.getName()); Future<List<Service>> resultSearch = searchServices(filter,cis.getCisId()); searchList.put(cis.getName(), resultSearch); } if(logger.isDebugEnabled()) logger.debug("Searching our local node..."); searchList.put("Local Node",searchServices(filter)); //And now we check... for( String searchNode : searchList.keySet()){ logger.debug("Checking result for {}", searchNode); List<Service> foundServices = searchList.get(searchNode).get(); for(Service foundService : foundServices){ String key = ServiceModelUtils.serviceResourceIdentifierToString(foundService.getServiceIdentifier()); logger.debug("Found service {}", key); result.put(key, foundService); } } */ BlockingQueue<List<Service>> listServices = new ArrayBlockingQueue<List<Service>>(cisList.size()); for(ICis cis : cisList){ logger.debug("Searching in CIS {}", cis.getName()); try{ IIdentity node = getCommMngr().getIdManager().fromJid(cis.getCisId()); executor.execute(new SearchServiceAsync(this,filter,node,listServices)); } catch(Exception ex){ logger.error("Exception converting to node: {}",cis.getCisId()); ex.printStackTrace(); } } List<Service> searchResults = searchServices(filter).get(); for(Service searchResult : searchResults){ String key = ServiceModelUtils.serviceResourceIdentifierToString(searchResult.getServiceIdentifier()); logger.debug("Found service {}", key); result.put(key, searchResult); } for(int i = 0; i < cisList.size(); i++){ List<Service> foundServices = listServices.take(); for(Service foundService : foundServices){ String key = ServiceModelUtils.serviceResourceIdentifierToString(foundService.getServiceIdentifier()); logger.debug("Found service {}", key); result.put(key, foundService); } } } catch(Exception ex){ ex.printStackTrace(); logger.error("Searching for services: Exception! : " + ex); throw new ServiceDiscoveryException("Exception while searching for services",ex); } return new AsyncResult<List<Service>>(new ArrayList<Service>(result.values())); } @Override @Async public Future<List<Service>> searchServices(Service filter, IIdentity node) throws ServiceDiscoveryException { if(logger.isDebugEnabled()) logger.debug("Searching repository for a given service, on node: " + node.getJid()); List<Service> foundServices = new ArrayList<Service>(); try{ boolean myNode; INetworkNode currentNode = commMngr.getIdManager().getThisNetworkNode(); if (!currentNode.getJid().contentEquals(node.getJid())) myNode = false; else myNode = true; // Is it our node? If so, local search if(myNode){ if(logger.isDebugEnabled()) if(logger.isDebugEnabled()) logger.debug("It's the local node, so we do a local call"); return searchServices(filter); } else{ //Is it one of my CIS? If so, local search ICisOwned localCis = getCisManager().getOwnedCis(node.getJid()); if(localCis != null){ logger.debug("We're dealing with our CIS {}! Local search!", localCis.getName()); foundServices = getServiceReg().findServices(filter, node.getJid()); logger.debug("Found {} services that fulfill this criteria in {}!",foundServices.size(),localCis.getName()); } else{ if(logger.isDebugEnabled()) logger.debug("Attempting to retrieve services from remote node: " + node.getJid()); ServiceDiscoveryRemoteClient callback = new ServiceDiscoveryRemoteClient(); getServiceDiscoveryRemote().searchService(filter, node, callback); foundServices = callback.getResultList(); logger.debug("Found {} services that fulfill this criteria in remote CIS {}!",foundServices.size(),node.getJid()); } } } catch (Exception e) { logger.error("Exception while searching for services in a node! : " + e.getMessage()); e.printStackTrace(); } return new AsyncResult<List<Service>>(foundServices); } @Override @Async public Future<List<Service>> searchServices(Service filter, String jid) throws ServiceDiscoveryException { if(logger.isDebugEnabled()) logger.debug("Searching repository for a given service, on jid: " + jid); try { INetworkNode node = getCommMngr().getIdManager().fromFullJid(jid); return searchServices(filter,node); } catch (Exception ex) { ex.printStackTrace(); logger.error("Searching for services: Exception! : " + ex); throw new ServiceDiscoveryException("Exception while searching for services!",ex); } } private class SearchServiceAsync implements Runnable{ private BlockingQueue<List<Service>> listServices; private IIdentity node; private Service filter; private ServiceDiscovery parent; public SearchServiceAsync(ServiceDiscovery parent, Service filter, IIdentity node, BlockingQueue<List<Service>> listServices){ this.parent = parent; this.filter = filter; this.node = node; this.listServices = listServices; } @Override public void run() { try { List<Service> result = parent.searchServices(filter, node).get(); listServices.add(result); } catch (Exception e) { logger.error("Exception in Asynch handler for {} : {}",node.getJid(),e.getMessage()); e.printStackTrace(); } } } }