Dateiverwaltung für die WebBox
ulrich
2021-01-21 8cab6e94514c38151b2e0c53c9df47c6e1682e28
src/java/de/uhilger/filecms/api/CompileService.java
@@ -17,25 +17,19 @@
*/
package de.uhilger.filecms.api;
import static de.uhilger.filecms.api.FileMgr.HOME_DIR_NAME;
import static de.uhilger.filecms.api.FileMgr.HOME_DIR_PATH;
import static de.uhilger.filecms.api.FileMgr.PUB_DIR_NAME;
import static de.uhilger.filecms.api.FileMgr.PUB_DIR_PATH;
import de.uhilger.filecms.data.FileRef;
import de.uhilger.filecms.web.Initialiser;
import de.uhilger.transit.web.RequestKontext;
import de.uhilger.transit.web.WebKontext;
import de.uhilger.filecms.data.CompilerIssue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.security.Principal;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
@@ -43,25 +37,200 @@
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.apache.commons.io.FileUtils;
import org.apache.tomcat.util.log.SystemLogHandler;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
/**
 *
 */
public class CompileService implements RequestKontext, WebKontext {
public class CompileService extends Api {
  
  private static final Logger logger = Logger.getLogger(CompileService.class.getName());
  
  private ServletContext ctx;
  private HttpServletRequest request;
  public String antBuild(String relPath) {
    // Create a stream to hold the output
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    // IMPORTANT: Save the old System.out!
    PrintStream old = System.out;
    PrintStream err = System.err;
    // Tell Java to use your special stream
    System.setOut(ps);
    System.setErr(ps);
    File targetDir = getTargetDir(relPath); // App-Ordner
    StringBuilder sb = new StringBuilder();
    sb.append("Ant build ist noch nicht implementiert.");
    sb.append("<br/>");
    sb.append("targetDir: ");
    sb.append(targetDir.getAbsolutePath());
    File buildFile = new File(targetDir, "build.xml");
    Project p = new Project();
    //p.setName("FileCms Build");
    FileCmsBuildListener listener = new FileCmsBuildListener();
    p.addBuildListener(listener);
    p.setUserProperty("ant.file", buildFile.getAbsolutePath());
    p.init();
    ProjectHelper helper = ProjectHelper.getProjectHelper();
    p.addReference("ant.projectHelper", helper);
    helper.parse(p, buildFile);
    p.executeTarget(p.getDefaultTarget());
    sb.append("<br/>");
    sb.append(listener.getOutput());
    // Print some output: goes to your special stream
    //System.out.println("Foofoofoo!");
    // Put things back
    System.out.flush();
    System.err.flush();
    System.setOut(old);
    System.setErr(err);
    // Show what happened
    //System.out.println("Here: " + baos.toString());
    sb.append("<br/>");
    sb.append(baos.toString());
    return sb.toString();
  }
  
  public String compile(String relPath, List fileNames) throws IOException {
    //Files[] files1 = ... ; // input for first compilation task
    //Files[] files2 = ... ; // input for second compilation task
  /**
   * Annahme: relPath zeigt auf einen Ordner, in dem ein build-Ordner die
   * fertigen Klassen und ein web-Ordner die Struktur mit WEB-INF
   * enthaelt.
   *
   * @param relPath  der relative Pfad, der auf den App-Ordner verweist
   * @return
   */
  public String buildApp(String relPath) {
    String result = "ok";
    try {
      File targetDir = getTargetDir(relPath); // App-Ordner
      File classesDir = new File(targetDir, "web/WEB-INF/classes");
      if(classesDir.exists()) {
        FileUtils.deleteDirectory(classesDir);
      }
      classesDir.mkdirs();
      File buildDir = new File(targetDir, "build/");
      File[] files = buildDir.listFiles();
      for(int i = 0; i < files.length; i++) {
        if(files[i].isDirectory()) {
          FileUtils.copyDirectoryToDirectory(files[i], classesDir);
        } else {
          FileUtils.copyFileToDirectory(files[i], classesDir);
        }
      }
    } catch(Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    }
    return result;
  }
    
  public List<CompilerIssue> compileAll(String relPath) {
    logger.fine(relPath);
    List<CompilerIssue> compilerIssues = new ArrayList();
    try {
      File targetDir = getTargetDir(relPath);
      ArrayList<File> files = new ArrayList();
      collectFiles(files, targetDir, new JavaFileFilter());
      JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
      DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector();
      StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
      Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(files);
      final Iterable<String> options = buildOptions(targetDir);
      compiler.getTask(null, null, diagnostics, options, null, compilationUnits).call();
      fileManager.close();
      collectResults(diagnostics, compilerIssues, relPath);
    } catch(Exception ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    }
    return compilerIssues;
  }
  private void collectResults(DiagnosticCollector<JavaFileObject> diagnostics, List<CompilerIssue> compilerIssues, String relPath) {
    List compileResults = diagnostics.getDiagnostics();
    Iterator i = compileResults.iterator();
    while (i.hasNext()) {
      Object o = i.next();
      Diagnostic<? extends JavaFileObject> err;
      if (o instanceof Diagnostic) {
        err = (Diagnostic) o;
        CompilerIssue issue = new CompilerIssue();
        issue.setKind(err.getKind().name());
        issue.setLineNumber(err.getLineNumber());
        issue.setMessage(err.getMessage(Locale.GERMANY));
        String srcName = err.getSource().getName().replace("\\", "/");
        String cleanRelPath = relPath.replace(HOME_DIR_NAME + "/", "").replace(PUB_DIR_NAME + "/", "");
        int pos = srcName.indexOf(cleanRelPath);
        String className = srcName.substring(pos + cleanRelPath.length());
        issue.setSourceName(className.replace("/", ".").substring(1));
        //issue.setSourceName(srcName + "\r\n" + relPath);
        compilerIssues.add(issue);
      }
    }
  }
  private void collectFiles(ArrayList<File> files, File dir, FileFilter filter) {
    File[] dirFiles = dir.listFiles(filter);
    for(int i = 0; i < dirFiles.length; i++) {
      if(dirFiles[i].isDirectory()) {
        logger.fine("drill down to " + dirFiles[i].getAbsolutePath());
        collectFiles(files, dirFiles[i], filter);
      } else {
        logger.fine("add " + dirFiles[i].getAbsolutePath());
        files.add(dirFiles[i]);
      }
    }
  }
  public class JavaFileFilter implements FileFilter {
    @Override
    public boolean accept(File pathname) {
      boolean doAccept = false;
      if(pathname.getName().endsWith(".java") || pathname.isDirectory()) {
        doAccept = true;
      }
      return doAccept;
    }
  }
  public class JarFileFilter implements FileFilter {
    @Override
    public boolean accept(File pathname) {
      boolean doAccept = false;
      if(pathname.getName().endsWith(".jar")) {
        doAccept = true;
      }
      return doAccept;
    }
  }
  /**
   *
   * @param relPath
   * @param fileNames
   * @param mode 0 = test, 1 = build
   * @return
   * @throws IOException
   */
  public List<CompilerIssue> compile(String relPath, List fileNames, String mode) throws IOException {
    File targetDir = getTargetDir(relPath);
    //System.out.println(targetDir.getAbsolutePath());
    ArrayList<File> files = new ArrayList();
    for(int i=0; i < fileNames.size(); i++) {
      Object o = fileNames.get(i);
      if(o instanceof ArrayList) {
@@ -72,103 +241,73 @@
        files.add(targetFile);
      }
    }
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
    Iterable<? extends JavaFileObject> compilationUnits1 = fileManager.getJavaFileObjectsFromFiles(files);
    final Iterable<String> options =
            Arrays.asList(new String[]{"-Xlint",
              /*"-cp", project.getClassPath(),*/
              "-d", targetDir.getAbsolutePath()
              });
    Iterable<? extends JavaFileObject> compilationUnits1 = fileManager.getJavaFileObjectsFromFiles(files);
    final Iterable<String> options = buildOptions(targetDir);
    compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits1).call();
    /*
    Iterable<? extends JavaFileObject> compilationUnits2
            = fileManager.getJavaFileObjects(files2); // use alternative method
    // reuse the same file manager to allow caching of jar files
    compiler.getTask(null, fileManager, null, null, null, compilationUnits2).call();
    */
    fileManager.close();
    List<CompilerIssue> compilerIssues = new ArrayList();
    collectResults(diagnostics, compilerIssues, relPath);
    return compilerIssues;
  }
    StringBuilder msg = new StringBuilder();
    msg.append("Result of compile to Java bytecode (empty means no error):");
    for (Diagnostic<? extends JavaFileObject> err : diagnostics.getDiagnostics()) {
      msg.append('\n');
      msg.append(err.getKind());
      msg.append(": ");
      if (err.getSource() != null) {
        msg.append(err.getSource().getName());
  private final Iterable<String> buildOptions(File targetDir) {
      String cbase = getCatalinaBase(getServletContext());
      File lib = new File(cbase, "lib");
      String cp = "";
      cp = buildCPFromDir(cp, lib);
      logger.fine(lib.getAbsolutePath());
      logger.fine(cp);
      /*
        wegen dieser Funktion MUSS alles in 'src' liegen
      */
      File srcDir = targetDir;
      while(!srcDir.getName().endsWith("src")) {
        srcDir = srcDir.getParentFile();
      }
      msg.append(':');
      msg.append(err.getLineNumber());
      msg.append(": ");
      msg.append(err.getMessage(Locale.GERMANY));
    }
      File appDir = srcDir.getParentFile();
      File appLibDir = new File(appDir, "web/WEB-INF/lib");
      cp = buildCPFromDir(cp, appLibDir);
      logger.fine(cp);
      /*
        ausgehend von src eins hoeher, dann nach build
      */
      File buildDir = new File(appDir, "build");
      final Iterable<String> options = Arrays.asList(new String[]{"-Xlint",
                    "-cp", cp,
                    "-d", buildDir.getAbsolutePath()
                    });
      try {
        FileUtils.deleteDirectory(buildDir);
        buildDir.mkdir();
      } catch (IOException ex) {
        logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
      }
      return options;
  }
  /*
  -classpath $JLIB/jettison-1.3.3.jar:$JLIB/xstream-1.4.7.jar
  */
  private String buildCPFromDir(String cp, File dir) {
    StringBuffer buf = new StringBuffer(cp);
    File[] files = dir.listFiles(new JarFileFilter());
    for(int i = 0; i < files.length; i++) {
      if(buf.length() > 0) {
        buf.append(File.pathSeparatorChar);
      }
      //buf.append("\"");
      buf.append(files[i].getAbsolutePath());
      //buf.append("\"");
    }
    
    return msg.toString();
  }
  private File getTargetDir(String relPath) {
    logger.fine(relPath);
    String targetPath = null;
    if(relPath.startsWith(PUB_DIR_NAME)) {
      targetPath = PUB_DIR_PATH + getUserName() + relPath.substring(PUB_DIR_NAME.length());
    } else if(relPath.startsWith(HOME_DIR_NAME)) {
      targetPath = HOME_DIR_PATH + getUserName() + relPath.substring(HOME_DIR_NAME.length());
    } else {
      // kann eigentlich nicht sein..
    }
    logger.fine(targetPath);
    File targetDir = new File(getBase().getAbsolutePath(), targetPath);
    return targetDir;
  }
  private FileRef getBase() {
    FileRef base = null;
    Object o = getServletContext().getAttribute(Initialiser.FILE_BASE);
    if(o instanceof String) {
      String baseStr = (String) o;
      logger.fine(baseStr);
      File file = new File(baseStr);
      base = new FileRef(file.getAbsolutePath(), file.isDirectory());
    }
    return base;
  }
  private String getUserName() {
    String userName = null;
    Object p = getRequest().getUserPrincipal();
    if(p instanceof Principal) {
      userName = ((Principal) p).getName();
    }
    return userName;
  }
  @Override
  public HttpServletRequest getRequest() {
    return request;
  }
  @Override
  public void setRequest(HttpServletRequest r) {
    this.request = r;
  }
  @Override
  public ServletContext getServletContext() {
    return ctx;
  }
  @Override
  public void setServletContext(ServletContext servletContext) {
    this.ctx = servletContext;
    return buf.toString();
  }
}
/*
 Beispeil fuer einen dynamischen Compiler-Aufruf