/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.lens.client; import static org.apache.lens.client.LensClientConfig.*; import java.net.ConnectException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.lens.api.APIResult; import org.apache.lens.api.LensSessionHandle; import org.apache.lens.api.StringList; import org.apache.lens.api.util.MoxyJsonConfigurationContextResolver; import org.apache.lens.client.exceptions.LensClientException; import org.apache.lens.client.exceptions.LensClientServerConnectionException; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.moxy.json.MoxyJsonFeature; import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** * Top level client connection class which is used to connect to a lens server. */ @Slf4j public class LensConnection implements AutoCloseable { /** The params. */ private final LensConnectionParams params; /** The open. */ private AtomicBoolean open = new AtomicBoolean(false); /** The session handle. */ @Getter private LensSessionHandle sessionHandle; private boolean closed = false; /** * Construct a connection to lens server specified by connection parameters. * * @param params parameters to be used for creating a connection */ public LensConnection(LensConnectionParams params) { this.params = params; } /** * Construct a connection to lens server specified by connection parameters with an already established session * * @param params parameters to be used for creating a connection */ public LensConnection(LensConnectionParams params, LensSessionHandle sessionHandle) { this.params = params; this.sessionHandle = sessionHandle; } /** * Check if the connection is opened. Please note that,lens connections are persistent connections. But a session * mapped by ID running on the lens server. * * @return true if connected to server */ public boolean isOpen() { return open.get(); } /** * Gets the session web target. * * @param client the client * @return the session web target */ private WebTarget getSessionWebTarget(Client client) { return client.target(params.getBaseConnectionUrl()).path(params.getSessionResourcePath()); } /** * Gets the metastore web target. * * @param client the client * @return the metastore web target */ private WebTarget getMetastoreWebTarget(Client client) { return client.target(params.getBaseConnectionUrl()).path(params.getMetastoreResourcePath()); } public Client buildClient() { ClientBuilder cb = ClientBuilder.newBuilder().register(MultiPartFeature.class).register(MoxyJsonFeature.class) .register(MoxyJsonConfigurationContextResolver.class); for (Class<?> aClass : params.getRequestFilters()) { cb.register(aClass); } Client client = cb.build(); //Set Timeouts LensClientConfig config = params.getConf(); client.property(ClientProperties.CONNECT_TIMEOUT, config.getInt(CONNECTION_TIMEOUT_MILLIS, DEFAULT_CONNECTION_TIMEOUT_MILLIS)); client.property(ClientProperties.READ_TIMEOUT, config.getInt(READ_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT_MILLIS)); return client; } private WebTarget getSessionWebTarget() { return getSessionWebTarget(buildClient()); } private WebTarget getMetastoreWebTarget() { return getMetastoreWebTarget(buildClient()); } public WebTarget getLogWebTarget() { Client client = buildClient(); return getLogWebTarget(client); } public WebTarget getLogWebTarget(Client client) { return client.target(params.getBaseConnectionUrl()).path(params.getLogResourcePath()); } /** * Open. * * @param password the password * @return the lens session handle */ public LensSessionHandle open(String password) { WebTarget target = getSessionWebTarget(); FormDataMultiPart mp = new FormDataMultiPart(); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("username").build(), params.getUser())); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("password").build(), password)); String database = params.getDbName(); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("database").build(), database)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("sessionconf").fileName("sessionconf").build(), params.getSessionConf(), MediaType.APPLICATION_XML_TYPE)); try { Response response = target.request().post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE)); if (response.getStatus() != 200) { throw new LensClientServerConnectionException(response.getStatus()); } final LensSessionHandle handle = response.readEntity(LensSessionHandle.class); if (handle != null) { sessionHandle = handle; log.debug("Created a new session {}", sessionHandle.getPublicId()); } else { throw new IllegalStateException("Unable to connect to lens " + "server with following paramters" + params); } } catch (ProcessingException e) { if (e.getCause() != null && e.getCause() instanceof ConnectException) { throw new LensClientServerConnectionException(e.getCause().getMessage(), e); } } log.debug("Successfully switched to database {}", params.getDbName()); open.set(true); return sessionHandle; } /** * Attach database to session. * * @return the API result */ public APIResult attachDatabaseToSession() { WebTarget target = getMetastoreWebTarget(); return target.path("databases").path("current").queryParam("sessionid", this.sessionHandle) .request(MediaType.APPLICATION_XML_TYPE).put(Entity.xml(params.getDbName()), APIResult.class); } /** * Close the connection. */ @Override public synchronized void close() { if (closed) { log.warn("Session already closed. Ignoring the attempt to close again."); return; } WebTarget target = getSessionWebTarget(); Response response = target.queryParam("sessionid", this.sessionHandle).request().delete(); if (response == null) { // Should never come here, just fool-proofing throw new LensClientException("Null response from server while closing connection."); } switch(response.getStatus()){ case 410: log.warn("Session is already gone. Ignoring the attempt to close again."); break; case 200: APIResult apiResult = response.readEntity(APIResult.class); if (apiResult.getStatus() != APIResult.Status.SUCCEEDED) { throw new LensClientException("Error closing lens connection: " + apiResult.getMessage()); } break; default: throw new LensClientException("Couldn't close session, error code: " + response.getStatus()); } closed = true; } /** * Adds the resource to connection. * * @param type the type * @param resourcePath the resource path * @return the API result */ public APIResult addResourceToConnection(String type, String resourcePath) { WebTarget target = getSessionWebTarget(); FormDataMultiPart mp = new FormDataMultiPart(); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("sessionid").build(), this.sessionHandle, MediaType.APPLICATION_XML_TYPE)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("type").build(), type)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("path").build(), resourcePath)); return target.path("resources/add").request() .put(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), APIResult.class); } /** * Removes the resource from connection. * * @param type the type * @param resourcePath the resource path * @return the API result */ public APIResult removeResourceFromConnection(String type, String resourcePath) { WebTarget target = getSessionWebTarget(); FormDataMultiPart mp = new FormDataMultiPart(); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("sessionid").build(), this.sessionHandle, MediaType.APPLICATION_XML_TYPE)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("type").build(), type)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("path").build(), resourcePath)); return target.path("resources/delete").request() .put(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), APIResult.class); } /** * List resources from session * * @param type type of resource * @return List of resources */ public List<String> listResourcesFromConnection(String type) { WebTarget target = getSessionWebTarget(); StringList result = target.path("resources/list").queryParam("sessionid", this.sessionHandle) .queryParam("type", type).request().get(StringList.class); return result.getElements(); } /** * get the logs for a given log file * * @param logFile log segregation */ public Response getLogs(String logFile) { WebTarget target = getLogWebTarget(); return target.path(logFile).request(MediaType.APPLICATION_OCTET_STREAM).get(); } /** * Sets the connection params. * * @param key the key * @param value the value * @return the API result */ public APIResult setConnectionParams(String key, String value) { WebTarget target = getSessionWebTarget(); FormDataMultiPart mp = new FormDataMultiPart(); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("sessionid").build(), this.sessionHandle, MediaType.APPLICATION_XML_TYPE)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("key").build(), key)); mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("value").build(), value)); log.debug("Setting connection params {}={}", key, value); return target.path("params").request() .put(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), APIResult.class); } public List<String> getConnectionParams() { WebTarget target = getSessionWebTarget(); StringList list = target.path("params").queryParam("sessionid", this.sessionHandle).queryParam("verbose", true) .request().get(StringList.class); return list.getElements(); } /** * Gets the connection params. * * @param key the key * @return the connection params */ public List<String> getConnectionParams(String key) { WebTarget target = getSessionWebTarget(); StringList value = target.path("params").queryParam("sessionid", this.sessionHandle).queryParam("key", key) .request().get(StringList.class); return value.getElements(); } public Map<String, String> getConnectionParamsAsMap() { List<String> params = getConnectionParams(); Map<String, String> paramsMap = new HashMap<>(params.size()); String[] paramKeyAndValue; for (String param : params) { paramKeyAndValue = param.split("="); if (paramKeyAndValue.length == 2) { paramsMap.put(paramKeyAndValue[0], paramKeyAndValue[1]); } } return paramsMap; } public LensConnectionParams getLensConnectionParams() { return this.params; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "LensConnection{" + "sessionHandle=" + sessionHandle.getPublicId() + '}'; } }