All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aarboard.nextcloud.api.utils.ConnectorCommon Maven / Gradle / Ivy

There is a newer version: 13.1.0
Show newest version
package org.aarboard.nextcloud.api.utils;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import javax.net.ssl.SSLContext;

import org.aarboard.nextcloud.api.ServerConfig;
import org.aarboard.nextcloud.api.exception.NextcloudApiException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;

public class ConnectorCommon
{
    private final ServerConfig serverConfig;

    public ConnectorCommon(ServerConfig serverConfig) {
        this.serverConfig = serverConfig;
    }

    public  CompletableFuture executeGet(String part, ResponseParser parser) {
        return executeGet(part, null, parser);
    }

    public  CompletableFuture executeGet(String part, List queryParams, ResponseParser parser) {
        try {
            URI url= buildUrl(part, queryParams, parser instanceof JsonAnswerParser);

            HttpRequestBase request = new HttpGet(url.toString());
            return executeRequest(parser, request);
        } catch (IOException e) {
            throw new NextcloudApiException(e);
        }
    }

    public  CompletableFuture executePost(String part, ResponseParser parser) {
        return executePost(part, null, parser);
    }

    public  CompletableFuture executePost(String part, List postParams, ResponseParser parser) {
        try {
            URI url= buildUrl(part, postParams, parser instanceof JsonAnswerParser);

            HttpRequestBase request = new HttpPost(url.toString());
            return executeRequest(parser, request);
        } catch (IOException e) {
            throw new NextcloudApiException(e);
        }
    }

    public  CompletableFuture executePut(String part1, String part2, ResponseParser parser) {
        return executePut(part1, part2, null, parser);
    }

    public  CompletableFuture executePut(String part1, String part2, List putParams, ResponseParser parser) {
        try {
            URI url= buildUrl(part1 + "/" + part2, putParams, parser instanceof JsonAnswerParser);

            HttpRequestBase request = new HttpPut(url.toString());
            return executeRequest(parser, request);
        } catch (IOException e) {
            throw new NextcloudApiException(e);
        }
    }

    public  CompletableFuture executeDelete(String part1, String part2, ResponseParser parser) {
        return executeDelete(part1, part2, null, parser);
    }

    public  CompletableFuture executeDelete(String part1, String part2, List deleteParams, ResponseParser parser) {
        try {
            URI url= buildUrl(part1 + "/" + part2, deleteParams, parser instanceof JsonAnswerParser);

            HttpRequestBase request = new HttpDelete(url.toString());
            return executeRequest(parser, request);
        } catch (IOException e) {
            throw new NextcloudApiException(e);
        }
    }

    private URI buildUrl(String subPath, List queryParams, boolean useJson) {
    	if(serverConfig.getSubPathPrefix()!=null) {
    		subPath = serverConfig.getSubPathPrefix()+"/"+subPath;
    	}

        if (useJson) {
            if (queryParams == null || queryParams.isEmpty()) {
                queryParams = new ArrayList<>();
            }
            queryParams.add(new BasicNameValuePair("format", "json"));
        }
    	
        URIBuilder uB= new URIBuilder()
        .setScheme(serverConfig.isUseHTTPS() ? "https" : "http")
        .setHost(serverConfig.getServerName())
        .setPort(serverConfig.getPort())
        .setPath(subPath);

        if (serverConfig.getAuthenticationConfig().usesBasicAuthentication()) {
            uB.setUserInfo(serverConfig.getAuthenticationConfig().getLoginName(),
                serverConfig.getAuthenticationConfig().getPassword());
        }

        if (queryParams != null) {
            uB.addParameters(queryParams);
        }

        try {
            return uB.build();
        } catch (URISyntaxException e) {
            throw new NextcloudApiException(e);
        }
    }

    private  CompletableFuture executeRequest(final ResponseParser parser, HttpRequestBase request) throws IOException, ClientProtocolException {
        // https://docs.nextcloud.com/server/14/developer_manual/core/ocs-share-api.html
        request.addHeader("OCS-APIRequest", "true");
        request.addHeader("Content-Type", "application/x-www-form-urlencoded");
        request.setProtocolVersion(HttpVersion.HTTP_1_1);

        HttpClientContext context = prepareContext();

        CompletableFuture futureResponse = new CompletableFuture<>();
        HttpAsyncClientSingleton.getInstance(serverConfig).execute(request, context, new ResponseCallback<>(parser, futureResponse));
        return futureResponse;
    }

    private HttpClientContext prepareContext() {
        if (serverConfig.getAuthenticationConfig().usesBasicAuthentication()) {
            HttpHost targetHost = new HttpHost(serverConfig.getServerName(), serverConfig.getPort(), serverConfig.isUseHTTPS() ? "https" : "http");
            AuthCache authCache = new BasicAuthCache();
            authCache.put(targetHost, new BasicScheme());

            CredentialsProvider credsProvider = new BasicCredentialsProvider();
            UsernamePasswordCredentials credentials
            = new UsernamePasswordCredentials(serverConfig.getAuthenticationConfig().getLoginName(), serverConfig.getAuthenticationConfig().getPassword());
            credsProvider.setCredentials(AuthScope.ANY, credentials);

            // Add AuthCache to the execution context
            HttpClientContext context = HttpClientContext.create();
            context.setCredentialsProvider(credsProvider);
            context.setAuthCache(authCache);
            return context;
        }
        return HttpClientContext.create();
    }

    private final class ResponseCallback implements FutureCallback {
        private final ResponseParser parser;
        private final CompletableFuture futureResponse;

        private ResponseCallback(ResponseParser parser, CompletableFuture futureResponse) {
            this.parser = parser;
            this.futureResponse = futureResponse;
        }

        @Override
        public void completed(HttpResponse response) {
            try {
                R result = handleResponse(parser, response);
                futureResponse.complete(result);
            } catch(Exception ex) {
                futureResponse.completeExceptionally(ex);
            }
        }

        private R handleResponse(ResponseParser parser, HttpResponse response) throws IOException {
            StatusLine statusLine= response.getStatusLine();
            if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    Charset charset = ContentType.getOrDefault(entity).getCharset();
                    Reader reader = new InputStreamReader(entity.getContent(), charset);
                    return parser.parseResponse(reader);
                }
                throw new NextcloudApiException("Empty response received");
            }
            throw new NextcloudApiException(String.format("Request failed with %d %s", statusLine.getStatusCode(), statusLine.getReasonPhrase()));
        }

        @Override
        public void failed(Exception ex) {
            futureResponse.completeExceptionally(ex);
        }

        @Override
        public void cancelled() {
            futureResponse.cancel(true);
        }
    }

	private static class HttpAsyncClientSingleton {
		private static CloseableHttpAsyncClient HTTPC_CLIENT;
		
		private HttpAsyncClientSingleton(){}
		
		public static CloseableHttpAsyncClient getInstance(ServerConfig serverConfig)
			throws IOException{
			if (HTTPC_CLIENT == null) {
				if (serverConfig.isTrustAllCertificates()) {
					try {
						SSLContext sslContext = SSLContexts.custom()
							.loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build();
						HTTPC_CLIENT = HttpAsyncClients.custom()
							.setSSLHostnameVerifier((NoopHostnameVerifier.INSTANCE))
							.setSSLContext(sslContext)
							.build();
					} catch (KeyManagementException | NoSuchAlgorithmException
							| KeyStoreException e) {
						throw new IOException(e);
					} 
					
				} else {
					HTTPC_CLIENT = HttpAsyncClients.createDefault();
				}
				
				HTTPC_CLIENT.start();
			}
			return HTTPC_CLIENT;
		}
		
	}

    public interface ResponseParser {
        R parseResponse(Reader reader);
    }

    /**
     * Close the http client. Required for clean shutdown.
     * @throws IOException error on shutdown
     */
    public static void shutdown() throws IOException {
            if(HttpAsyncClientSingleton.HTTPC_CLIENT != null) {
                HttpAsyncClientSingleton.getInstance(null).close();
            }		
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy