package com.lukechenshui.jresume; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.lukechenshui.jresume.exceptions.InvalidJSONException; import com.lukechenshui.jresume.resume.Resume; import freemarker.template.Configuration; import freemarker.template.Template; import net.lingala.zip4j.core.ZipFile; import org.apache.commons.io.FileDeleteStrategy; import org.apache.commons.io.FileUtils; import org.json.JSONException; import org.json.JSONObject; import spark.Request; import spark.Response; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Scanner; import java.util.concurrent.atomic.AtomicInteger; public class ResumeGenerator { //Used to separate output folders generated by different requests. private static AtomicInteger multithreadedOutputPrefixNumber = new AtomicInteger(0); Config config; File newResourceZip; File newResourceFolder; RuntimeConfiguration runtime; ResumeGenerator(Config config) throws Exception { this.config = config; int currentReqId = multithreadedOutputPrefixNumber.incrementAndGet(); File outputDirectory = new File("data/jresume" + currentReqId + ".tmp"); runtime = new RuntimeConfiguration(outputDirectory, currentReqId, config); extractResourcesFromJarFile(); } ResumeGenerator(Config config, RuntimeConfiguration runtime) throws Exception { this(config); this.runtime = runtime; } public static int getMultithreadedOutputPrefixNumber() { return multithreadedOutputPrefixNumber.incrementAndGet(); } private String generateResumeHTML(String json, String theme) throws Exception { if (!isValidJSON(json)) { throw new InvalidJSONException(); } Gson gson = new GsonBuilder().setPrettyPrinting().create(); Configuration cfg = new Configuration(Configuration.VERSION_2_3_25); cfg.setDirectoryForTemplateLoading(new File("themes")); cfg.setDefaultEncoding("UTF-8"); Template temp = cfg.getTemplate(theme + ".html"); //System.out.println(json); Resume resume = gson.fromJson(json, Resume.class); resume.getRidOfArraysWithEmptyElements(); resume.setConfig(config); StringWriter htmlStringWriter = new StringWriter(); temp.process(resume, htmlStringWriter); String html = htmlStringWriter.toString(); return html; } private boolean isValidJSON(String json) { try { JSONObject obj = new JSONObject(json); return true; } catch (JSONException exc) { exc.printStackTrace(); System.out.println("Invalid JSON:" + json); return false; } } private void extractResourcesFromJarFile() throws Exception { String classUrl = Main.class.getResource("Main.class").toString(); URL url = Main.class.getResource("/resources.zip"); //System.out.println("JAR Resource Zip URL: " + url.toString()); InputStream inputStream = url.openStream(); if (config.serverMode) { newResourceZip = config.serverInitialResourceZip; newResourceFolder = Paths.get("data", "resources").toFile(); } else { newResourceZip = Paths.get(config.outputDirectory, "webresume-resources.zip").toFile(); newResourceFolder = Paths.get(config.outputDirectory, config.resourceDirectory).toFile(); } if (!newResourceFolder.exists()) { newResourceFolder.mkdirs(); } if (newResourceZip.exists()) { FileDeleteStrategy.FORCE.delete(newResourceZip); } Files.copy(inputStream, newResourceZip.toPath()); ZipFile zipFile = new ZipFile(newResourceZip); zipFile.extractAll(newResourceFolder.getAbsolutePath()); } public Object generateResumeInRoute(String theme, Request request, Response response, WEBREQUEST_TYPE type) throws Exception { runtime.setWebRequestType(type); File location = writeResume(request.body(), theme); HttpServletResponse rawResponse = response.raw(); if (type == WEBREQUEST_TYPE.HTML) { rawResponse.setContentType("text/html"); } if (type == WEBREQUEST_TYPE.PDF) { rawResponse.setContentType("application/pdf"); } OutputStream out = rawResponse.getOutputStream(); writeFiletoOutputStreamByteByByte(location, out); //FileDeleteStrategy.FORCE.delete(outputDirectory); return rawResponse; } public File writeResume(String json, String theme) throws Exception { if (json == null) { json = readJSONFromFile(); } String html = generateResumeHTML(json, theme); File location = runtime.getOutputHtmlFile(); if (!location.exists()) { location.getParentFile().mkdirs(); } ; FileWriter writer = new FileWriter(location, false); writer.write(html); writer.close(); //System.out.println(html); System.out.println("Success! You can find your resume at " + runtime.getOutputHtmlFile().getAbsolutePath()); // createPDF(runtime.getOutputHtmlFile().getAbsolutePath()); if (!newResourceFolder.getAbsolutePath().toString().equals(Paths.get(runtime.getOutputDirectory().getAbsolutePath().toString(), "resources").toString())) { FileUtils.copyDirectoryToDirectory(newResourceFolder, runtime.getOutputDirectory()); } File outputFile; if (runtime.getWebRequestType() == WEBREQUEST_TYPE.PDF) { createPDF(runtime.getOutputHtmlFile().getAbsolutePath(), runtime); outputFile = runtime.getOutputHtmlFile("output.pdf"); } else { createInlineHTML(runtime); outputFile = runtime.getOutputHtmlFile("resume_inline.html"); } System.out.println("Output File: " + outputFile.getAbsolutePath()); return outputFile; } private String readJSONFromFile() throws Exception { String jsonResumePath = config.getInputFileName(); String json = ""; Scanner reader = new Scanner(new File(jsonResumePath)); while (reader.hasNextLine()) { json += reader.nextLine(); json += "\n"; } reader.close(); return json; } private void writeFiletoOutputStreamByteByByte(File file, OutputStream out) throws IOException { FileInputStream input = new FileInputStream(file); int c; while ((c = input.read()) != -1) { out.write(c); } input.close(); out.close(); } public void createPDF(String path, RuntimeConfiguration runtime) { System.out.println(path); try { String[] args = new String[]{"/bin/bash", "-c", "cd " + runtime.getOutputDirectory().getAbsolutePath() + " ; `npm get prefix`/bin/chrome-headless-render-pdf --url file://" + path + " --pdf output.pdf"}; Process process = new ProcessBuilder(args).start(); process.waitFor(); } catch (Exception e) { e.printStackTrace(); } } public void createInlineHTML(RuntimeConfiguration runtime) { try { System.out.println("Dir " + runtime.getOutputDirectory().getAbsolutePath()); System.out.println("Output file" + runtime.getOutputHtmlFile()); String[] args = new String[]{"/bin/bash", "-c", "cd " + runtime.getOutputDirectory().getAbsolutePath() + " && nohup script --quiet --return --command '`npm get prefix`/bin/inliner " + runtime.getOutputHtmlFile().getAbsolutePath() + " /dev/null'"}; Process process = new ProcessBuilder(args).redirectErrorStream(true).redirectOutput(runtime.getOutputHtmlFile("resume_inline.html")).start(); process.waitFor(); } catch (Exception e) { e.printStackTrace(); } } enum WEBREQUEST_TYPE { HTML, PDF, PREVIEW } }