/*
* Copyright (C) 2007 - 2016 GeoSolutions S.A.S.
* http://www.geo-solutions.it
*
* GPLv3 + Classpath exception
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package it.geosolutions.geostore.services.rest.impl;
import it.geosolutions.geostore.core.model.StoredData;
import it.geosolutions.geostore.core.model.User;
import it.geosolutions.geostore.services.SecurityService;
import it.geosolutions.geostore.services.StoredDataService;
import it.geosolutions.geostore.services.exception.NotFoundServiceEx;
import it.geosolutions.geostore.services.rest.RESTStoredDataService;
import it.geosolutions.geostore.services.rest.exception.BadRequestWebEx;
import it.geosolutions.geostore.services.rest.exception.ForbiddenErrorWebEx;
import it.geosolutions.geostore.services.rest.exception.InternalErrorWebEx;
import it.geosolutions.geostore.services.rest.exception.NotFoundWebEx;
import it.geosolutions.geostore.services.rest.model.enums.RawFormat;
import it.geosolutions.geostore.services.rest.utils.DataURIDecoder;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import net.sf.json.JSON;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import net.sf.json.xml.XMLSerializer;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
/**
* Class RESTStoredDataServiceImpl.
*
* @author ETj (etj at geo-solutions.it)
* @author Tobia di Pisa (tobia.dipisa at geo-solutions.it)
*
*/
public class RESTStoredDataServiceImpl extends RESTServiceImpl implements RESTStoredDataService {
private final static Logger LOGGER = Logger.getLogger(RESTStoredDataServiceImpl.class);
private StoredDataService storedDataService;
/**
* @param storedDataService
*/
public void setStoredDataService(StoredDataService storedDataService) {
this.storedDataService = storedDataService;
}
/* (non-Javadoc)
* @see it.geosolutions.geostore.services.rest.impl.RESTServiceImpl#getSecurityService()
*/
@Override
protected SecurityService getSecurityService() {
return storedDataService;
}
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTStoredDataService#update(long, java.lang.String)
*/
@Override
public long update(SecurityContext sc, long id, String data) throws NotFoundWebEx {
try {
if (data == null)
throw new BadRequestWebEx("Data is null");
//
// Authorization check.
//
boolean canUpdate = false;
User authUser = extractAuthUser(sc);
canUpdate = resourceAccessWrite(authUser, id); // The ID is also the resource ID
if (canUpdate) {
storedDataService.update(id, data);
} else {
throw new ForbiddenErrorWebEx("This user cannot update or create this store !");
}
return id;
} catch (NotFoundServiceEx ex) {
LOGGER.warn("Data not found (" + id + "): " + ex.getMessage(), ex);
throw new NotFoundWebEx("Data not found");
}
}
// /* (non-Javadoc)
// * @see it.geosolutions.geostore.services.rest.RESTStoredDataService#getAll()
// */
// @Override
// public StoredDataList getAll(SecurityContext sc) {
// return new StoredDataList(storedDataService.getAll());
// }
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTStoredDataService#delete(long)
*/
@Override
public void delete(SecurityContext sc, long id) throws NotFoundWebEx {
//
// Authorization check.
//
boolean canDelete = false;
User authUser = extractAuthUser(sc);
canDelete = resourceAccessWrite(authUser, id);
if (canDelete) {
boolean ret = storedDataService.delete(id);
if (!ret)
throw new NotFoundWebEx("Data not found");
} else
throw new ForbiddenErrorWebEx("This user cannot delete this store !");
}
private final static Collection<MediaType> GET_XML_MEDIA_TYPES = Arrays.asList(
MediaType.TEXT_XML_TYPE, MediaType.APPLICATION_XML_TYPE);
private final static Collection<MediaType> GET_JSON_MEDIA_TYPES = Arrays
.asList(MediaType.APPLICATION_JSON_TYPE);
private final static Collection<MediaType> GET_TEXT_MEDIA_TYPES = Arrays
.asList(MediaType.TEXT_PLAIN_TYPE);
/*
* (non-Javadoc)
*
* @see it.geosolutions.geostore.services.rest.RESTStoredDataService#get(long)
*/
@Override
public String get(SecurityContext sc, HttpHeaders headers, long id) throws NotFoundWebEx {
if (id == -1)
return "dummy payload";
//
// Authorization check.
//
boolean canRead = false;
User authUser = extractAuthUser(sc);
canRead = resourceAccessRead(authUser, id); // The ID is also the resource ID
if(!canRead){
throw new ForbiddenErrorWebEx("This user cannot read this stored data !");
}
StoredData storedData;
try {
storedData = storedDataService.get(id);
} catch (NotFoundServiceEx e) {
throw new NotFoundWebEx("Data not found");
}
String data = storedData == null ? "" : storedData.getData();
// prefer no transformation
if (headers.getAcceptableMediaTypes().contains(MediaType.WILDCARD_TYPE)) {
return data;
} else if (!Collections.disjoint(GET_TEXT_MEDIA_TYPES, headers.getAcceptableMediaTypes())) {
return data;
} else if (!Collections.disjoint(GET_JSON_MEDIA_TYPES, headers.getAcceptableMediaTypes())) {
return toJSON(data);
} else if (!Collections.disjoint(GET_XML_MEDIA_TYPES, headers.getAcceptableMediaTypes())) {
return toXML(data);
} else
throw new InternalErrorWebEx("Illegal state (" + headers.getAcceptableMediaTypes()
+ ")");
}
private String toJSON(String data) {
try {
// ////////////////////////
// XML to JSON
// ////////////////////////
XMLSerializer xmlSerializer = new XMLSerializer();
JSON json = xmlSerializer.read(data);
String ret = json.toString();
if (LOGGER.isDebugEnabled())
LOGGER.debug("Transformed XML -> JSON");
return ret;
} catch (JSONException exc) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Data is not in native XML format.");
}
try {
// ///////////////////////
// data To JSON conversion
// ///////////////////////
JSONSerializer.toJSON(data);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Data is in native JSON format.");
return data;
} catch (JSONException e) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Data is not in native JSON format.");
}
JSONObject jsonObj = new JSONObject();
jsonObj.put("data", data);
String ret = jsonObj.toString();
if (LOGGER.isDebugEnabled())
LOGGER.debug("Transformed plaintext -> JSON");
return ret;
}
private String toXML(String data) {
// Try XML source
try {
StringReader reader = new StringReader(data);
SAXBuilder builder = new SAXBuilder();
builder.build(reader);
// no errors: return the original data
if (LOGGER.isDebugEnabled())
LOGGER.debug("Data is in native XML format.");
return data;
} catch (Exception e) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Data is not in native XML format.");
}
// Try JSON source
try {
// ///////////////////////
// JSON To XML conversion
// ///////////////////////
JSON json = JSONSerializer.toJSON(data);
XMLSerializer xmlSerializer = new XMLSerializer();
String ret = xmlSerializer.write(json);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Transformed JSON -> XML");
return ret;
} catch (JSONException exc) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Data is not in native JSON format.");
}
// Force XML format
Element element = new Element("data");
element.addContent(data);
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
String ret = outputter.outputString(element);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Transformed plaintext -> XML");
return ret;
}
@Override
public Response getRaw(SecurityContext sc, HttpHeaders headers, long id, String decodeFormat)
throws NotFoundWebEx
{
if(id == -1)
return Response.ok().entity("dummy payload").build();
StoredData storedData;
try {
storedData = storedDataService.get(id);
} catch(NotFoundServiceEx e){
return Response.status(Response.Status.NOT_FOUND).build();
}
if(storedData == null) {
return Response.noContent().build();
}
String data = storedData.getData();
// prefer no transformation
if( decodeFormat == null) {
return Response.ok().entity(data).build();
}
else if(decodeFormat.equalsIgnoreCase(RawFormat.BASE64.name())) {
byte[] decoded = Base64.decodeBase64(data);
return Response.ok().entity(decoded).build();
}
else if(decodeFormat.equalsIgnoreCase(RawFormat.DATAURI.name())) {
return decodeDataURI(data);
}
else {
LOGGER.warn("Unknown decode format '"+decodeFormat+"'");
return Response.ok().entity(data).build();
}
}
private Response decodeDataURI(String data)
{
if(! data.startsWith("data:")) {
return Response.status(Response.Status.BAD_REQUEST).entity("Not a data URI").build();
}
String[] split = data.split(",", 2);
if(split.length < 2) {
return Response.status(Response.Status.BAD_REQUEST).entity("Bad data, comma is missing").build();
}
DataURIDecoder dud = new DataURIDecoder(split[0]);
if(! dud.isValid()) {
LOGGER.warn("Could not parse data URI '"+split[0]+"'");
return Response.status(Response.Status.BAD_REQUEST).entity("Bad data URI").build();
}
if(dud.getCharset() != null) {
LOGGER.warn("TODO: Charset '"+dud.getCharset()+"' should be handled.");
}
if(dud.getEncoding() != null && ! dud.isBase64Encoded()) {
LOGGER.warn("TODO: Encoding '"+dud.getEncoding()+"' should be handled.");
}
Object entity = dud.isBase64Encoded() ? Base64.decodeBase64(split[1]) : split[1];
return Response.ok().type(dud.getNormalizedMediatype()).entity(entity).build();
}
/**
* @param id
* @return long
* @throws BadRequestWebEx
*/
@SuppressWarnings("unused")
private long parseId(String id) throws BadRequestWebEx {
try {
return Long.parseLong(id);
} catch (Exception e) {
LOGGER.info("Bad id requested '" + id + "'");
throw new BadRequestWebEx("Bad id");
}
}
}