package org.docear.plugin.services.features.io; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.NoRouteToHostException; import java.net.Proxy; import java.net.Proxy.Type; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import javax.swing.SwingUtilities; import javax.ws.rs.core.MultivaluedMap; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.io.IOExceptionWithCause; import org.docear.plugin.core.DocearController; import org.docear.plugin.core.io.ProgressInputStream; import org.docear.plugin.services.ADocearServiceFeature; import org.docear.plugin.services.DocearServiceException; import org.freeplane.core.util.LogUtils; import org.freeplane.core.util.TextUtils; import org.freeplane.features.mode.ModeController; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse.Status; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource.Builder; import com.sun.jersey.client.apache.ApacheHttpClient; import com.sun.jersey.client.apache.config.DefaultApacheHttpClientConfig; import com.sun.jersey.core.util.StringKeyStringValueIgnoreCaseMultivaluedMap; import com.sun.jersey.multipart.impl.MultiPartWriter; public class DocearConnectionProvider extends ADocearServiceFeature { public static final int CONNECTION_TIMEOUT = 10000; private Object proxyAuthentication = new Object(); private static ApacheHttpClient client = ApacheHttpClient.create(); static { updateClientConfiguration(true); } private Map<String, String> headerMap = new LinkedHashMap<String, String>(); /*********************************************************************************** * CONSTRUCTORS **********************************************************************************/ /*********************************************************************************** * METHODS **********************************************************************************/ protected WebResource getWebResource(URI uri) { synchronized (client) { WebResource resource = client.resource(uri); return resource; } } public WebResource getServiceResource() { WebResource resource = client.resource(getOnlineServiceUri()); return resource; } private static InputStream getErrorMessageInputStream(ClientResponse response) { Status status = response.getClientResponseStatus(); InputStream is = response.getEntityInputStream(); if(status != null) { // rewrite http server error messages if(status.getStatusCode() >= 500 || status.getStatusCode() == 408 || status.getStatusCode() == 413 || status.getStatusCode() == 414) { is = new ByteArrayInputStream(TextUtils.getText("docear.service.error.server", "[missing translation]").getBytes()); } else if(status.getStatusCode() == 404) { is = new ByteArrayInputStream(TextUtils.getText("docear.service.error.not_found", "[missing translation]").getBytes()); } } return is; } public URI getOnlineServiceUri() { if (System.getProperty("org.docear.localhost", "false").equals("true")) { return URI.create("http://127.0.0.1:8080/"); } return URI.create("https://api.docear.org/"); } public void setDefaultHeader(String key, String value) { if(key == null) { return; } synchronized (headerMap) { if(value == null) { headerMap.remove(key); } else { headerMap.put(key, value); } } } public String getDefaultHeader(String key) { if(key == null) { return null; } synchronized (headerMap) { return headerMap.get(key); } } public String[] getDefaultHeaderKeys() { synchronized (headerMap) { return headerMap.keySet().toArray(new String[0]); } } public static String getErrorMessageString(ClientResponse response) { try { return readResponseContent(getErrorMessageInputStream(response)); } catch (Exception e) { } return ""; } public static String readResponseContent(InputStream is) throws IOException { int chr; StringBuilder message = new StringBuilder(); while ((chr = is.read()) > -1) { message.append((char) chr); } is.close(); return message.toString(); } private static void updateClientConfiguration(boolean prefChanged) { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(DocearConnectionProvider.class.getClassLoader()); try { DefaultApacheHttpClientConfig cc = new DefaultApacheHttpClientConfig(); if (DocearProxyAuthenticator.useProxyServer()) { String host = DocearProxyAuthenticator.getHost(); String port = DocearProxyAuthenticator.getPort(); String username = DocearProxyAuthenticator.getUsername(); String proxyPassword = DocearProxyAuthenticator.getPassword(); cc.getProperties().put(DefaultApacheHttpClientConfig.PROPERTY_PROXY_URI, "http://" + host + ":" + port + "/"); if (username != null && username.length() > 0 && proxyPassword != null) { try { cc.getState().setProxyCredentials(AuthScope.ANY_REALM, host, Integer.parseInt(port), username, proxyPassword); } catch (NumberFormatException e) { LogUtils.severe(e); } } } synchronized (client) { if (prefChanged) { cc.getClasses().add(MultiPartWriter.class); client = ApacheHttpClient.create(cc); client.setConnectTimeout(CONNECTION_TIMEOUT); client.setReadTimeout(CONNECTION_TIMEOUT*3); } else { client.getProperties().put(DefaultApacheHttpClientConfig.PROPERTY_HTTP_STATE, cc.getState()); } } } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } public DocearServiceResponse put(String path) { return put(path, null); } public DocearServiceResponse put(String path, MultivaluedMap<String, String> params) { try { if (params == null) { params = new StringKeyStringValueIgnoreCaseMultivaluedMap(); } ClientResponse response = put(getServiceResource().path(path).queryParams(params), ClientResponse.class); try { Status status = response.getClientResponseStatus(); if (status != null && status.equals(Status.OK)) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.OK, response.getEntityInputStream()); } else if (status != null && status.equals(Status.NO_CONTENT)) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.NO_CONTENT, response.getEntityInputStream()); } else if (status != null && status.equals(Status.UNAUTHORIZED)) { DocearController.getController().getEventQueue().dispatchEvent(new DocearUnauthorizedExceptionEvent(this)); return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.UNAUTHORIZED, getErrorMessageInputStream(response)); } else { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, getErrorMessageInputStream(response)); } } finally { response.close(); } } catch (ClientHandlerException e) { if (e.getCause() instanceof UnknownHostException || e.getCause() instanceof NoRouteToHostException || e.getCause() instanceof SocketTimeoutException || e.getCause() instanceof ConnectException) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.UNKNOWN_HOST, new ByteArrayInputStream(e.getMessage().getBytes())); } else { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, new ByteArrayInputStream(e.getMessage().getBytes())); } } catch (Exception e) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, new ByteArrayInputStream( e.getMessage().getBytes())); } } protected <T> T put(Builder builder, Class<T> c) throws Exception { if (SwingUtilities.isEventDispatchThread()) { throw new Exception("Never call the webservice from the event dispatch thread."); } synchronized (proxyAuthentication) { try { appendDefaultHeaders(builder); return builder.put(c); } catch (Exception e) { LogUtils.info(e.getCause().toString()); try { if (raiseProxyCredentialsDialog(e)) { return put(builder, c); } else { throw (e); } } catch (Exception ex) { throw (ex); } } finally { client.getClientHandler().getHttpClient().getHttpConnectionManager().closeIdleConnections(100); } } } protected <T> T put(WebResource webResource, Class<T> c) throws Exception { return put(webResource.getRequestBuilder(), c); } public DocearServiceResponse post(String path, MultivaluedMap<String, String> params) { try { if (params == null) { params = new StringKeyStringValueIgnoreCaseMultivaluedMap(); } ClientResponse response = post(getServiceResource().path(path), params); try { Status status = response.getClientResponseStatus(); if (status != null && status.equals(Status.OK)) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.OK, response.getEntityInputStream()); } else if (status != null && status.equals(Status.NO_CONTENT)) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.NO_CONTENT, response.getEntityInputStream()); } else if (status != null && status.equals(Status.UNAUTHORIZED)) { DocearController.getController().getEventQueue().dispatchEvent(new DocearUnauthorizedExceptionEvent(this)); return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.UNAUTHORIZED, getErrorMessageInputStream(response)); } else { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, getErrorMessageInputStream(response)); } } finally { response.close(); } } catch (ClientHandlerException e) { if (e.getCause() instanceof UnknownHostException || e.getCause() instanceof NoRouteToHostException || e.getCause() instanceof SocketTimeoutException || e.getCause() instanceof ConnectException) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.UNKNOWN_HOST, new ByteArrayInputStream(e.getMessage().getBytes())); } else { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, new ByteArrayInputStream(e.getMessage().getBytes())); } } catch (Exception e) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, new ByteArrayInputStream( e.getMessage().getBytes())); } } public ClientResponse post(WebResource webResource, Object requestEntity) throws Exception { return post(webResource.getRequestBuilder(), requestEntity); } public ClientResponse post(Builder builder, Object requestEntity) throws Exception { if (SwingUtilities.isEventDispatchThread()) { throw new Exception("Never call the webservice from the event dispatch thread."); } synchronized (proxyAuthentication) { try { appendDefaultHeaders(builder); ClientResponse response = builder.post(ClientResponse.class, requestEntity); try { Status status = response.getClientResponseStatus(); if (status != null && status.equals(Status.UNAUTHORIZED)) { DocearController.getController().getEventQueue().dispatchEvent(new DocearUnauthorizedExceptionEvent(this)); } } catch (Exception e) { } return response; } catch (Exception e) { LogUtils.info(e.getCause().toString()); try { if (raiseProxyCredentialsDialog(e)) { return post(builder, requestEntity); } else { throw (e); } } catch (Exception ex) { throw (ex); } } finally { client.getClientHandler().getHttpClient().getHttpConnectionManager().closeIdleConnections(200); } } } public DocearServiceResponse get(String path) { return get(path, null); } public DocearServiceResponse get(String path, MultivaluedMap<String, String> params) { try { if (params == null) { params = new StringKeyStringValueIgnoreCaseMultivaluedMap(); } ClientResponse response = get(getServiceResource().path(path).queryParams(params), ClientResponse.class); try { Status status = response.getClientResponseStatus(); if (status != null && status.equals(Status.OK)) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.OK, response.getEntityInputStream()); } else if (status != null && status.equals(Status.NO_CONTENT)) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.NO_CONTENT, response.getEntityInputStream()); } else if (status != null && status.equals(Status.UNAUTHORIZED)) { DocearController.getController().getEventQueue().dispatchEvent(new DocearUnauthorizedExceptionEvent(this)); return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.UNAUTHORIZED, getErrorMessageInputStream(response)); } else { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, getErrorMessageInputStream(response)); } } finally { response.close(); } } catch (ClientHandlerException e) { if (e.getCause() instanceof UnknownHostException || e.getCause() instanceof NoRouteToHostException || e.getCause() instanceof SocketTimeoutException || e.getCause() instanceof ConnectException) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.UNKNOWN_HOST, new ByteArrayInputStream(e.getMessage().getBytes())); } else { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, new ByteArrayInputStream(e.getMessage().getBytes())); } } catch (Exception e) { return new DocearServiceResponse(org.docear.plugin.services.features.io.DocearServiceResponse.Status.FAILURE, new ByteArrayInputStream( e.getMessage().getBytes())); } } protected <T> T get(WebResource webResource, Class<T> c) throws Exception { return get(webResource.getRequestBuilder(), c); } protected <T> T get(Builder builder, Class<T> c) throws Exception { if (SwingUtilities.isEventDispatchThread()) { throw new IOException("Never call the webservice from the event dispatch thread."); } synchronized (proxyAuthentication) { try { appendDefaultHeaders(builder); return builder.get(c); } catch (Exception e) { LogUtils.info(e.getCause().toString()); try { if (raiseProxyCredentialsDialog(e)) { return get(builder, c); } else { throw (e); } } catch (Exception ex) { throw (ex); } } finally { client.getClientHandler().getHttpClient().getHttpConnectionManager().closeIdleConnections(100); } } } private void appendDefaultHeaders(Builder builder) { synchronized (headerMap) { for(Entry<String, String> entry : headerMap.entrySet()) { builder.header(entry.getKey(), entry.getValue()); } } } private boolean raiseProxyCredentialsDialog(Exception e) throws Exception { return false; //does not work currently // if (e instanceof ClientHandlerException // && e.getCause() != null // && e.getCause().getCause() != null // && e.getCause().getCause() instanceof IOException // && DocearProxyAuthenticator.useProxyServer()) { // // if(!DocearProxyAuthenticator.requestAuthenticationData()) { // throw e; // } // return true; // } // return false; } public InputStream getDownloadStream(URI uri) throws IOException, DocearServiceException { URL url = uri.toURL(); InputStream inStream = null; int length = 0; if("ftp".equals(url.getProtocol())) { URLConnection conn; if(DocearProxyAuthenticator.useProxyServer()) { String host = DocearProxyAuthenticator.getHost(); String port = DocearProxyAuthenticator.getPort(); if(port == null) { port = "1080"; //SOCKS DEFAULT } conn = url.openConnection(new Proxy(Type.HTTP, new InetSocketAddress(host, Integer.parseInt(port)))); } else { conn = url.openConnection(); } inStream = conn.getInputStream(); length = inStream.available()-1; } else { try { WebResource webResource = getWebResource(uri); ClientResponse response = get(webResource, ClientResponse.class); inStream = response.getEntityInputStream(); length = response.getLength(); } catch (Exception e) { throw new IOExceptionWithCause(e); } } return new ProgressInputStream(inStream, uri.toURL(), length); } @Override protected void installDefaults(ModeController modeController) { } @Override public void shutdown() { } /*********************************************************************************** * REQUIRED METHODS FOR INTERFACES **********************************************************************************/ }