/*
* Copyright 2015-2016 OpenCB
*
* 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.opencb.opencga.storage.server.rest;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.apache.commons.lang3.StringUtils;
import org.opencb.biodata.models.alignment.Alignment;
import org.opencb.biodata.models.feature.Genotype;
import org.opencb.biodata.models.variant.StudyEntry;
import org.opencb.biodata.models.variant.VariantSource;
import org.opencb.biodata.models.variant.avro.VariantAnnotation;
import org.opencb.biodata.models.variant.stats.VariantStats;
import org.opencb.commons.datastore.core.*;
import org.opencb.opencga.storage.core.StorageEngineFactory;
import org.opencb.opencga.storage.core.alignment.json.AlignmentDifferenceJsonMixin;
import org.opencb.opencga.storage.core.config.StorageConfiguration;
import org.opencb.opencga.storage.core.variant.io.json.mixin.*;
import org.opencb.opencga.storage.server.common.AuthManager;
import org.opencb.opencga.storage.server.common.DefaultAuthManager;
import org.opencb.opencga.storage.server.common.exceptions.NotAuthorizedHostException;
import org.opencb.opencga.storage.server.common.exceptions.NotAuthorizedUserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.*;
import java.io.IOException;
import java.util.*;
/**
* Created by imedina on 23/10/14.
*/
public class GenericRestWebService {
@DefaultValue("json")
@QueryParam("of")
protected String outputFormat;
@DefaultValue("true")
@QueryParam("metadata")
protected Boolean metadata;
@DefaultValue("v1")
@QueryParam("version")
protected String version;
protected final String sessionIp;
protected final UriInfo uriInfo;
private final long startTime;
protected QueryOptions queryOptions;
protected MultivaluedMap<String, String> params;
protected static StorageConfiguration storageConfiguration;
protected static String defaultStorageEngine;
protected static StorageEngineFactory storageEngineFactory;
protected static AuthManager authManager;
protected static Set<String> authorizedHosts;
private static Logger privLogger;
protected Logger logger;
private static ObjectMapper jsonObjectMapper;
private static ObjectWriter jsonObjectWriter;
static {
jsonObjectMapper = new ObjectMapper();
jsonObjectMapper.addMixIn(StudyEntry.class, VariantSourceEntryJsonMixin.class);
jsonObjectMapper.addMixIn(VariantAnnotation.class, VariantAnnotationMixin.class);
jsonObjectMapper.addMixIn(VariantSource.class, VariantSourceJsonMixin.class);
jsonObjectMapper.addMixIn(VariantStats.class, VariantStatsJsonMixin.class);
jsonObjectMapper.addMixIn(Genotype.class, GenotypeJsonMixin.class);
jsonObjectMapper.addMixIn(Alignment.AlignmentDifference.class, AlignmentDifferenceJsonMixin.class);
jsonObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonObjectWriter = jsonObjectMapper.writer();
privLogger = LoggerFactory.getLogger("org.opencb.opencga.storage.server.rest.GenericRestWebService");
}
public GenericRestWebService(@PathParam("version") String version, @Context UriInfo uriInfo,
@Context HttpServletRequest httpServletRequest, @Context ServletContext context) throws IOException {
this.startTime = System.currentTimeMillis();
this.version = version;
this.uriInfo = uriInfo;
this.params = uriInfo.getQueryParameters();
this.queryOptions = new QueryOptions(params, true);
this.sessionIp = httpServletRequest.getRemoteAddr();
logger = LoggerFactory.getLogger(this.getClass());
defaultStorageEngine = storageConfiguration.getDefaultStorageEngineId();
// Only one StorageManagerFactory is needed, this acts as a simple Singleton pattern which improves the performance significantly
if (storageEngineFactory == null) {
privLogger.debug("Creating the StorageManagerFactory object");
// TODO: We will need to pass catalog manager once storage starts doing things over catalog
storageEngineFactory = StorageEngineFactory.get(storageConfiguration);
}
if (authorizedHosts == null) {
privLogger.debug("Creating the authorizedHost HashSet");
authorizedHosts = new HashSet<>(storageConfiguration.getServer().getAuthorizedHosts());
}
if (authManager == null) {
try {
if (StringUtils.isNotEmpty(context.getInitParameter("authManager"))) {
privLogger.debug("Loading AuthManager in {} from {}", this.getClass(), context.getInitParameter("authManager"));
authManager = (AuthManager) Class.forName(context.getInitParameter("authManager")).newInstance();
} else {
privLogger.debug("Loading DefaultAuthManager in {} from {}", this.getClass(), DefaultAuthManager.class);
authManager = new DefaultAuthManager();
}
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
protected void checkAuthorizedHosts(Query query, String ip) throws NotAuthorizedHostException, NotAuthorizedUserException {
if (authorizedHosts.contains("0.0.0.0") || authorizedHosts.contains("*") || authorizedHosts.contains(ip)) {
authManager.checkPermission(query, "");
} else {
throw new NotAuthorizedHostException("No queries are allowed from " + ip);
}
}
protected Response createJsonResponse(Object object) {
try {
return buildResponse(Response.ok(jsonObjectWriter.writeValueAsString(object), MediaType.APPLICATION_JSON_TYPE));
} catch (JsonProcessingException e) {
return createErrorResponse("Error parsing QueryResponse object:\n" + Arrays.toString(e.getStackTrace()));
}
}
protected Response createErrorResponse(Object o) {
QueryResult<ObjectMap> result = new QueryResult<>();
result.setErrorMsg(o.toString());
System.out.println("ERROR" + o.toString());
return createOkResponse(result);
}
protected Response createOkResponse(Object obj) {
QueryResponse queryResponse = new QueryResponse();
long endTime = System.currentTimeMillis() - startTime;
queryResponse.setTime(new Long(endTime - startTime).intValue());
queryResponse.setApiVersion(version);
queryResponse.setQueryOptions(queryOptions);
// Guarantee that the QueryResponse object contains a coll of results
List coll;
if (obj instanceof List) {
coll = (List) obj;
} else {
coll = new ArrayList();
coll.add(obj);
}
queryResponse.setResponse(coll);
switch (outputFormat.toLowerCase()) {
case "json":
return createJsonResponse(queryResponse);
case "xml":
// return createXmlResponse(queryResponse);
default:
return buildResponse(Response.ok());
}
}
//Response methods
protected Response createOkResponse(Object o1, MediaType o2) {
return buildResponse(Response.ok(o1, o2));
}
protected Response createOkResponse(Object o1, MediaType o2, String fileName) {
return buildResponse(Response.ok(o1, o2).header("content-disposition", "attachment; filename =" + fileName));
}
protected Response buildResponse(Response.ResponseBuilder responseBuilder) {
return responseBuilder
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "x-requested-with, content-type")
.build();
}
public static void setStorageConfiguration(StorageConfiguration storageConfiguration) {
GenericRestWebService.storageConfiguration = storageConfiguration;
}
}