From ae7ed9ce3238527bc352781746856ca73b0d206c Mon Sep 17 00:00:00 2001 From: Francesco Mangiacrapa Date: Tue, 5 Apr 2016 09:57:55 +0000 Subject: [PATCH] 3135: Uri Resolver enhancements: create a Genetwork Resolver Task-Url: https://support.d4science.org/issues/3135 Added new servlet/resolver Geonetwork Updated pom version at 1.6.0 git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/data-transfer/uri-resolver@126760 82a268e6-3cf1-43bd-a215-b396298e98cf --- pom.xml | 8 +- .../resolver/UriResolverRewriteFilter.java | 80 ++- .../resolver/gis/GeoRuntimeReader.java | 43 +- .../gis/GeonetworkServiceInterface.java | 20 +- .../resolver/gis/GisResolver.java | 2 +- .../gis/geonetwork/GNAuthentication.java | 84 +++ .../gis/geonetwork/GeonetworkResolver.java | 339 ++++++++++ .../gis/geonetwork/HTTPCallsUtils.java | 634 ++++++++++++++++++ src/main/webapp/WEB-INF/web.xml | 12 + 9 files changed, 1171 insertions(+), 51 deletions(-) create mode 100644 src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GNAuthentication.java create mode 100644 src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GeonetworkResolver.java create mode 100644 src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/HTTPCallsUtils.java diff --git a/pom.xml b/pom.xml index 4931296..7d4c8d7 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.gcube.data.transfer uri-resolver - 1.5.0-SNAPSHOT + 1.6.0-SNAPSHOT war The URI Resolver is an HTTP URI resolver implemented as an HTTP servlet which gives access trough HTTP to different protocols URIs. @@ -95,6 +95,12 @@ compile + + org.jdom + jdom + 2.0.2 + + diff --git a/src/main/java/org/gcube/datatransfer/resolver/UriResolverRewriteFilter.java b/src/main/java/org/gcube/datatransfer/resolver/UriResolverRewriteFilter.java index ac75382..e63bfb5 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/UriResolverRewriteFilter.java +++ b/src/main/java/org/gcube/datatransfer/resolver/UriResolverRewriteFilter.java @@ -13,19 +13,24 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import org.gcube.datatransfer.resolver.gis.geonetwork.GeonetworkResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** + * The Class UriResolverRewriteFilter. * * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it - * Nov 20, 2015 + * Apr 5, 2016 */ public class UriResolverRewriteFilter implements Filter{ + public static final String SERVLET_GEONETWORK = "/geonetwork"; protected static final String SMP_ID = "smp-id"; protected static final String SERVLET_RESOLVER_BY_ID = "id"; protected static final Logger logger = LoggerFactory.getLogger(UriResolverRewriteFilter.class); + private FilterConfig config; /* (non-Javadoc) * @see javax.servlet.Filter#destroy() */ @@ -43,36 +48,71 @@ public class UriResolverRewriteFilter implements Filter{ HttpServletRequest request = (HttpServletRequest) req; String requestURI = request.getRequestURI(); String queryString = request.getQueryString(); - logger.debug("Request URI: " + requestURI + ", QueryString: " +queryString); - if (queryString == null) { // IS A /XXXXX - logger.debug("QueryString is null, is It a new SMP public uri by ID?"); - int lastSlash = requestURI.lastIndexOf("/"); - if (lastSlash + 1 == requestURI.length()) { - logger.debug("'/' is last index, doFilter Request"); - // req.getRequestDispatcher("/").forward(req, res); - chain.doFilter(req, res); + logger.debug("Request URI: " + requestURI + ", QueryString: " +queryString+ ", Servlet path: "+request.getServletPath()); + + //IS A REQUEST FOR GEONETWORK AUTHENTICATION? (CKAN HARVESTING?) + if(isGeonetworkRequest(request.getServletPath())){ + logger.debug("is geonetwork"); + String path = request.getServletPath(); + String scope = getScope(path); + String newURI = SERVLET_GEONETWORK + "?" + GeonetworkResolver.SCOPE + "=" + scope; + logger.debug("forward "+newURI); + req.getRequestDispatcher(newURI).forward(req, res); + }else{ + //IS WORKSPACE REQUEST? + if (queryString == null) { // IS A /XXXXX + logger.debug("QueryString is null, is It a new SMP public uri by ID?"); + int lastSlash = requestURI.lastIndexOf("/"); + if (lastSlash + 1 == requestURI.length()) { + logger.debug("'/' is last index, doFilter Request"); + // req.getRequestDispatcher("/").forward(req, res); + chain.doFilter(req, res); + } + else { + String toStorageID = requestURI.substring(lastSlash + 1, requestURI.length()); + // String newURI = requestURI.replace(toReplace, + // SERVLET_RESOLVER_BY_ID+"?"+SMP_ID+"="+toReplace); + String newURI = SERVLET_RESOLVER_BY_ID + "?" + SMP_ID + "=" + toStorageID; + logger.debug("forward to: " + newURI); + req.getRequestDispatcher(newURI).forward(req, res); + } } else { - String toStorageID = requestURI.substring(lastSlash + 1, requestURI.length()); - // String newURI = requestURI.replace(toReplace, - // SERVLET_RESOLVER_BY_ID+"?"+SMP_ID+"="+toReplace); - String newURI = SERVLET_RESOLVER_BY_ID + "?" + SMP_ID + "=" + toStorageID; - logger.debug("forward to: " + newURI); - req.getRequestDispatcher(newURI).forward(req, res); + logger.debug("is NOT a SMP public uri by ID, doFilter Request"); + chain.doFilter(req, res); } } - else { - logger.debug("is NOT a SMP public uri by ID, doFilter Request"); - chain.doFilter(req, res); - } + } + + /** + * Gets the scope. + * + * @param servletPath the servlet path + * @return the scope + */ + private String getScope(String servletPath){ + logger.debug("Read servlet path: "+servletPath); + String scope = servletPath.substring(servletPath.lastIndexOf("/"), servletPath.length()); + return scope.replaceAll("_", "/"); + } + + /** + * Checks if is geonetwork request. + * + * @param servletPath the servlet path + * @return true, if is geonetwork request + */ + private boolean isGeonetworkRequest(String servletPath){ + return servletPath.startsWith(SERVLET_GEONETWORK); } /* (non-Javadoc) * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ @Override - public void init(FilterConfig arg0) throws ServletException { + public void init(FilterConfig config) throws ServletException { logger.trace("run init"); + this.config = config; } } diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/GeoRuntimeReader.java b/src/main/java/org/gcube/datatransfer/resolver/gis/GeoRuntimeReader.java index cc4b05e..4f27695 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/gis/GeoRuntimeReader.java +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/GeoRuntimeReader.java @@ -1,5 +1,5 @@ /** - * + * */ package org.gcube.datatransfer.resolver.gis; @@ -27,62 +27,59 @@ import org.slf4j.LoggerFactory; * */ public class GeoRuntimeReader { - + public static final String GEOSERVER_RESOURCE_NAME = "GeoServer"; public static final String GEONETWORK_RESOURCE_NAME = "GeoNetwork"; public static final String WORKSPACES_PROPERTY_NAME = "workspaces"; - + public static enum GEO_SERVICE{GEOSERVER, GEONETWORK}; - + public static final Logger logger = LoggerFactory.getLogger(GisResolver.class); private ServerParameters getParameters(String scope, GEO_SERVICE geoservice) throws Exception { - + ServerParameters parameters = new ServerParameters(); - + try{ - + boolean isGeoserver = geoservice.equals(GEO_SERVICE.GEOSERVER); - String resourceName = (isGeoserver ? GEOSERVER_RESOURCE_NAME : GEONETWORK_RESOURCE_NAME); + String resourceName = isGeoserver ? GEOSERVER_RESOURCE_NAME : GEONETWORK_RESOURCE_NAME; ScopeProvider.instance.set(scope); - + SimpleQuery query = queryFor(ServiceEndpoint.class); query.addCondition("$resource/Profile/Name/string() eq '"+resourceName+"'"); - + DiscoveryClient client = clientFor(ServiceEndpoint.class); List r = client.submit(query); if (r == null || r.isEmpty()) throw new Exception("Cannot retrieve the runtime resource: "+resourceName); - + ServiceEndpoint se = r.get(0); if(se.profile()==null) throw new Exception("IS profile is null for resource: "+resourceName); - + Group accessPoints = se.profile().accessPoints(); if(accessPoints.size()==0) throw new Exception("Accesspoint in resource "+resourceName+" not found"); - + AccessPoint ap = accessPoints.iterator().next(); parameters.setUrl(ap.address()); parameters.setUser(ap.username()); //username - + String decryptedPassword = StringEncrypter.getEncrypter().decrypt(ap.password()); - parameters.setPassword(decryptedPassword); //password - + if (isGeoserver){ Group properties = ap.properties(); - if(properties.size()==0) throw new Exception("Properties in resource "+resourceName+" not found"); - Iterator iter = properties.iterator(); - + while (iter.hasNext()) { - + Property prop = iter.next(); - + if(prop.name().compareTo(WORKSPACES_PROPERTY_NAME)==0){ // logger.trace("Property "+WORKSPACES_PROPERTY_NAME+" found, setting value: "+prop.value()); // parameters.setWorkspaces(prop.value()); @@ -103,10 +100,10 @@ public class GeoRuntimeReader { { if(geoservice==null) return null; - + try { return getParameters(scope, geoservice); - + } catch (Exception e){ logger.error("Error retrieving the "+geoservice+" parameters", e); throw new Exception("Error retrieving the "+geoservice+" parameters", e); diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/GeonetworkServiceInterface.java b/src/main/java/org/gcube/datatransfer/resolver/gis/GeonetworkServiceInterface.java index 621d543..89af619 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/gis/GeonetworkServiceInterface.java +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/GeonetworkServiceInterface.java @@ -1,22 +1,30 @@ /** - * + * */ package org.gcube.datatransfer.resolver.gis; /** + * The Interface GeonetworkServiceInterface. + * * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it * @Oct 7, 2014 - * */ public interface GeonetworkServiceInterface { - + /** - * @param scope - * @param geonetworkInstance - * @return + * Gets the geonetwork instance. + * + * @param authenticate the authenticate + * @return the geonetwork instance + * @throws Exception the exception */ public GeonetworkInstance getGeonetworkInstance(boolean authenticate) throws Exception; + /** + * Gets the scope. + * + * @return the scope + */ public String getScope(); } diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/GisResolver.java b/src/main/java/org/gcube/datatransfer/resolver/gis/GisResolver.java index aff7300..3df4097 100644 --- a/src/main/java/org/gcube/datatransfer/resolver/gis/GisResolver.java +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/GisResolver.java @@ -200,7 +200,7 @@ public class GisResolver extends HttpServlet{ return; } - logger.info("SCOPE is: " + gisUUID); + logger.info("SCOPE is: " + scope); try { diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GNAuthentication.java b/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GNAuthentication.java new file mode 100644 index 0000000..7bd339d --- /dev/null +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GNAuthentication.java @@ -0,0 +1,84 @@ +/** + * + */ +package org.gcube.datatransfer.resolver.gis.geonetwork; + +import org.apache.commons.httpclient.HttpStatus; +import org.jdom2.Element; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The Class GNAuthentication. + * + * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it + * Apr 4, 2016 + */ +public class GNAuthentication { + + private static Logger logger = LoggerFactory.getLogger(GNAuthentication.class); + public static final String XML_USER_LOGOUT = "xml.user.logout"; + private static final String SRV_EN_XML_USER_LOGIN = "/srv/en/xml.user.login"; + + /** + * Perform a GN login.
+ * GN auth is carried out via a JSESSIONID cookie returned by a successful login + * call.
+ * + *
    + *
  • Url: http://server:port/geonetwork/srv/en/xml.user.login
  • + *
  • Mime-type: application/xml
  • + *
  • Post request:
    {@code
    +	 *   
    +	 *   
    +	 *       user
    +	 *       pwd
    +	 *   
    +	 * }
  • + *
+ * + * @param connection the connection + * @param serviceURL the service url + * @param username the username + * @param password the password + * @return true, if successful + */ + public static boolean login(HTTPCallsUtils connection, String serviceURL, String username, String password) { + Element request = new Element("request"); + request.addContent(new Element("username").setText(username)); + request.addContent(new Element("password").setText(password)); + + XMLOutputter outputter = new XMLOutputter(Format.getCompactFormat()); + String xml = outputter.outputString(request); + + logger.trace("Authentication on Geonetwork: "+xml); + + String loginURL = serviceURL+SRV_EN_XML_USER_LOGIN; + connection.postXml(loginURL, xml); + + return connection.getLastHttpStatus() == HttpStatus.SC_OK; + } + + + /** + * Logout. + * + * @param connection the connection + * @param serviceURL the service url + * @return true, if successful + */ + public static boolean logout(HTTPCallsUtils connection, String serviceURL) { + Element request = new Element("request"); + + XMLOutputter outputter = new XMLOutputter(Format.getCompactFormat()); + String xml = outputter.outputString(request); + + String logOut = serviceURL+XML_USER_LOGOUT; + connection.postXml(logOut, xml); + + return connection.getLastHttpStatus() == HttpStatus.SC_OK; + } +} \ No newline at end of file diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GeonetworkResolver.java b/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GeonetworkResolver.java new file mode 100644 index 0000000..a8c92e6 --- /dev/null +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/GeonetworkResolver.java @@ -0,0 +1,339 @@ +/** + * + */ +package org.gcube.datatransfer.resolver.gis.geonetwork; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.gcube.datatransfer.resolver.gis.GeoRuntimeReader; +import org.gcube.datatransfer.resolver.gis.GeoRuntimeReader.GEO_SERVICE; +import org.gcube.datatransfer.resolver.gis.GeonetowrkAccessParameter; +import org.gcube.datatransfer.resolver.gis.GeonetworkServiceInterface; +import org.gcube.datatransfer.resolver.gis.MetadataConverter; +import org.gcube.datatransfer.resolver.gis.entity.ServerParameters; +import org.gcube.datatransfer.resolver.gis.exception.GeonetworkInstanceException; +import org.gcube.datatransfer.resolver.gis.exception.IllegalArgumentException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The Class GeonetworkResolver. + * + * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it + * Apr 4, 2016 + * + * This class authenticate all query on Geonetwork + */ +public class GeonetworkResolver extends HttpServlet{ + + /** + * + */ + private static final long serialVersionUID = -61097584153314181L; + public static final String SCOPE = "scope"; + public static final String CSW_SERVER = "srv/en/csw"; + + /** The logger. */ + private static final Logger logger = LoggerFactory.getLogger(GeonetworkResolver.class); + + protected Map cacheGNServerParams; //A cache: scope - geonetwork parameters + + private Timer timer; + + //THIRTY MINUTES + public static final long CACHE_RESET_TIME = 30*60*1000; + + //TEN MINUTES + public static final long CACHE_RESET_DELAY = 10*1000; + + /* (non-Javadoc) + * @see javax.servlet.GenericServlet#init() + */ + @Override + public void init() throws ServletException { + super.init(); + timer = new Timer(true); + timer.schedule(new TimerTask() { + @Override + public void run() { + logger.info("Resetting Geonetwork configuratios cache..."); + reseCacheServerParameters(); + } + }, CACHE_RESET_DELAY, CACHE_RESET_TIME); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String scope = req.getParameter(SCOPE); + + if (scope == null || scope.equals("")) { + logger.debug("Scope not found"); + sendError(resp, HttpServletResponse.SC_BAD_REQUEST, SCOPE+" not found or empty"); + return; + } + + logger.info("SCOPE is: " + scope); + + try { + + ServerParameters geonetworkParams = getGeonetworkCachedServerParameters(scope); + HTTPCallsUtils httpUtils = new HTTPCallsUtils(); + boolean authorized = GNAuthentication.login(httpUtils, geonetworkParams.getUrl(), geonetworkParams.getUser(), geonetworkParams.getPassword()); + logger.trace("Authorized on "+geonetworkParams +" ? "+authorized); + String gnGetlURL = geonetworkParams.getUrl()+"/"+CSW_SERVER+"?"+req.getQueryString(); + logger.info("Sending get request to URL: "+gnGetlURL); + String response = httpUtils.get(geonetworkParams.getUrl()+CSW_SERVER+"?"+req.getQueryString()); + InputStream in = IOUtils.toInputStream(response); + OutputStream out = resp.getOutputStream(); + try{ + int bytes = IOUtils.copy(in, out); + if(bytes==0) + logger.warn("ResponseBody is empty, returning empty resp"); + }catch(Exception e){ + logger.error("Error on copy response:", e); + }finally{ + IOUtils.closeQuietly(in); + } + + } catch (IllegalArgumentException e){ + logger.error("IllegalArgumentException:", e); + sendError(resp, HttpServletResponse.SC_BAD_REQUEST, "Illegal argument to carry out the request!"); + return; + + } catch (Exception e) { + logger.error("Exception:", e); + String error = "Sorry, an error occurred on resolving geonetwork request with scope "+scope+". Please, contact support!"; + sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error); + return; + } + } + + private String getScope(String servletPath){ + String path = servletPath; + logger.debug("Read servlet context: "+path); + String scope = path.substring(path.lastIndexOf("/")+1, path.length()); + return scope.replaceAll("_", "/"); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException { + + String scope = req.getParameter(SCOPE); + + if (scope == null || scope.equals("")) { + logger.debug("Scope not found"); + sendError(resp, HttpServletResponse.SC_BAD_REQUEST, SCOPE+" not found or empty"); + return; + } + + logger.info("SCOPE is: " + scope); + + try { + + ServerParameters geonetworkParams = getGeonetworkCachedServerParameters(scope); + HTTPCallsUtils httpUtils = new HTTPCallsUtils(); + boolean authorized = GNAuthentication.login(httpUtils, geonetworkParams.getUrl(), geonetworkParams.getUser(), geonetworkParams.getPassword()); + logger.trace("Authorized on "+geonetworkParams +" ? "+authorized); + String gnCSWlURL = geonetworkParams.getUrl()+"/"+CSW_SERVER; + logger.info("Sending CSW request to URL: "+gnCSWlURL); + String response = httpUtils.post(gnCSWlURL, req.getInputStream(), req.getContentType()); + InputStream in = IOUtils.toInputStream(response); + OutputStream out = resp.getOutputStream(); + try{ + int bytes = IOUtils.copy(IOUtils.toInputStream(response), out); + if(bytes==0) + logger.warn("ResponseBody is empty, returning empty resp"); + }catch(Exception e){ + logger.error("Error on copy response:", e); + }finally{ + IOUtils.closeQuietly(in); + } + + } catch (IllegalArgumentException e){ + logger.error("IllegalArgumentException:", e); + sendError(resp, HttpServletResponse.SC_BAD_REQUEST, "Illegal argument to carry out the request!"); + return; + + } catch (Exception e) { + logger.error("Exception:", e); + String error = "Sorry, an error occurred on resolving geonetwork request with scope "+scope+". Please, contact support!"; + sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error); + return; + } + } + + /** + * Gets the geonetwork cached server parameters. + * + * @param scope the scope + * @return the geonetwork cached server parameters + * @throws Exception the exception + */ + protected ServerParameters getGeonetworkCachedServerParameters(String scope) throws Exception{ + + if(cacheGNServerParams==null) + reseCacheServerParameters(); + + logger.info("Tentative for recovering geonetwork server parameters from cache with scope: "+scope); + ServerParameters serverParam = cacheGNServerParams.get(scope); + + if(serverParam==null){ + logger.info("Cache having null Geonetwork server parameters, reading from IS.."); + GeoRuntimeReader reader = new GeoRuntimeReader(); + try { + serverParam = reader.retrieveGisParameters(scope, GEO_SERVICE.GEONETWORK); + cacheGNServerParams.put(scope, serverParam); + logger.info("Updated Cache for Geonetwork server parameters! Scope "+scope+" linking "+serverParam); + } catch (Exception e) { + logger.error("An error occurred on reading application profile to "+GEO_SERVICE.GEONETWORK, e); + throw new Exception("Sorry, An error occurred on reading configuration to "+GEO_SERVICE.GEONETWORK); + } + }else + logger.info("Cache gis server param is not null using it"); + + logger.info("returning geonetworkParams "+serverParam); + + return serverParam; + } + + /** + * Rese cache server parameters. + */ + private void reseCacheServerParameters(){ + cacheGNServerParams = new HashMap(); + logger.info("Cache Geonetwork server params reset!"); + } + + /** + * Gets the layer wms request. + * + * @param scope the scope + * @param gisUUID the gis uuid + * @param geonetworkParams the geonetwork params + * @return the layer wms request + * @throws Exception the exception + */ + protected String getLayerWmsRequest(String scope, String gisUUID, ServerParameters geonetworkParams) throws Exception{ + + try { + GeonetworkServiceInterface gntwAccess = new GeonetowrkAccessParameter(scope, geonetworkParams); + return MetadataConverter.getWMSOnLineResource(gntwAccess.getGeonetworkInstance(true), gisUUID); + }catch (GeonetworkInstanceException e){ + logger.error("An error occurred when instancing geonetowrk gis layer with UUID "+gisUUID, e); + throw new IllegalArgumentException("Sorry, An error occurred when instancing geonetwork with UUID: "+gisUUID); + } catch (Exception e) { + logger.error("An error occurred when retrieving gis layer with UUID "+gisUUID, e); + throw new IllegalArgumentException("Sorry, An error occurred when retrieving gis layer with UUID "+gisUUID); + } + } + + /** + * Send error. + * + * @param response the response + * @param status the status + * @param message the message + * @throws IOException Signals that an I/O exception has occurred. + */ + protected void sendError(HttpServletResponse response, int status, String message) throws IOException + { +// response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setStatus(status); + logger.info("error message: "+message); + logger.info("writing response..."); + StringReader sr = new StringReader(message); + IOUtils.copy(sr, response.getOutputStream()); + +// response.getWriter().write(resultMessage.toString()); + logger.info("response writed"); + response.flushBuffer(); + } + + /** + * Url redirect. + * + * @param req the req + * @param response the response + * @param redirectTo the redirect to + * @throws IOException Signals that an I/O exception has occurred. + */ + protected void urlRedirect(HttpServletRequest req, HttpServletResponse response, String redirectTo) throws IOException { + response.sendRedirect(response.encodeRedirectURL(redirectTo)); + return; + } + + /** + * Gets the request url. + * + * @param req the req + * @return the request url + */ + public static String getRequestURL(HttpServletRequest req) { + + String scheme = req.getScheme(); // http + String serverName = req.getServerName(); // hostname.com + int serverPort = req.getServerPort(); // 80 + String contextPath = req.getContextPath(); // /mywebapp + // String servletPath = req.getServletPath(); // /servlet/MyServlet + // String pathInfo = req.getPathInfo(); // /a/b;c=123 + // String queryString = req.getQueryString(); // d=789 + + // Reconstruct original requesting URL + StringBuffer url = new StringBuffer(); + url.append(scheme).append("://").append(serverName); + + if (serverPort != 80 && serverPort != 443) { + url.append(":").append(serverPort); + } + + logger.trace("server: "+url); + logger.trace("omitted contextPath: "+contextPath); + return url.toString(); + } + /* + public static void main(String[] args) { + GisResolver gisResolver = new GisResolver(); + String scope = "/gcube/devsec/devVRE"; + String UUID = "177e1c3c-4a22-4ad9-b015-bfc443d16cb8"; + try { + ServerParameters geonetworkParams = gisResolver.getCachedServerParameters(scope); + String wmsRequest = gisResolver.getLayerWmsRequest(scope, UUID, geonetworkParams); + logger.info("Final url is: " + wmsRequest); + wmsRequest = URLEncoder.encode(wmsRequest, UTF_8); + logger.info("Encoded WMS request is: " + wmsRequest); + String gisPortletUrl = gisResolver.getGisViewerApplicationURL(scope); + logger.info("Gis Viewer Application url is: " + gisPortletUrl); +// logger.info("WmsRequest is: " + wmsRequest); +// wmsRequest = encodeURLWithParamDelimiter(wmsRequest); +// logger.info("Encoded url is: " + wmsRequest); +// wmsRequest = appendParamReplacement(wmsRequest); + gisPortletUrl+="?wmsrequest="+wmsRequest; + + System.out.println(gisPortletUrl); +// urlRedirect(req, resp, gisPortletUrl); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }*/ +} diff --git a/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/HTTPCallsUtils.java b/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/HTTPCallsUtils.java new file mode 100644 index 0000000..52e9e04 --- /dev/null +++ b/src/main/java/org/gcube/datatransfer/resolver/gis/geonetwork/HTTPCallsUtils.java @@ -0,0 +1,634 @@ +/** + * + */ +package org.gcube.datatransfer.resolver.gis.geonetwork; + + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.EntityEnclosingMethod; +import org.apache.commons.httpclient.methods.FileRequestEntity; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.InputStreamRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.RequestEntity; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.jdom2.Document; +import org.jdom2.input.SAXBuilder; + + +/** + * The Class HTTPCallsUtils. + * copied by Geosolution + * + * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it + * Apr 5, 2016 + */ +public class HTTPCallsUtils { + + private static final Logger logger = Logger.getLogger(HTTPCallsUtils.class); + private final String username; + private final String pw; + + /** + * This instance is shared among the various calls, so that the + * state (mainly cookies) will be preserved. + */ + private HttpClient client = new HttpClient(); + + /** + * Some apps may require application/xml, so you can set it to whatever is needed. + */ + private String xmlContentType = "text/xml"; + + private int lastHttpStatus; + private boolean ignoreResponseContentOnSuccess = false; + + /** + * Instantiates a new HTTP utils. + */ + public HTTPCallsUtils() { + this(null, null); + } + + /** + * Instantiates a new HTTP utils. + * + * @param userName the user name + * @param password the password + */ + public HTTPCallsUtils(String userName, String password) { + this.username = userName; + this.pw = password; + } + + /** + * Sets the xml content type. + * + * @param xmlContentType the new xml content type + */ + public void setXmlContentType(String xmlContentType) { + this.xmlContentType = xmlContentType; + } + + /** + * Gets the last http status. + * + * @return the last http status + */ + public int getLastHttpStatus() { + return lastHttpStatus; + } + + /** + * Checks if is ignore response content on success. + * + * @return true, if is ignore response content on success + */ + public boolean isIgnoreResponseContentOnSuccess() { + return ignoreResponseContentOnSuccess; + } + + /** + * Sets the ignore response content on success. + * + * @param ignoreResponseContentOnSuccess the new ignore response content on success + */ + public void setIgnoreResponseContentOnSuccess(boolean ignoreResponseContentOnSuccess) { + this.ignoreResponseContentOnSuccess = ignoreResponseContentOnSuccess; + } + + + /** + * Performs an HTTP GET on the given URL. + * + * @param url The URL where to connect to. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * @throws MalformedURLException the malformed url exception + */ + public String get(String url) throws MalformedURLException { + + GetMethod httpMethod = null; + try { + setAuth(client, url, username, pw); + httpMethod = new GetMethod(url); + client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); + lastHttpStatus = client.executeMethod(httpMethod); + if(lastHttpStatus == HttpStatus.SC_OK) { + InputStream is = httpMethod.getResponseBodyAsStream(); + String response = IOUtils.toString(is); + if(response.trim().length()==0) { // sometime gs rest fails + logger.warn("ResponseBody is empty"); + return null; + } else { + return response; + } + } else { + logger.info("("+lastHttpStatus+") " + HttpStatus.getStatusText(lastHttpStatus) + " -- " + url ); + } + } catch (ConnectException e) { + logger.info("Couldn't connect to ["+url+"]"); + } catch (IOException e) { + logger.info("Error talking to ["+url+"]", e); + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + + return null; + } + + /** + * Performs an HTTP GET on the given URL and return reponse body. + * + * @param url The URL where to connect to. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * @throws MalformedURLException the malformed url exception + */ + public InputStream getBody(String url) throws MalformedURLException { + + GetMethod httpMethod = null; + try { + setAuth(client, url, username, pw); + httpMethod = new GetMethod(url); + client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); + lastHttpStatus = client.executeMethod(httpMethod); + if(lastHttpStatus == HttpStatus.SC_OK) { + return httpMethod.getResponseBodyAsStream(); + } else { + logger.info("("+lastHttpStatus+") " + HttpStatus.getStatusText(lastHttpStatus) + " -- " + url ); + } + } catch (ConnectException e) { + logger.info("Couldn't connect to ["+url+"]"); + } catch (IOException e) { + logger.info("Error talking to ["+url+"]", e); + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + + return null; + } + + //========================================================================== + //=== PUT + //========================================================================== + + + /** + * Gets the client. + * + * @return the client + */ + public HttpClient getClient() { + + return client; + } + + /** + * PUTs a String representing an XML document to the given URL. + * + * @param url The URL where to connect to. + * @param content The XML content to be sent as a String. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String putXml(String url, String content) { + return put(url, content, xmlContentType); + } + + /** + * PUTs a File to the given URL. + * + * @param url The URL where to connect to. + * @param file The File to be sent. + * @param contentType The content-type to advert in the PUT. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String put(String url, File file, String contentType) { + return put(url, new FileRequestEntity(file, contentType)); + } + + /** + * PUTs a String to the given URL. + * + * @param url The URL where to connect to. + * @param content The content to be sent as a String. + * @param contentType The content-type to advert in the PUT. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String put(String url, String content, String contentType) { + try { + return put(url, new StringRequestEntity(content, contentType, null)); + } catch (UnsupportedEncodingException ex) { + logger.error("Cannot PUT " + url, ex); + return null; + } + } + + /** + * Performs a PUT to the given URL. + * + * @param url The URL where to connect to. + * @param requestEntity The request to be sent. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String put(String url, RequestEntity requestEntity) { + return send(new PutMethod(url), url, requestEntity); + } + + //========================================================================== + //=== POST + //========================================================================== + + /** + * POSTs a String representing an XML document to the given URL. + * + * @param url The URL where to connect to. + * @param content The XML content to be sent as a String. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String postXml(String url, String content) { + return post(url, content, xmlContentType); + } + + /** + * POSTs a Stream content representing an XML document to the given URL. + * + * @param url The URL where to connect to. + * @param content The content to be sent as an InputStream. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String postXml(String url, InputStream content) { + return post(url, content, xmlContentType); + } + + /** + * POSTs a File to the given URL. + * + * @param url The URL where to connect to. + * @param file The File to be sent. + * @param contentType The content-type to advert in the POST. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String post(String url, File file, String contentType) { + return post(url, new FileRequestEntity(file, contentType)); + } + + /** + * POSTs a String to the given URL. + * + * @param url The URL where to connect to. + * @param content The content to be sent as a String. + * @param contentType The content-type to advert in the POST. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String post(String url, String content, String contentType) { + try { + return post(url, new StringRequestEntity(content, contentType, null)); + } catch (UnsupportedEncodingException ex) { + logger.error("Cannot POST " + url, ex); + return null; + } + } + + /** + * POSTs a Stream content to the given URL. + * + * @param url The URL where to connect to. + * @param content The content to be sent as an InputStream. + * @param contentType The content-type to advert in the POST. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String post(String url, InputStream content, String contentType) { + return post(url, new InputStreamRequestEntity(content, contentType)); + } + + /** + * Performs a POST to the given URL. + *
Basic auth is used if both username and pw are not null. + * + * @param url The URL where to connect to. + * @param requestEntity The request to be sent. + * @return The HTTP response as a String if the HTTP response code was 200 (OK). + * the HTTP response or null on errors. + */ + public String post(String url, RequestEntity requestEntity) { + return send(new PostMethod(url), url, requestEntity); + } + + //========================================================================== + //=== HTTP requests + //========================================================================== + + /** + * Send an HTTP request (PUT or POST) to a server. + *
Basic auth is used if both username and pw are not null. + *

+ * Only

    + *
  • 200: OK
  • + *
  • 201: ACCEPTED
  • + *
  • 202: CREATED
  • + *
are accepted as successful codes; in these cases the response string will be returned. + * + * @param httpMethod the http method + * @param url the url + * @param requestEntity the request entity + * @return the HTTP response or null on errors. + */ + protected String send(final EntityEnclosingMethod httpMethod, String url, RequestEntity requestEntity) { + + try { + setAuth(client, url, username, pw); + + client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); + if(requestEntity != null) + httpMethod.setRequestEntity(requestEntity); + + lastHttpStatus = client.executeMethod(httpMethod); + + switch(lastHttpStatus) { + case HttpURLConnection.HTTP_OK: + case HttpURLConnection.HTTP_CREATED: + case HttpURLConnection.HTTP_ACCEPTED: + if(logger.isDebugEnabled()) + logger.debug("HTTP "+ httpMethod.getStatusText() + " <-- " + url); + if(ignoreResponseContentOnSuccess) + return ""; + String response = IOUtils.toString(httpMethod.getResponseBodyAsStream()); + return response; + default: + String badresponse = IOUtils.toString(httpMethod.getResponseBodyAsStream()); + String message = getGeoNetworkErrorMessage(badresponse); + + logger.warn("Bad response: "+lastHttpStatus + + " " + httpMethod.getStatusText() + + " -- " + httpMethod.getName() + + " " +url + + " : " + + message + ); + if(logger.isDebugEnabled()) + logger.debug("GeoNetwork response:\n"+badresponse); + return null; + } + } catch (ConnectException e) { + logger.info("Couldn't connect to ["+url+"]"); + return null; + } catch (IOException e) { + logger.error("Error talking to " + url + " : " + e.getLocalizedMessage()); + return null; + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + } + + /** + * Send an HTTP request (PUT or POST) to a server. + *
Basic auth is used if both username and pw are not null. + *

+ * Only

    + *
  • 200: OK
  • + *
  • 201: ACCEPTED
  • + *
  • 202: CREATED
  • + *
are accepted as successful codes; in these cases the response string will be returned. + * + * @param httpMethod the http method + * @param url the url + * @param requestEntity the request entity + * @return the HTTP response or null on errors. + */ + protected InputStream sendPost(final EntityEnclosingMethod httpMethod, String url, RequestEntity requestEntity) { + + try { + setAuth(client, url, username, pw); + + client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); + if(requestEntity != null) + httpMethod.setRequestEntity(requestEntity); + + lastHttpStatus = client.executeMethod(httpMethod); + + switch(lastHttpStatus) { + case HttpURLConnection.HTTP_OK: + case HttpURLConnection.HTTP_CREATED: + case HttpURLConnection.HTTP_ACCEPTED: + if(logger.isDebugEnabled()) + logger.debug("HTTP "+ httpMethod.getStatusText() + " <-- " + url); + return httpMethod.getResponseBodyAsStream(); + default: + logger.warn("Bad response: "+lastHttpStatus + + " " + httpMethod.getStatusText() + + " -- " + httpMethod.getName() + + " " +url + ); + return httpMethod.getResponseBodyAsStream(); + } + } catch (ConnectException e) { + logger.info("Couldn't connect to ["+url+"]"); + return null; + } catch (IOException e) { + logger.error("Error talking to " + url + " : " + e.getLocalizedMessage()); + return null; + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + } + + /** + * Delete. + * + * @param url the url + * @return true, if successful + */ + public boolean delete(String url) { + + DeleteMethod httpMethod = null; + + try { +// HttpClient client = new HttpClient(); + setAuth(client, url, username, pw); + httpMethod = new DeleteMethod(url); + client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); + lastHttpStatus = client.executeMethod(httpMethod); + String response = ""; + if(lastHttpStatus == HttpStatus.SC_OK) { + if(logger.isDebugEnabled()) + logger.debug("("+lastHttpStatus+") " + httpMethod.getStatusText() + " -- " + url ); + + if( ! ignoreResponseContentOnSuccess) { + InputStream is = httpMethod.getResponseBodyAsStream(); + response = IOUtils.toString(is); + if(response.trim().equals("")) { + if(logger.isDebugEnabled()) + logger.debug("ResponseBody is empty (this may be not an error since we just performed a DELETE call)"); + } + } + return true; + } else { + logger.info("("+lastHttpStatus+") " + httpMethod.getStatusText() + " -- " + url ); + logger.info("Response: '"+response+"'" ); + } + } catch (ConnectException e) { + logger.info("Couldn't connect to ["+url+"]"); + } catch (IOException e) { + logger.info("Error talking to ["+url+"]", e); + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + + return false; + } + + /** + * Http ping. + * + * @param url the url + * @return true if the server response was an HTTP_OK + */ + public boolean httpPing(String url) { + + GetMethod httpMethod = null; + + try { +// HttpClient client = new HttpClient(); + setAuth(client, url, username, pw); + httpMethod = new GetMethod(url); + client.getHttpConnectionManager().getParams().setConnectionTimeout(2000); + lastHttpStatus = client.executeMethod(httpMethod); + if(lastHttpStatus != HttpStatus.SC_OK) { + logger.warn("PING failed at '"+url+"': ("+lastHttpStatus+") " + httpMethod.getStatusText()); + return false; + } else { + return true; + } + + } catch (ConnectException e) { + return false; + } catch (IOException e) { + e.printStackTrace(); + return false; + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + } + + /** + * Used to query for REST resources. + * + * @param url The URL of the REST resource to query about. + * @return true on 200, false on 404. + */ + public boolean exists(String url) { + + GetMethod httpMethod = null; + + try { +// HttpClient client = new HttpClient(); + setAuth(client, url, username, pw); + httpMethod = new GetMethod(url); + client.getHttpConnectionManager().getParams().setConnectionTimeout(2000); + lastHttpStatus = client.executeMethod(httpMethod); + switch(lastHttpStatus) { + case HttpStatus.SC_OK: + return true; + case HttpStatus.SC_NOT_FOUND: + return false; + default: + throw new RuntimeException("Unhandled response status at '"+url+"': ("+lastHttpStatus+") " + httpMethod.getStatusText()); + } + } catch (ConnectException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if(httpMethod != null) + httpMethod.releaseConnection(); + } + } + + + /** + * Sets the auth. + * + * @param client the client + * @param url the url + * @param username the username + * @param pw the pw + * @throws MalformedURLException the malformed url exception + */ + private static void setAuth(HttpClient client, String url, String username, String pw) throws MalformedURLException { + URL u = new URL(url); + if(username != null && pw != null) { + Credentials defaultcreds = new UsernamePasswordCredentials(username, pw); + client.getState().setCredentials(new AuthScope(u.getHost(), u.getPort()), defaultcreds); + client.getParams().setAuthenticationPreemptive(true); // GS2 by default always requires authentication + } else { + if(logger.isTraceEnabled()) { + logger.trace("Not setting credentials to access to " + url); + } + } + } + + /** + * Gets the geo network error message. + * + * @param msg the msg + * @return the geo network error message + */ + protected static String getGeoNetworkErrorMessage(String msg) { + try { + SAXBuilder builder = new SAXBuilder(); + Document error = builder.build(new StringReader(msg)); + return error.getRootElement().getChildText("message"); + } catch (Exception ex) { + return "-"; + } + } + + /** + * Gets the geo network error message. + * + * @param msg the msg + * @return the geo network error message + */ + protected static String getGeoNetworkErrorMessage(InputStream msg) { + try { + SAXBuilder builder = new SAXBuilder(); + Document error = builder.build(msg); + return error.getRootElement().getChildText("message"); + } catch (Exception ex) { + return "-"; + } + } + +} diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index f98bdd6..9a568a6 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -34,6 +34,13 @@ org.gcube.datatransfer.resolver.gis.GisResolver 1 + + + geonetwork + geonetwork + org.gcube.datatransfer.resolver.gis.geonetwork.GeonetworkResolver + 1 + smp @@ -50,4 +57,9 @@ /id + + geonetwork + /geonetwork + +