/******************************************************************************* * Copyright (c) 2012, 2016, 2017 PDT Extension Group and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * PDT Extension Group - initial API and implementation * Kaloyan Raev - [501269] externalize strings *******************************************************************************/ package org.eclipse.php.composer.api.packages; import java.net.ProxySelector; import java.net.URI; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpClientConnection; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContextBuilder; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.SystemDefaultRoutePlanner; import org.apache.http.protocol.HttpRequestExecutor; import org.apache.http.util.EntityUtils; public class AsyncDownloader extends AbstractDownloader { public final static int TIMEOUT = 30; private int lastSlot = 1; private Log log = LogFactory.getLog(AsyncDownloader.class); private PoolingHttpClientConnectionManager connectionManager; private Map<Integer, Connection> connections = new HashMap<Integer, Connection>(); private class Connection implements Runnable { private String url; private Thread thread; public Connection(String url) { super(); this.url = url; } public void closed() { abortListeners(url); } @Override public void run() { if (Thread.currentThread().isInterrupted()) { closed(); return; } try { URI uri = URI.create(url); final HttpGet httpGet = new HttpGet(uri); httpGet.addHeader("Accept", "*/*"); //$NON-NLS-1$ //$NON-NLS-2$ httpGet.addHeader("User-Agent", getClass().getName()); //$NON-NLS-1$ httpGet.addHeader("Host", uri.getHost()); //$NON-NLS-1$ HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); SystemDefaultRoutePlanner planner = new SystemDefaultRoutePlanner(ProxySelector.getDefault()); HttpClientContext context = HttpClientContext.create(); HttpRoute route = planner.determineRoute(host, httpGet, context); ConnectionRequest connRequest = connectionManager.requestConnection(route, null); HttpClientConnection conn = connRequest.get(TIMEOUT, TimeUnit.SECONDS); try { if (Thread.currentThread().isInterrupted()) { closed(); return; } connectionManager.connect(conn, route, 1000, context); if (Thread.currentThread().isInterrupted()) { closed(); return; } connectionManager.routeComplete(conn, route, context); if (Thread.currentThread().isInterrupted()) { closed(); return; } HttpRequestExecutor exeRequest = new HttpRequestExecutor(); context.setTargetHost(host); HttpResponse response = exeRequest.execute(httpGet, conn, context); if (response.getStatusLine().getStatusCode() >= 300) { throw new HttpResponseException(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); } HttpEntity entity = response.getEntity(); if (Thread.currentThread().isInterrupted()) { closed(); return; } if (entity == null) { throw new ClientProtocolException("Response contains no content"); //$NON-NLS-1$ } try { synchronized (AsyncDownloader.this) { for (DownloadListenerInterface listener : listeners) { try { if (Thread.currentThread().isInterrupted()) { closed(); return; } listener.dataReceived(response.getEntity().getContent(), httpGet.getURI().toString()); } catch (Exception e) { listener.errorOccured(e); } } } } finally { EntityUtils.consume(entity); } } finally { if (conn != null) { connectionManager.releaseConnection(conn, null, 1, TimeUnit.SECONDS); } } } catch (Exception ex) { synchronized (AsyncDownloader.this) { for (DownloadListenerInterface listener : listeners) { listener.errorOccured(ex); } } } } public void abort() { synchronized (this) { if (thread != null && thread.isAlive()) { thread.interrupt(); } } } public void start() { this.thread = new Thread(this); this.thread.start(); } } public AsyncDownloader() { super(); } public AsyncDownloader(String url) { super(url); } protected void init() { super.init(); try { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(builder.build()); Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", ssf).build(); //$NON-NLS-1$ //$NON-NLS-2$ connectionManager = new PoolingHttpClientConnectionManager(r); } catch (NoSuchAlgorithmException e) { log.error("Exception during init", e); //$NON-NLS-1$ } catch (KeyManagementException e) { log.error("Exception during init", e); //$NON-NLS-1$ } catch (KeyStoreException e) { log.error("Exception during init", e); //$NON-NLS-1$ } } /** * Starts the async download. The returned number is the internal slot for * this download transfer, which can be used as parameter in abort to stop * this specific transfer. * * @return slot */ public int download() { try { int slot = ++this.lastSlot; Connection connection = new Connection(url); connection.start(); connections.put(slot, connection); return slot; } catch (Exception e) { for (DownloadListenerInterface listener : listeners) { listener.errorOccured(e); } } return -1; } /** * Aborts the last transfer */ public void abort() { abort(lastSlot); } /** * Aborts a transfer at the given slot * * @param slot */ public void abort(int slot) { try { Connection conn = connections.get(slot); if (conn != null) { conn.abort(); } } catch (Exception e) { log.error(e.getMessage()); } } protected void abortListeners(String url) { synchronized (this) { for (DownloadListenerInterface listener : listeners) { listener.aborted(url); } } } /** * Shuts down the download client */ public void shutdown() { if (connectionManager != null) { for (Connection conn : connections.values()) { if (conn != null) { conn.abort(); } } connectionManager.shutdown(); } } }