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

org.ojalgo.netio.ServiceClient Maven / Gradle / Ivy

Go to download

oj! Algorithms - ojAlgo - is Open Source Java code that has to do with mathematics, linear algebra and optimisation.

There is a newer version: 55.0.1
Show newest version
/*
 * Copyright 1997-2024 Optimatika
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.ojalgo.netio;

import java.io.InputStream;
import java.net.Authenticator;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpClient.Version;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandler;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;

import org.ojalgo.ProgrammingError;
import org.ojalgo.netio.ResourceLocator.KeyedValues;
import org.ojalgo.netio.ResourceLocator.Method;

/**
 * Make http/https calls.
 * 

* Based on {@link HttpClient} and somewhat designed after how {@link ResourceLocator} works. *

* * @author apete */ public final class ServiceClient { public static final class Request implements BasicLogger.Printable { private Object myBody = null; private final HttpRequest.Builder myBuilder; private final KeyedValues myForm = new KeyedValues(); private ResourceLocator.Method myMethod = ResourceLocator.Method.GET; private transient HttpRequest myRequest = null; private final ResourceSpecification myResourceSpecification; private final ServiceClient.Session mySession; Request(final ServiceClient.Session session) { super(); mySession = session; myBuilder = HttpRequest.newBuilder(); myResourceSpecification = new ResourceSpecification(); this.copy(session.getParameters()); } Request(final ServiceClient.Session session, final URI uri) { super(); mySession = session; myBuilder = HttpRequest.newBuilder(uri); myResourceSpecification = new ResourceSpecification(uri); this.copy(session.getParameters()); } public ServiceClient.Request body(final Object body) { myBody = body; myForm.clear(); myRequest = null; return this; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof Request)) { return false; } Request other = (Request) obj; if (myBody == null) { if (other.myBody != null) { return false; } } else if (!myBody.equals(other.myBody)) { return false; } if (myBuilder == null) { if (other.myBuilder != null) { return false; } } else if (!myBuilder.equals(other.myBuilder)) { return false; } if (!myForm.equals(other.myForm) || (myMethod != other.myMethod)) { return false; } if (myResourceSpecification == null) { if (other.myResourceSpecification != null) { return false; } } else if (!myResourceSpecification.equals(other.myResourceSpecification)) { return false; } if (mySession == null) { if (other.mySession != null) { return false; } } else if (!mySession.equals(other.mySession)) { return false; } return true; } public ServiceClient.Request expectContinue(final boolean enable) { myBuilder.expectContinue(enable); return this; } public ServiceClient.Request form(final String form) { myForm.parse(form); myBody = myForm; myRequest = null; return this; } public ServiceClient.Request form(final String key, final String value) { ProgrammingError.throwIfNull(key); if (value != null) { myForm.put(key, value); } else { myForm.remove(key); } myBody = myForm; myRequest = null; return this; } public ServiceClient.Request fragment(final String fragment) { myResourceSpecification.setFragment(fragment); myRequest = null; return this; } public String getFormValue(final String key) { return myForm.get(key); } public String getQueryValue(final String key) { return myResourceSpecification.getQueryValue(key); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((myBody == null) ? 0 : myBody.hashCode()); result = prime * result + ((myBuilder == null) ? 0 : myBuilder.hashCode()); result = prime * result + myForm.hashCode(); result = prime * result + ((myMethod == null) ? 0 : myMethod.hashCode()); result = prime * result + ((myResourceSpecification == null) ? 0 : myResourceSpecification.hashCode()); return prime * result + ((mySession == null) ? 0 : mySession.hashCode()); } public ServiceClient.Request header(final String name, final String value) { myBuilder.header(name, value); return this; } public ServiceClient.Request host(final String host) { myResourceSpecification.setHost(host); myRequest = null; return this; } public ServiceClient.Request method(final ResourceLocator.Method method) { myMethod = method; myRequest = null; return this; } public ServiceClient.Request method(final String method, final BodyPublisher bodyPublisher) { myBuilder.method(method, bodyPublisher); return this; } public ServiceClient.Request path(final String path) { myResourceSpecification.setPath(path); myRequest = null; return this; } /** * The default (null) value is -1. */ public ServiceClient.Request port(final int port) { myResourceSpecification.setPort(port); myRequest = null; return this; } public void print(final BasicLogger receiver) { mySession.print(receiver); receiver.println("Request URI: {}", this.getURI()); if (myForm.size() > 0) { receiver.println("Request form: {}", myForm); } } public ServiceClient.Request query(final String query) { myResourceSpecification.setQuery(query); myRequest = null; return this; } public ServiceClient.Request query(final String key, final String value) { myResourceSpecification.putQueryEntry(key, value); myRequest = null; return this; } /** * https or http ? */ public ServiceClient.Request secure(final boolean secure) { if (secure) { myResourceSpecification.setScheme("https"); } else { myResourceSpecification.setScheme("http"); } myRequest = null; return this; } public Response send(final BodyHandler responseBodyHandler) { return this.getSession().send(this, responseBodyHandler); } public ServiceClient.Request timeout(final Duration duration) { myBuilder.timeout(duration); return this; } @Override public String toString() { return this.getURI().toString(); } public ServiceClient.Request version(final Version version) { myBuilder.version(version); return this; } private void copy(final KeyedValues sessionParameters) { for (Entry entry : sessionParameters.entrySet()) { myBuilder.setHeader(entry.getKey(), entry.getValue()); } } BodyPublisher body() { if (myBody != null) { Class bodyType = myBody.getClass(); if (byte[].class.isAssignableFrom(bodyType)) { return BodyPublishers.ofByteArray((byte[]) myBody); } else if (InputStream.class.isAssignableFrom(bodyType)) { return BodyPublishers.ofInputStream(() -> (InputStream) myBody); } else { return BodyPublishers.ofString(myBody.toString()); } } else { return BodyPublishers.noBody(); } } HttpRequest getRequest() { if (myRequest == null) { myBuilder.uri(this.getURI()); BodyPublisher body = this.body(); switch (myMethod) { case DELETE: myBuilder.DELETE(); break; case GET: myBuilder.GET(); break; case POST: myBuilder.POST(body); break; case PUT: myBuilder.PUT(body); break; default: myBuilder.method(myMethod.name(), body); break; } myRequest = myBuilder.build(); } return myRequest; } ServiceClient.Session getSession() { return mySession; } URI getURI() { return myResourceSpecification.toURI(); } } /** * This is actually a wrapper of a future response. The only things you can do without waiting/blocking * for the actual response is {@link Response#isDone()} and {@link Response#cancel()}. * * @author apete */ public static final class Response implements BasicLogger.Printable { private final CompletableFuture> myFutureResponse; private final ServiceClient.Session mySession; Response(final ServiceClient.Request request, final CompletableFuture> response) { super(); mySession = request.getSession(); myFutureResponse = response; } public boolean cancel() { return myFutureResponse.cancel(true); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof Response)) { return false; } Response other = (Response) obj; if (!myFutureResponse.equals(other.myFutureResponse)) { return false; } if (mySession == null) { if (other.mySession != null) { return false; } } else if (!mySession.equals(other.mySession)) { return false; } return true; } public T getBody() { return this.getResponse().body(); } public HttpHeaders getHeaders() { return this.getResponse().headers(); } public Optional> getPreviousResponse() { return this.getResponse().previousResponse(); } /** * Will recreate the request that resulted in the final response. If there has been one or more * redirects, then this is NOT the same as the original request. */ public HttpRequest getRequest() { return this.getResponse().request(); } /** * @return The http response status code, or -1 if this is not http/hhtps or if the code cannot be * discerned from the response */ public int getStatusCode() { return this.getResponse().statusCode(); } public URI getURI() { return this.getResponse().uri(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((myFutureResponse == null) ? 0 : myFutureResponse.hashCode()); return prime * result + ((mySession == null) ? 0 : mySession.hashCode()); } public boolean isDone() { return myFutureResponse.isDone(); } /** * @return true if the status (response) code is in [200,300) */ public boolean isResponseOK() { int statusCode = this.getStatusCode(); if ((200 <= statusCode) && (statusCode < 300)) { return true; } else { return false; } } public void print(final BasicLogger receiver) { receiver.println("Response body: {}", this.toString()); receiver.println("Response headers: {}", this.getResponse().headers().map()); receiver.println(""); // TODO this.getRequest().print(receiver); receiver.println(""); mySession.print(receiver); } @Override public String toString() { return myFutureResponse.toString(); } HttpResponse getResponse() { try { return myFutureResponse.get(); } catch (InterruptedException | ExecutionException cause) { throw new RuntimeException(cause); } } } /** * When you need to make a sequence of calls maintaining some state inbetween calls. * * @author apete */ public static final class Session implements BasicLogger.Printable { private final HttpClient myClient; private final CookieManager myCookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL); private final KeyedValues myParameters = new KeyedValues(ResourceLocator.DEFAULTS.getValues()); Session(final HttpClient.Builder builder) { super(); builder.cookieHandler(myCookieManager); myClient = builder.build(); } /** * @see #parameter(String, String) */ public String getParameter(final String key) { return myParameters.get(key); } public Request newRequest() { return new Request(this); } public Request newRequest(final String url) { try { return new Request(this, new URI(url)); } catch (URISyntaxException cause) { throw new ProgrammingError(cause); } } /** * Session parameters are transferred to requests as headers */ public ServiceClient.Session parameter(final String key, final String value) { Objects.requireNonNull(key); if (value != null) { myParameters.put(key, value); } else { myParameters.remove(key); } return this; } public void print(final BasicLogger receiver) { receiver.println("Session parameters: {}", myParameters); receiver.println("Session cookies: {}", myCookieManager.getCookieStore().getCookies()); } HttpClient getClient() { return myClient; } KeyedValues getParameters() { return myParameters; } Response send(final Request request, final BodyHandler responseBodyHandler) { HttpClient client = this.getClient(); CompletableFuture> futureResponse = client.sendAsync(request.getRequest(), responseBodyHandler); return new Response<>(request, futureResponse); } } public static Response get(final String url) { return ServiceClient.newRequest(url).method(Method.GET).send(BodyHandlers.ofString()); } public static Request newRequest() { return ServiceClient.newSession().newRequest(); } public static Request newRequest(final String url) { return ServiceClient.newSession().newRequest(url); } public static ServiceClient.Session newSession() { ServiceClient serviceClient = new ServiceClient(); return serviceClient.getSession(); } public static Response post(final String url, final byte[] body) { return ServiceClient.newRequest(url).method(Method.POST).body(body).send(BodyHandlers.ofString()); } public static Response post(final String url, final KeyedValues body) { return ServiceClient.newRequest(url).method(Method.POST).body(body).send(BodyHandlers.ofString()); } private final HttpClient.Builder myBuilder = HttpClient.newBuilder(); public ServiceClient() { super(); myBuilder.executor(ReaderWriterBuilder.executor()); } public ServiceClient authenticator(final Authenticator authenticator) { myBuilder.authenticator(authenticator); return this; } public ServiceClient connectTimeout(final Duration duration) { myBuilder.connectTimeout(duration); return this; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof ServiceClient)) { return false; } ServiceClient other = (ServiceClient) obj; if (myBuilder == null) { if (other.myBuilder != null) { return false; } } else if (!myBuilder.equals(other.myBuilder)) { return false; } return true; } public ServiceClient executor(final Executor executor) { myBuilder.executor(executor); return this; } public ServiceClient followRedirects(final Redirect policy) { myBuilder.followRedirects(policy); return this; } public ServiceClient.Session getSession() { return new Session(myBuilder); } @Override public int hashCode() { final int prime = 31; int result = 1; return prime * result + ((myBuilder == null) ? 0 : myBuilder.hashCode()); } public ServiceClient priority(final int priority) { myBuilder.priority(priority); return this; } public ServiceClient proxy(final ProxySelector proxySelector) { myBuilder.proxy(proxySelector); return this; } public ServiceClient sslContext(final SSLContext sslContext) { myBuilder.sslContext(sslContext); return this; } public ServiceClient sslParameters(final SSLParameters sslParameters) { myBuilder.sslParameters(sslParameters); return this; } public ServiceClient version(final Version version) { myBuilder.version(version); return this; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy