package org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager; import org.ebayopensource.turmeric.tools.codegen.CodeGenContext; import org.ebayopensource.turmeric.tools.codegen.builders.BaseCodeGenerator; import org.ebayopensource.turmeric.tools.codegen.exception.CodeGenFailedException; import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufSchema; import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil; import org.ebayopensource.turmeric.tools.codegen.util.JavacHelper; public class ProtoBufCompiler { private static final ProtoBufCompiler m_instance = new ProtoBufCompiler(); private ProtoBufCompiler(){ } private static Logger s_logger = LogManager.getInstance(ProtoBufCompiler.class); private Logger getLogger() { return s_logger; } public static ProtoBufCompiler getInstance(){ return m_instance; } public void compileProtoFile(ProtobufSchema protobufSchema, CodeGenContext codeGenContext) throws CodeGenFailedException{ getLogger().log(Level.INFO, "Start protoc compiler invocation"); String protoPath = "--proto_path=" + protobufSchema.getDotprotoTargetDir(); String protoFileLocation = protobufSchema.getDotprotoTargetDir() + "/" + protobufSchema.getDotprotoFileName(); String destLocation = "--java_out=" + codeGenContext.getJavaSrcDestLocation(); File protocFile = createTempExeFile(codeGenContext); if(protocFile == null) throw new CodeGenFailedException(); String protoLoc = protocFile.getAbsolutePath(); List<String> command = new ArrayList<String>(); command.add(protoLoc); command.add(protoPath); command.add("--error_format=gcc"); command.add(destLocation); command.add(protoFileLocation); getLogger().log(Level.INFO, "protoc invoked with the following options \n" + command); ProcessBuilder builder = new ProcessBuilder(command); Process protocProcess = null; try { protocProcess = builder.start(); writeProcessOutput(protocProcess); } catch (CodeGenFailedException codeGenFailedException) { throw codeGenFailedException; } catch (Exception exception) { throw new CodeGenFailedException(exception.getMessage(), exception); }finally{ try { String jProtoClassName = protobufSchema.getJProtoOuterClassName(); BaseCodeGenerator.compileJavaFile(CodeGenUtil.toJavaSrcFilePath( codeGenContext.getJavaSrcDestLocation(), jProtoClassName), codeGenContext.getBinLocation()); JavacHelper.addToClasspath(codeGenContext.getBinLocation()); } catch (Exception exception) { throw new CodeGenFailedException(exception.getMessage(), exception); } deleteFile( protocFile ); } } private static boolean deleteFile(File file){ return file.delete(); } private File createTempExeFile(CodeGenContext codeGenContext) { File outputFile = new File(codeGenContext.getDestLocation() + "/protoc.exe"); try { InputStream jarInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/proto/protoc.exe"); if(jarInputStream!=null) { FileOutputStream os=new FileOutputStream(outputFile); final byte[] buf = new byte[1024]; int i = 0; while((i = jarInputStream.read(buf)) != -1) { os.write(buf, 0, i); } jarInputStream.close(); os.close(); } else { getLogger().log(Level.SEVERE, " The protoc.exe could not be copied as the file " + "was not available in the classpath. Make sure protobuf.jar is in the classpath."); } } catch (IOException e) { getLogger().log(Level.SEVERE, " Could not copy protoc.exe from the jar properly, this will lead to codegen failure"); } return outputFile; } private void writeProcessOutput(Process process) throws Exception{ StringBuilder builder = new StringBuilder(); InputStream es = process.getErrorStream(); InputStreamReader esr = new InputStreamReader(es, Charset.defaultCharset()); BufferedReader ebr = new BufferedReader(esr); // InputStream os = process.getInputStream(); // InputStreamReader osr = new InputStreamReader(os); // BufferedReader obr = new BufferedReader(osr); String line; builder.append("Compilation of Proto files generated resulted in the following error \n"); try{ while ((line = ebr.readLine()) != null) { String[] errormessage = line.split(":"); if(errormessage.length == 4){ builder.append("Filename : ").append(errormessage[0]) .append("\nLine : ").append(errormessage[1]) .append("\nColumn : ").append(errormessage[2]) .append("\nErrorMessage : ").append(errormessage[3]); builder.append("\n"); } else builder.append(line).append("\n"); } }finally{ ebr.close(); es.close(); } // os.close(); if(process.exitValue() != 0){ getLogger().log(Level.INFO, "Compilation of Proto files generated resulted in " + "the following error:\n" + builder.toString()); CodeGenFailedException codeGenFailedException = new CodeGenFailedException(builder.toString()); codeGenFailedException.setMessageFormatted(true); throw codeGenFailedException; } } }