/** * EasySOA * Copyright 2011-2013 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; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; import java.io.File; import java.io.FileInputStream; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; import org.apache.log4j.Logger; import org.easysoa.registry.DocumentService; import org.easysoa.registry.types.ResourceDownloadInfo; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.model.ComponentContext; import org.nuxeo.runtime.model.DefaultComponent; /** * Impl of ResourceDownloadService for Nuxeo components. * * Makes the global ResourceDownloadService available in Nuxeo, * but if not available or timeout at activation, disables it and does instead * a local synchronous download (using HttpDownloaderServiceImpl). * * Default configuration (change it in nxserver/config/easysoa.properties ) : * * ResourceDownloadServiceImpl.delegateResourceDownloadServiceUrl = http://localhost:7080/get * * Reuses a single Jersey client (rather than creating one per download which costs too much). * * TODO delegatedDownload(rdi) should get and return RDI properties other * than file also (using response headers ???), same on delegated remote i.e. FraSCAti Studio side * * TODO LATER better : * test delegate async at start * * @author jguillemotte, mdutoo * */ public class ResourceDownloadServiceImpl extends DefaultComponent implements ResourceDownloadService { private static Logger logger = Logger.getLogger(ResourceDownloadServiceImpl.class); private String delegateResourceDownloadServiceUrl; // caches private Client client = null; private boolean isDelegatedDownloadDisabled = false; @Override public void activate(ComponentContext context) throws Exception { super.activate(context); // Configuration (following ex. SchedulerImpl) // TODO LATER maybe rather as an extension point / contribution ?? try { DocumentService documentService = Framework.getService(DocumentService.class); Properties props = documentService.getProperties(); if (props != null) { delegateResourceDownloadServiceUrl = props.getProperty( "ResourceDownloadServiceImpl.delegateResourceDownloadServiceUrl", "http://localhost:7080/ResourceDownloadServices/get"); } } catch (Exception e) { throw new RuntimeException("Unable to get DocumentService", e); } // Create reusable Jersey client, rather than creating one per download else costs too much // (ex. lots of threads at SignatureParser l. 79 within annotation parsing). // TODO LATER better : called by ResourceUpdate within async using Nuxeo Work client = Client.create(); client.setConnectTimeout(3000); // Set timeout to 3 seconds TODO LATER make it configurable // TODO LATER start async Work that tests delegated download and sets isDelegatedDownloadDisabled } @Override public void deactivate(ComponentContext context) throws Exception { client = null; super.deactivate(context); } @Override public ResourceDownloadInfo get(URL url) throws Exception { File file = null; if (!isDelegatedDownloadDisabled) { // First try : Connect to FraSCAti studio download service try { file = delegatedDownload(url); } catch(Exception ex){ // Error or timeout, try the second donwload method logger.warn("unable to get the resource with delegated downloader, " + "disabling it and trying local downloader !"); isDelegatedDownloadDisabled = true; } } if(file == null){ // If no response : Use local downloader service file = localDownload(url); } return this.setRdi(url, file); } /** * * @param url * @param file * @return */ private ResourceDownloadInfo setRdi(URL url, File file){ ResourceDownloadInfo resourceDownloadInfo = new ResourceDownloadInfoImpl(); // Compute timestamp if(file != null){ Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat(ResourceDownloadInfo.TIMESTAMP_DATETIME_PATTERN); resourceDownloadInfo.setTimestamp(sdf.format(date)); resourceDownloadInfo.setFile(file); String urlString = url.toString(); if(urlString.contains("?callback")){ resourceDownloadInfo.setUrl(urlString.substring(0, urlString.indexOf("?callback"))); // TODO why ? when ? } else { resourceDownloadInfo.setUrl(urlString); } } return resourceDownloadInfo; } @Override public ResourceDownloadInfo get(ResourceDownloadInfo rdi) throws Exception { if (!isDelegatedDownloadDisabled) { // First try : Connect to FraSCAti studio download service try { delegatedDownload(rdi); } catch(Exception ex){ // Error or timeout, try the second donwload method logger.warn("unable to get the resource with delegated downloader, " + "disabling it and trying local downloader !"); isDelegatedDownloadDisabled = true; } } if(rdi.getFile() == null){ rdi = get(new URL(rdi.getUrl())); // TODO LATER use rdi.timestamp in local download cache... } return rdi; } /** * * @param url * @return * @throws Exception */ private File delegatedDownload(URL url) throws Exception { WebResource webResource = client.resource(delegateResourceDownloadServiceUrl); // NB. reuses Jersey client rather than creating one per download else costs too much // (ex. lots of threads at SignatureParser l. 79 within annotation parsing) // TODO LATER better : called by ResourceUpdate within async using Nuxeo Work // TODO for frascati service side : Add a parameter to pass the url String urlString = url.toString(); String urlWithoutCallback = urlString; if(urlString.contains("?callback")){ //String callback = urlString.substring(urlString.indexOf("?callback")); urlWithoutCallback = urlString.substring(0, urlString.indexOf("?callback")); } webResource = webResource.queryParam("fileURL", URLEncoder.encode(urlWithoutCallback, "UTF-8")); // Get the resource File resourceFile = webResource.get(File.class); return resourceFile; } /** * TODO should get and fill RDI properties other than file also * (using response headers ???) * * @param rdi ResourceDownloadInfo * @return * @throws Exception */ private void delegatedDownload(ResourceDownloadInfo rdi) throws Exception { WebResource webResource = client.resource(delegateResourceDownloadServiceUrl); webResource.entity(rdi); // Get the resource File resourceFile = webResource.get(File.class); rdi.setFile(resourceFile); } /** * * @param url * @return * @throws Exception */ private File localDownload(URL url) throws Exception{ if ("file".equals(url.getProtocol())) { // workaround to accept local file URLs // TODO also in actual ResourceDownloadService impl (HTTP Proxy, FraSCAti Studio...) try { return new File(url.toURI()); } catch(URISyntaxException usex) { return new File(url.getPath()); // to accept Windows URLs, see https://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html } } HttpDownloaderService httpDownloaderService = new HttpDownloaderServiceImpl(); HttpDownloader fileDownloader = httpDownloaderService.createHttpDownloader(url); fileDownloader.download(); return fileDownloader.getFile(); } /** * Read data from File * @param file * @return */ @Override public String getData(File file) throws Exception { String data; FileInputStream fis = null; try { if (file == null) { // ex. if site root url returns something else than 200 (ex. 403) return ""; // else cleaner.clean(siteRootFile) throws NullPointerException } fis = new FileInputStream(file); StringBuilder dataBuffer = new StringBuilder(); int c; while ((c = fis.read()) != -1) { dataBuffer.append((char) c); } data = dataBuffer.toString(); } catch (Exception e) { data = null; } finally { if (fis != null) { fis.close(); } } return data; } //@Reference //protected List<UrlPatternWebResourceDownloadService> downloadServices; /* (non-Javadoc) * @see org.easysoa.registry.rest.integration.resources.RoutingWebResourceDownloadService#get(java.net.URL) */ /*@Override @Path("/get/") @GET public File get(URL url, PreferencesManagerItf preferences) throws Exception { // - Sort the list, to have the webResourceDownloadServices in the specified order // Need to be sorted manually because FraSCAti doesn't provide a native way to specify an execution order Collections.sort(downloadServices, new DownloadServicesComparator()); for(UrlPatternWebResourceDownloadService service : downloadServices){ if(service.matchUrl(url)){ // file type is always WSDL ?? File localTempFolder = new File(tempFolder + "tempWsdl"); if(!localTempFolder.exists()){ localTempFolder.mkdir(); } File localWsdlFile = new File(tempFolder + "tempWsdl/temp_" + System.currentTimeMillis() + ".wsdl"); if (!localWsdlFile.createNewFile()) { throw new IOException("Unable to create local WSDL file at " + localWsdlFile.getAbsolutePath()); } // TODO closes ? IOUtils.write(service.get(url), new FileOutputStream(localWsdlFile)); return localWsdlFile; //return Response.ok(service.get(url)).type("text/xml").build(); } } //return Response.serverError().build(); return null; } */ }