/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2013-2016 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.client;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
import static org.apache.sis.util.ArgumentChecks.ensureStrictlyPositive;
import org.apache.sis.util.logging.Logging;
import org.constellation.configuration.AcknowlegementType;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
/**
*
* @author Bernard Fabien (Geomatys)
* @author Benjamin Garcia (Geomatys)
* @author Guilhem Legal (Geomatys)
* @author Johann Sorel (Geomatys)
*/
public class ConstellationClient {
private static final Logger LOGGER = Logging.getLogger("org.constellation.admin.service");
/**
* Jersey client.
*/
private final Client client;
/**
* Constellation server URL.
*/
private final String url;
/**
* Constellation configuration API version.
*/
private final String version;
public final AdminAPI adminApi;
public final CswAPI cswApi;
public final DataAPI dataApi;
public final DataSetAPI datasetApi;
public final MapAPI mapApi;
public final MapContextAPI mapcontextApi;
public final MetadataAPI metadataApi;
public final PortrayalAPI portrayalApi;
public final ProviderAPI providerApi;
public final SensorAPI sensorApi;
public final ServicesAPI servicesApi;
public final SosAPI sosApi;
public final StyleAPI styleApi;
public final TaskAPI taskApi;
public final UserAPI userApi;
public final WpsAPI wpsApi;
/**
* Creates a new client instance ready to communicate with the Constellation server.
* <p>
* Use automatically the latest version.
*
* @param url the constellation server root url
*/
public ConstellationClient(final String url) {
this(url, "1");
}
/**
* Creates a new client instance ready to communicate with the Constellation server.
*
* @param url the constellation server root url
* @param version the constellation configuration API version
*/
public ConstellationClient(final String url, final String version) {
ensureNonNull("url", url);
ensureNonNull("version", version);
// Initialize Jersey client.
final Configuration config = new ClientConfig(NodeReader.class, ParameterValueGroupWriter.class);
this.client = ClientBuilder.newClient(config);
setConnectTimeout(5000);
setReadTimeout(20000);
this.url = url.endsWith("/") ? url : url + "/";
this.version = version;
adminApi = new AdminAPI(this);
cswApi = new CswAPI(this);
dataApi = new DataAPI(this);
datasetApi = new DataSetAPI(this);
mapApi = new MapAPI(this);
mapcontextApi = new MapContextAPI(this);
metadataApi = new MetadataAPI(this);
portrayalApi = new PortrayalAPI(this);
providerApi = new ProviderAPI(this);
sensorApi = new SensorAPI(this);
servicesApi = new ServicesAPI(this);
sosApi = new SosAPI(this);
styleApi = new StyleAPI(this);
taskApi = new TaskAPI(this);
userApi = new UserAPI(this);
wpsApi = new WpsAPI(this);
}
public String getUrl() {
return url;
}
public WebTarget getWebTarget() {
return client.target(url);
}
/**
* Authenticates an user before trying to communicate with the Constellation server.
*
* @param login the user login
* @param password the user password
* @return the {@link ConstellationClient} instance
*/
public ConstellationClient authenticate(final String login, final String password) throws IOException {
ensureNonNull("login", login);
ensureNonNull("password", password);
final String token = TokenAuthenticator.requestToken(url, login, password);
this.client.register(new TokenAuthenticator(token));
return this;
}
/**
* Configures the Jersey {@link Client} read timeout for HTTP communication.
*
* @param timeout the timeout value (in ms)
*/
public void setReadTimeout(final int timeout) {
ensureStrictlyPositive("timeout", timeout);
this.client.property(ClientProperties.READ_TIMEOUT, timeout);
}
/**
* Configures the Jersey {@link Client} connection timeout for HTTP communication.
*
* @param timeout the timeout value (in ms)
*/
public void setConnectTimeout(final int timeout) {
ensureStrictlyPositive("timeout", timeout);
this.client.property(ClientProperties.CONNECT_TIMEOUT, timeout);
}
/**
* Submits a HTTP GET request and returns the response.
*
* @param path the request path
* @param type the submitted/expected media type
* @return the response instance
* @throws IOException on HTTP communication problem like connection or read timeout
*/
ResponseContainer get(final String path, final MediaType type) throws IOException {
try {
return new ResponseContainer(newRequest(path, type).get(Response.class));
} catch (ProcessingException | WebApplicationException ex) {
throw new IOException("An error occurred during HTTP communication with the Constellation server.", ex);
}
}
/**
* Submits a HTTP POST request and returns the response.
*
* @param path the request path
* @param type the submitted/expected media type
* @param body the request entity
* @return the response instance
* @throws IOException on HTTP communication problem like connection or read timeout
*/
ResponseContainer post(final String path, final MediaType type, final Object body) throws IOException {
try {
return new ResponseContainer(newRequest(path, type).post(Entity.entity(body, type), Response.class));
} catch (ProcessingException | WebApplicationException ex) {
throw new IOException("An error occurred during HTTP communication with the Constellation server.", ex);
}
}
/**
* Submits a HTTP PUT request and returns the response.
*
* @param path the request path
* @param type the submitted/expected media type
* @param body the request entity
* @return the response instance
* @throws IOException on HTTP communication problem like connection or read timeout
*/
ResponseContainer put(final String path, final MediaType type, final Object body) throws IOException {
try {
return new ResponseContainer(newRequest(path, type).put(Entity.entity(body, type), Response.class));
} catch (ProcessingException | WebApplicationException ex) {
throw new IOException("An error occurred during HTTP communication with the Constellation server.", ex);
}
}
/**
* Submits a HTTP DELETE request and returns the response.
*
* @param path the request path
* @param type the submitted/expected media type
* @return the response instance
* @throws IOException on HTTP communication problem like connection or read timeout
*/
ResponseContainer delete(final String path, final MediaType type) throws IOException {
try {
return new ResponseContainer(newRequest(path, type).delete(Response.class));
} catch (ProcessingException | WebApplicationException ex) {
throw new IOException("An error occurred during HTTP communication with the Constellation server.", ex);
}
}
/**
* Submits a HTTP DELETE request and returns the response.
*
* @param path the request path
* @param type the submitted/expected media type
* @param paramName parameter send name
* @param paramValue parameter send
* @return the response instance
* @throws IOException on HTTP communication problem like connection or read timeout
*/
ResponseContainer delete(final String path, final MediaType type, String paramName, String paramValue) throws IOException {
try {
return new ResponseContainer(newRequest(path, type, paramName, paramValue).delete(Response.class));
} catch (ProcessingException | WebApplicationException ex) {
throw new IOException("An error occurred during HTTP communication with the Constellation server.", ex);
}
}
/**
* Creates a new request from the specified path and {@link MediaType}.
*
* @param path the request path
* @param type the submitted/expected media type
* @return the response instance
*/
private Invocation.Builder newRequest(final String path, final MediaType type) {
return this.client.target(url + "api/" + version + "/").path(path).request(type);
}
private Invocation.Builder newRequest(final String path, final MediaType type, String paramName, String paramValue) {
return this.client.target(url + "api/" + version + "/").queryParam(paramName, paramValue).path(path).request(type);
}
/**
* {@link Response} wrapper class for specific response handling.
*/
final static class ResponseContainer {
/**
* Wrapped {@link Response} instance.
*/
private final Response response;
/**
* Creates a {@link Response} wrapper instance.
*
* @param response the response to wrap
*/
private ResponseContainer(final Response response) {
ensureNonNull("response", response);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(response.toString());
}
this.response = response;
}
/**
* @see ClientResponse#getEntity()
*
* @param <T> the type of the response
* @param c the type of the entity
* @return an instance of the type {@code c}
* @throws HttpResponseException if the response does not have a {@code 2xx} status code
* @throws IOException if the response entity parsing has failed
*/
public <T> T getEntity(final Class<T> c) throws HttpResponseException, IOException {
ensureNonNull("c", c);
try {
return response.readEntity(c);
} catch (ProcessingException | WebApplicationException ex) {
throw new IOException("Response entity processing has failed.", ex);
} finally {
response.close();
}
}
/**
* Ensures that the response has a "success" status code {@code 2xx}.
*
* @throws HttpResponseException if the response does not have a {@code 2xx} status code
* @throws IOException if the response entity parsing has failed
*/
public void ensure2xxStatus() throws HttpResponseException, IOException {
if (response.getStatus() / 100 != 2) {
final String message;
if (MediaType.TEXT_PLAIN_TYPE.equals(response.getMediaType())) {
message = response.readEntity(String.class);
} else if (MediaType.TEXT_XML_TYPE.equals(response.getMediaType())
|| MediaType.APPLICATION_XML_TYPE.equals(response.getMediaType())
|| MediaType.APPLICATION_JSON_TYPE.equals(response.getMediaType())) {
message = response.readEntity(AcknowlegementType.class).getMessage();
} else {
message = response.toString();
}
throw new HttpResponseException(response.getStatus(), message);
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return response.toString();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
this.client.close();
}
}