/**
* EasySOA Registry
* Copyright 2011 Open Wide
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact : easysoa-dev@googlegroups.com
*/
package org.easysoa.registry.dbb.rest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.log4j.Logger;
import org.easysoa.registry.DocumentService;
import org.easysoa.registry.dbb.BrowsingContext;
import org.easysoa.registry.dbb.FoundService;
import org.easysoa.registry.dbb.ServiceFinderService;
import org.easysoa.registry.dbb.ServiceFinderStrategy;
import org.easysoa.registry.rest.EasysoaModuleRoot;
import org.easysoa.registry.types.Endpoint;
import org.json.JSONArray;
import org.json.JSONObject;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory;
import org.nuxeo.ecm.webengine.model.WebObject;
import org.nuxeo.runtime.api.Framework;
import com.sun.jersey.api.core.HttpContext;
/**
* REST service to find WSDLs from given URL.
*
* Use: .../nuxeo/site/easysoa/servicefinder/{url}
* Params: {url} The URL of the page to consider (not encoded).
* Other protocols than HTTP are not supported.
*
* @author mkalam-alami
*
*/
@Path("easysoa/servicefinder")
//@Produces("application/x-javascript") // doesn't work for bookmarklet's jquery $.ajax() in jsonp ("parsing error")
@Produces("application/javascript") // required (?!) for bookmarklet's jquery $.ajax() in jsonp (else "parsing error")
@WebObject(type = "servicefinder")
public class ServiceFinderRest extends EasysoaModuleRoot {
private static Logger logger = Logger.getLogger(ServiceFinderRest.class);
private DocumentModelList endpointsCache = null;
private List<String> environmentsNamesCache = null;
@GET
public Object getDefault() {
return "Invalid use (please append an address to explore to the URL)";
}
/**
* Computes a list of all existing environments, by analysing the endpoints properties.
* @return
* @throws Exception
*/
@GET
@Path("/environments")
public Object getEnvironmentList() throws Exception {
CoreSession session = SessionFactory.getSession(request);
DocumentService docService = Framework.getService(DocumentService.class);
DocumentModelList allEndpoints = docService.query(session, "SELECT * FROM " + Endpoint.DOCTYPE, true, false); // TODO Phase (LATER +component)
// Use a cache for performance
boolean computeEnvList = true;
if (environmentsNamesCache != null) {
if (endpointsCache != null && endpointsCache.equals(allEndpoints)) {
computeEnvList = false;
}
}
if (computeEnvList) {
List<String> environmentsNames = new ArrayList<String>();
for (DocumentModel endpoint : allEndpoints) {
String environmentName = (String) endpoint.getPropertyValue(Endpoint.XPATH_ENDP_ENVIRONMENT);
if (environmentName != null && !environmentsNames.contains(environmentName)) {
environmentsNames.add(environmentName);
}
}
endpointsCache = allEndpoints;
environmentsNamesCache = environmentsNames;
}
return new JSONArray(environmentsNamesCache).toString();
}
/**
* JSONP for bookmarklet
* NB. requires application/javascript mimetype to work on bookmarklet's
* jquery $.ajax() side in jsonp, but doesn't work if set here, has to be put at the tope
* @param uriInfo
* @return
* @throws Exception
*/
@GET
@Path("/find/{url:.*}")
public Object findServices(@Context UriInfo uriInfo) throws Exception {
URL url = null;
String callback = null;
try {
// Retrieve URL
String restServiceURL = uriInfo.getBaseUri().toString() + "easysoa/servicefinder/find/";
url = new URL(uriInfo.getRequestUri().toString().substring(restServiceURL.length()));
if (url.getQuery() != null && url.getQuery().contains("callback=")) {
List<NameValuePair> queryTokens = URLEncodedUtils.parse(url.toURI(), "UTF-8");
for (NameValuePair token : queryTokens) {
if (token.getName().equals("callback")) {
callback = token.getValue(); // now let's remove this callback from original jsonp URL
}
}
}
}
catch (MalformedURLException e) {
logger.debug(e);
return "{ errors: '" + formatError(e) + "' }";
}
// Find WSDLs
String foundServicesJson = findServices(new BrowsingContext(url));
String res;
if (callback != null) {
res = callback + '(' + foundServicesJson + ");"; // ')' // TODO OK
}
else {
res = foundServicesJson;
}
return res;
}
@POST
public Object findServices(@Context HttpContext httpContext, @Context HttpServletRequest request) throws Exception {
// Retrieve params
@SuppressWarnings("unchecked")
Map<String, String> formValues = getFirstValues(request.getParameterMap());
// Find WSDLs
BrowsingContext browsingContext = new BrowsingContext(new URL(formValues.get("url")), formValues.get("data"));
return findServices(browsingContext);
}
public String findServices(BrowsingContext context) throws Exception {
JSONArray errors = new JSONArray();
JSONObject result = new JSONObject();
// Run finders
List<FoundService> foundServices = new LinkedList<FoundService>();
if (context.getURL() != null && context.getData() != null) {
ServiceFinderService finderComponent = Framework.getService(ServiceFinderService.class);
List<ServiceFinderStrategy> strategies = finderComponent.getStrategies();
for (ServiceFinderStrategy strategy : strategies) {
List<FoundService> strategyResult = null;
try {
strategyResult = strategy.findFromContext(context);
}
catch (Exception e) {
errors.put(formatError(e, "Failed to run service finder strategy "+strategy.getClass().getName()));
}
if (strategyResult != null) {
foundServices.addAll(strategyResult);
}
}
}
// TODO: Filter duplicates
// Format response
JSONObject foundLinks = new JSONObject();
for (FoundService foundService : foundServices) {
String appName = foundService.getApplicationName();
if (appName != null) {
result.put("applicationName", appName);
}
foundLinks.put(foundService.getName(), foundService.getURL());
}
if (foundLinks.keys().hasNext()) {
result.put("foundLinks", foundLinks);
}
if (errors.length() > 0) {
result.put("errors", errors);
}
return result.toString();
}
private static Map<String, String> getFirstValues(Map<String, String[]> multivaluedMap) {
Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String[]> entry : multivaluedMap.entrySet()) {
if (entry.getValue().length > 0) {
map.put(entry.getKey(), entry.getValue()[0]);
}
}
return map;
}
private String formatError(Exception e, String message) {
return e.getClass().getSimpleName()+": "+message+" (cause: "+e.getMessage()+")";
}
private String formatError(Exception e) {
return e.getClass().getSimpleName()+": "+e.getMessage();
}
}