/*
*
* Copyright (C) 2012-2014 R T Huitema. All Rights Reserved.
* Web: www.42.co.nz
* Email: robert@42.co.nz
* Author: R T Huitema
*
* This file is part of the signalk-server-java project
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* 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 nz.co.fortytwo.signalk.processor;
import static nz.co.fortytwo.signalk.util.ConfigConstants.MAP_DIR;
import static nz.co.fortytwo.signalk.util.ConfigConstants.STATIC_DIR;
import static nz.co.fortytwo.signalk.util.SignalKConstants.dot;
import static nz.co.fortytwo.signalk.util.SignalKConstants.name;
import static nz.co.fortytwo.signalk.util.SignalKConstants.resources;
import static nz.co.fortytwo.signalk.util.SignalKConstants.resources_charts;
import static nz.co.fortytwo.signalk.util.SignalKConstants.routes;
import static nz.co.fortytwo.signalk.util.SignalKConstants.value;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.component.http.HttpMessage;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.restlet.data.MediaType;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.representation.InputRepresentation;
import mjson.Json;
import nz.co.fortytwo.signalk.util.SignalKConstants;
import nz.co.fortytwo.signalk.util.Util;
import nz.co.fortytwo.signalk.util.ZipUtils;
public class UploadProcessor extends SignalkProcessor implements Processor {
private static Logger logger = LogManager.getLogger(UploadProcessor.class);
@Override
public void process(Exchange exchange) throws Exception {
logger.debug("UploadProcessor starts");
HttpServletRequest request = exchange.getIn(HttpMessage.class).getRequest();
logger.debug("Session = " + request.getSession().getId());
HttpSession session = request.getSession();
if (logger.isDebugEnabled()) {
logger.debug("Request = " + exchange.getIn().getHeader(Exchange.HTTP_SERVLET_REQUEST).getClass());
logger.debug("Session = " + session.getId());
}
if (session.getId() != null) {
String remoteAddress = request.getRemoteAddr();
String localAddress = request.getLocalAddr();
if (Util.sameNetwork(localAddress, remoteAddress)) {
exchange.getIn().setHeader(SignalKConstants.MSG_TYPE, SignalKConstants.INTERNAL_IP);
} else {
exchange.getIn().setHeader(SignalKConstants.MSG_TYPE, SignalKConstants.EXTERNAL_IP);
}
if (exchange.getIn().getHeader(Exchange.HTTP_METHOD).equals("POST")) {
processUpload(exchange);
}
} else {
exchange.getIn().setHeader("Location", SignalKConstants.SIGNALK_AUTH);
exchange.getIn().setBody("Authentication Required");
}
}
private void processUpload(Exchange exchange) throws Exception {
logger.debug("Begin import:"+ exchange.getIn().getHeaders());
if(exchange.getIn().getBody()!=null){
logger.debug("Body class:"+ exchange.getIn().getBody().getClass());
}else{
logger.debug("Body class is null");
}
//logger.debug("Begin import:"+ exchange.getIn().getBody());
MediaType mediaType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, MediaType.class);
InputRepresentation representation = new InputRepresentation((InputStream) exchange.getIn().getBody(), mediaType);
logger.debug("Found MIME:"+ mediaType+", length:"+exchange.getIn().getHeader(Exchange.CONTENT_LENGTH, Integer.class));
//make a reply
Json reply = Json.read("{\"files\": []}");
Json files = reply.at("files");
try {
List<FileItem> items = new RestletFileUpload(new DiskFileItemFactory()).parseRepresentation(representation);
logger.debug("Begin import files:"+items);
for (FileItem item : items) {
if (!item.isFormField()) {
InputStream inputStream = item.getInputStream();
Path destination = Paths.get(Util.getConfigProperty(STATIC_DIR)+Util.getConfigProperty(MAP_DIR)+item.getName());
logger.debug("Save import file:"+destination);
long len = Files.copy(inputStream, destination, StandardCopyOption.REPLACE_EXISTING);
Json f = Json.object();
f.set("name",item.getName());
f.set("size",len);
files.add(f);
install(destination);
}
}
} catch (FileUploadException | IOException e) {
logger.error(e.getMessage(),e);
}
exchange.getIn().setBody(reply);
}
private void install(Path destination) throws Exception {
if(!destination.toString().endsWith(".zip"))return;
//unzip here
logger.debug("Unzipping file:"+destination);
try{
File zipFile = destination.toFile();
String f = destination.toFile().getName();
f= f.substring(0,f.indexOf("."));
File destDir = new File(Util.getConfigProperty(STATIC_DIR)+Util.getConfigProperty(MAP_DIR)+f);
if(!destDir.exists()){
destDir.mkdirs();
}
ZipUtils.unzip(destDir, zipFile);
logger.debug("Unzipped file:"+destDir);
//now add a reference in resources
loadChart(f);
}catch(Exception e){
logger.error(e.getMessage(),e);
throw e;
}
}
public void loadChart(String chartName) throws Exception{
try{
File destDir = new File(Util.getConfigProperty(STATIC_DIR)+Util.getConfigProperty(MAP_DIR)+chartName);
SAXReader reader = new SAXReader();
Document document = reader.read(new File(destDir, "tilemapresource.xml"));
String title = document.getRootElement().element("Title").getText();
String scale = "250000";
if(document.getRootElement().element("Metadata")!=null){
scale = document.getRootElement().element("Metadata").attribute("scale").getText();
}
double maxRes = 0.0;
double minRes = Double.MAX_VALUE;
int maxZoom = 0;
int minZoom = 99;
Element tileSets = document.getRootElement().element("TileSets");
for(Object o: tileSets.elements("TileSet")){
Element e = (Element)o;
int href = Integer.parseInt(e.attribute("href").getValue());
maxZoom=Math.max(href, maxZoom);
minZoom=Math.min(href, minZoom);
double units = Double.parseDouble(e.attribute("units-per-pixel").getValue());
maxRes=Math.max(units, maxRes);
minRes=Math.min(units, minRes);
}
//now make an entry in resources
Json resource = createChartMsg(chartName, title, scale);
inProducer.asyncSendBody(inProducer.getDefaultEndpoint(),resource);
}catch(Exception e){
logger.error(e.getMessage(),e);
throw e;
}
}
private Json createChartMsg(String f, String title, String scale){
Json val = Json.object();
val.set(SignalKConstants.PATH, "charts." + "urn:mrn:signalk:uuid:"+UUID.randomUUID().toString());
Json currentChart = Json.object();
val.set(value, currentChart);
String time = Util.getIsoTimeString();
time = time.substring(0, time.indexOf("."));
currentChart.set("identifier", f);
currentChart.set(name, title);
currentChart.set("description", title);
currentChart.set("tilemapUrl", "/"+Util.getConfigProperty(MAP_DIR)+f);
try{
int scaleInt = Integer.valueOf(scale);
currentChart.set("scale", scaleInt);
}catch(Exception e){
currentChart.set("scale", 0);
}
Json values = Json.array();
values.add(val);
Json update = Json.object();
update.set(SignalKConstants.values, values);
Json updates = Json.array();
updates.add(update);
Json msg = Json.object();
msg.set(SignalKConstants.CONTEXT, resources);
msg.set(SignalKConstants.PUT, updates);
if(logger.isDebugEnabled())logger.debug("Created new chart msg:"+msg);
return msg;
}
}