Skip to content

Feature/smg flow #1

Merged
merged 2 commits into from Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -3,16 +3,17 @@ package com.lmco.spectrum.systemnavigation3d.controller;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.amazonaws.util.IOUtils;
import com.lmco.spectrum.systemnavigation3d.service.ResourceService;
import com.lmco.spectrum.systemnavigation3d.service.SmgService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -21,36 +22,80 @@ import java.io.InputStream;
@RequestMapping("api/resources")
public class ResourceController {

private final HttpHeaders PDF_HEADERS;

@Autowired
ResourceService resourceService;
@Autowired
SmgService smgService;

private ResponseEntity<?> handleS3Input(String key, @Nullable HttpHeaders headers) {
try (InputStream is = resourceService.getFileAsStream(key)) {
return new ResponseEntity<>(IOUtils.toByteArray(is), headers, HttpStatus.OK);
} catch (AmazonS3Exception e) {
if(e.getErrorCode().equals("NoSuchKey")) {
return new ResponseEntity<>(e.getErrorMessage(), HttpStatus.NOT_FOUND);
public ResourceController() {
this.PDF_HEADERS = new HttpHeaders();
PDF_HEADERS.setContentType(MediaType.APPLICATION_PDF);
}

private HttpHeaders getAutoDownloadHeaders(String key) {
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData(key, key);
return headers;
}

private ResponseEntity<?> respondToException(Exception e) {
if (e instanceof AmazonS3Exception) {
AmazonS3Exception ex = (AmazonS3Exception)e;
if (ex.getErrorCode().equals("NoSuchKey")) {
return new ResponseEntity<>(ex.getErrorMessage(), HttpStatus.NOT_FOUND);
} else {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
ex.printStackTrace();
return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (IOException e) {
} else if(e instanceof IOException) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}

// Return 500 as default.
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}

@RequestMapping(value = "/pdf/{key}", method = RequestMethod.GET)
private ResponseEntity<?> fetchResponseFromS3(String key, @Nullable HttpHeaders headers) {
try (InputStream is = resourceService.getFileAsStream(key)) {
return new ResponseEntity<>(IOUtils.toByteArray(is), headers, HttpStatus.OK);
} catch (Exception e) {
return respondToException(e);
}
}

@GetMapping("/pdf/{key}")
public ResponseEntity<?> getResourcePDF(@PathVariable("key") String key){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
// headers.setContentDispositionFormData(key, key); // Auto download
return handleS3Input(key, headers);
return fetchResponseFromS3(key, this.PDF_HEADERS);
}

@RequestMapping(value = "/{key}", method = RequestMethod.GET)
@GetMapping("/{key}")
public ResponseEntity<?> getResource(@PathVariable("key") String key){
return handleS3Input(key, null);
return fetchResponseFromS3(key, null);
}

// TODO what input data?
@GetMapping("/smg/{key}")
public ResponseEntity<?> getResourceSMG(@PathVariable("key") String key) {

try(InputStream is = resourceService.getFileAsStream(String.format("smg/%s", key))) {

byte[] data = IOUtils.toByteArray(is);

byte[] processed = smgService.processSmg(data);
if(processed == null) {
// TODO failed to process, error?
// TODO maybe return error list
return new ResponseEntity<>("Failed to process SMG file.", HttpStatus.INTERNAL_SERVER_ERROR);
} else {
return new ResponseEntity<>(processed, getAutoDownloadHeaders(key), HttpStatus.OK);
}

} catch(Exception e) {
return respondToException(e);
}
}

}
@@ -0,0 +1,85 @@
package com.lmco.spectrum.systemnavigation3d.service;

import com.amazonaws.util.IOUtils;
import com.lmco.spectrum.systemnavigation3d.util.io.SfxZipInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

@Service
public class SmgService {

private static final Logger log = LoggerFactory.getLogger(SmgService.class);
private static final String PRODUCT_XML = "product.smgXml";

@Autowired
TransformationService transformationService;

// Max capacity is 2^31-1 bytes or ~2GB.
@Nullable
public byte[] processSmg(byte[] data){
Map<String, byte[]> entryMap = getSmgXmlEntry(data);
byte[] smgXmlData = entryMap.get(PRODUCT_XML);
if(smgXmlData != null) {
String smgXml = new String(smgXmlData);
smgXml = transformationService.transform(smgXml);
entryMap.put(PRODUCT_XML, smgXml.getBytes()); // Update XML entry.
return repackageSmg(entryMap);
} else {
return null;
}
}

public Map<String, byte[]> getSmgXmlEntry(byte[] data) {

Map<String, byte[]> entryMap = new HashMap<>();

try(ZipInputStream zis = new ZipInputStream(new SfxZipInputStream(new ByteArrayInputStream(data)))) {

ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
entryMap.put(entry.getName(), IOUtils.toByteArray(zis));
zis.closeEntry();
}
return entryMap;

} catch (IOException e) {
log.error("Failed to read .smgXml entry!");
e.printStackTrace();
}

return entryMap;
}

@Nullable
public byte[] repackageSmg(Map<String, byte[]> entryMap) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(bos)) {
for(Map.Entry<String, byte[]> entry : entryMap.entrySet()) {
ZipEntry zipEntry = new ZipEntry(entry.getKey());
zos.putNextEntry(zipEntry);
zos.write(entry.getValue());
zos.closeEntry();
}
} catch (IOException e) {
log.error("Failed to repackage smg file");
e.printStackTrace();
return null;
}

return bos.toByteArray();

}

}
@@ -0,0 +1,17 @@
package com.lmco.spectrum.systemnavigation3d.service;

import org.springframework.stereotype.Service;

@Service
public class TransformationService {

// TODO make functional
// TODO consider types of transformations we need to make
// accept list of transformation options?
// accept GEIA dtos and infer transformations?
// pending
public String transform(String smgXml){
return smgXml;
}

}
@@ -0,0 +1,46 @@
package com.lmco.spectrum.systemnavigation3d.util.io;

import javax.annotation.Nonnull;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

// Credit
// https://stackoverflow.com/questions/7924895/how-can-i-read-from-a-winzip-self-extracting-exe-zip-file-in-java
public class SfxZipInputStream extends FilterInputStream {

// https://en.wikipedia.org/wiki/Zip_(file_format)#Local_file_header
public static final byte[] ZIP_LOCAL = { 0x50, 0x4b, 0x03, 0x04 };
protected int ip;
protected int op;

public SfxZipInputStream(InputStream is) {
super(is);
}

@Override
public int read() throws IOException {
while(ip < ZIP_LOCAL.length) {
int c = super.read();
if (c == ZIP_LOCAL[ip]) {
ip++;
}
else ip = 0;
}

if (op < ZIP_LOCAL.length)
return ZIP_LOCAL[op++];
else
return super.read();
}

@Override
public int read(@Nonnull byte[] b, int off, int len) throws IOException {
if (op == ZIP_LOCAL.length) return super.read(b, off, len);
int l = 0;
while (l < Math.min(len, ZIP_LOCAL.length)) {
b[l++] = (byte)read();
}
return l;
}
}