Package freenet.node.updater

Source Code of freenet.node.updater.UpdateDeployContext

package freenet.node.updater;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Properties;
import java.util.regex.Pattern;

import org.tanukisoftware.wrapper.WrapperManager;

import freenet.l10n.NodeL10n;
import freenet.node.NodeInitException;
import freenet.node.updater.MainJarDependenciesChecker.Dependency;
import freenet.node.updater.MainJarDependenciesChecker.MainJarDependencies;
import freenet.support.io.Closer;

/**
* Handles the wrapper.conf, essentially.
*/
public class UpdateDeployContext {

  public static class UpdateCatastropheException extends Exception {

    private static final long serialVersionUID = 1L;
    File oldConfig;
    File newConfig;
   
    UpdateCatastropheException(File oldConfig, File newConfig) {
      super(l10n("updateCatastrophe", new String[] { "old", "new" },
          new String[] { oldConfig.toString(), newConfig.toString() }));
      this.oldConfig = oldConfig;
      this.newConfig = newConfig;
    }
   
  }

  File mainJar;
  int mainClasspathNo;
  File newMainJar;
  File backupMainJar;
  boolean mainJarAbsolute;
  final MainJarDependencies deps;
 
  UpdateDeployContext(MainJarDependencies deps) throws UpdaterParserException {
    Properties p = WrapperManager.getProperties();
    this.deps = deps;
   
    for(int propNo=1;true;propNo++) {
      String prop = p.getProperty("wrapper.java.classpath."+propNo);
      if(prop == null) break;
      File f = new File(prop);
      boolean isAbsolute = f.isAbsolute();
      String name = f.getName().toLowerCase();
      if(mainJar == null) {
        if(name.equals("freenet-ext.jar") || name.equals("freenet-ext.jar.new") || (name.startsWith("freenet-ext") && name.endsWith(".jar")))
          // Don't match freenet-ext.jar!
          continue;
        // Try to match it
        if((name.startsWith("freenet") && (name.endsWith(".jar")))) {
          mainJar = f;
          newMainJar = new File(mainJar.getParent(), "freenet.jar.new");
          mainJarAbsolute = isAbsolute;
          mainClasspathNo = propNo;
          continue;
        } else if((name.startsWith("freenet") && (name.endsWith(".jar.new")))) {
          mainJar = f;
          newMainJar = new File(mainJar.getParent(), "freenet.jar");
          mainJarAbsolute = isAbsolute;
          mainClasspathNo = propNo;
          continue;
        }
      }
      // Else try to match from dependencies.
     
    }
   
    if(mainJar == null)
      throw new UpdaterParserException(l10n("cannotUpdateNoMainJar"));
    backupMainJar = new File(mainJar.getParent(), "freenet.jar.bak");
  }

  private String l10n(String key) {
    return NodeL10n.getBase().getString("UpdateDeployContext."+key);
  }

  public static String l10n(String key, String[] patterns, String[] values) {
    return NodeL10n.getBase().getString("UpdateDeployContext."+key, patterns, values);
  }

  public static String l10n(String key, String pattern, String value) {
    return NodeL10n.getBase().getString("UpdateDeployContext."+key, pattern, value);
  }

  File getMainJar() {
    return mainJar;
  }

  File getNewMainJar() {
    return newMainJar;
  }

  void rewriteWrapperConf(boolean writtenNewJar) throws IOException, UpdateCatastropheException, UpdaterParserException {
   
    // Rewrite wrapper.conf
    // Don't just write it out from properties; we want to keep it as close to what it was as possible.

    File oldConfig = new File("wrapper.conf");
    File newConfig = new File("wrapper.conf.new");
   
    if(!oldConfig.exists()) {
      File wrapperDir = new File("wrapper");
      if(wrapperDir.exists() && wrapperDir.isDirectory()) {
        File o = new File(wrapperDir, "wrapper.conf");
        if(o.exists()) {
          oldConfig = o;
          newConfig = new File(wrapperDir, "wrapper.conf.new");
        }
      }
    }
   
    FileInputStream fis = new FileInputStream(oldConfig);
    BufferedInputStream bis = new BufferedInputStream(fis);
    InputStreamReader isr = new InputStreamReader(bis);
    BufferedReader br = new BufferedReader(isr);
   
    FileOutputStream fos = new FileOutputStream(newConfig);
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    BufferedWriter bw = new BufferedWriter(osw);

    String line;
     
    boolean writtenReload = false;
    /** On Windows, we need the anchor file option explicitly. */
    boolean writtenAnchor = false;
    /** Write the anchor polling interval too if it doesn't exist already */
    boolean writtenAnchorInterval = false;
   
    String newMain = mainJarAbsolute ? newMainJar.getAbsolutePath() : newMainJar.getPath();
   
    String mainRHS = null;
   
    ArrayList<String> otherLines = new ArrayList<String>();
    ArrayList<String> classpath = new ArrayList<String>();
   
    // We MUST put the ext (and all other dependencies) before the main jar,
    // or auto-update of freenet-ext.jar on Windows won't work.
    // The main jar refers to freenet-ext.jar so that java -jar freenet.jar works.
    // Therefore, on Windows, if we update freenet-ext.jar, it will use freenet.jar and freenet-ext.jar
    // and freenet-ext.jar.new as well. The old freenet-ext.jar will take precedence, and we won't be
    // able to overwrite either of them, so we'll just restart every 5 minutes forever!
   
    while((line = br.readLine()) != null) {
        /** The values are case sensitive, but the keys aren't */
        String lowcaseLine = line.toLowerCase();
      // The classpath numbers are not reliable.
      // We have to check the content.
     
      boolean dontWrite = false;
     
      if(lowcaseLine.startsWith("wrapper.java.classpath.")) {
        line = line.substring("wrapper.java.classpath.".length());
        int idx = line.indexOf('=');
        if(idx != -1) {
          // Ignore the numbers.
          String rhs = line.substring(idx+1);
          dontWrite = true;
          if(rhs.equals("freenet.jar") || rhs.equals("freenet.jar.new") ||
              rhs.equals("freenet-stable-latest.jar") || rhs.equals("freenet-stable-latest.jar.new") ||
              rhs.equals("freenet-testing-latest.jar") || rhs.equals("freenet-testing-latest.jar.new")) {
            if(writtenNewJar)
              mainRHS = newMain;
            else
              mainRHS = rhs;
          } else {
            // Is it on the list of dependencies?
            Dependency dep = findDependencyByRHSFilename(new File(rhs));
            if(dep != null) {
                if(dep.oldFilename() != null)
                    System.out.println("Found old dependency "+dep.oldFilename());
                else
                    System.out.println("Found new dependency "+dep.newFilename());
            } else { // dep == null
                System.out.println("Found unknown jar in classpath, will keep: "+rhs);
              // If not, it's something the user has added, we just keep it.
              classpath.add(rhs);
            }
          }
        }
      } else if(lowcaseLine.equals("wrapper.restart.reload_configuration=true")) {
        writtenReload = true;
      } else if(lowcaseLine.startsWith("wrapper.anchorfile=")) {
          writtenAnchor = true;
      } else if(lowcaseLine.startsWith("wrapper.anchor.poll_interval=")) {
          writtenAnchorInterval = true;
      }
      if(!dontWrite)
        otherLines.add(line);
    }
    br.close();
   
    // Write classpath first
   
    if(mainRHS == null) {
      throw new UpdaterParserException(l10n("updateFailedNonStandardConfig",
          new String[] { "main" }, new String[] { Boolean.toString(mainRHS != null) } ));
    }
   
    // As above, we need to write ALL the dependencies BEFORE we write the main jar.
    int count = 1; // Classpath is 1-based.
    for(Dependency d : deps.dependencies) {
        System.out.println("Writing dependency "+d.newFilename()+" priority "+d.order());
      bw.write("wrapper.java.classpath."+count+"="+d.newFilename()+'\n');
      count++;
    }
   
    // Write the main jar.
    bw.write("wrapper.java.classpath."+count+"="+mainRHS+'\n');
    count++;
    for(String s : classpath) {
      bw.write("wrapper.java.classpath."+count+"="+s+'\n');
      count++;
    }
   
    for(String s : otherLines)
      bw.write(s+'\n');

    if(!writtenReload) {
      // Add it.
      bw.write("wrapper.restart.reload_configuration=TRUE\n");
    }
    if(!writtenAnchor) {
        bw.write("wrapper.anchorfile=Freenet.anchor\n");
    }
    if(!writtenAnchorInterval) {
        bw.write("wrapper.anchor.poll_interval=1\n");
    }
   
    bw.close();

    if(!newConfig.renameTo(oldConfig)) {
      if(!oldConfig.delete()) {
        throw new UpdaterParserException(l10n("updateFailedCannotDeleteOldConfig", "old", oldConfig.toString()));
      }
      if(!newConfig.renameTo(oldConfig)) {
        throw new UpdateCatastropheException(oldConfig, newConfig);
      }
    }
   
    // New config installed.
   
    System.err.println("Rewritten wrapper.conf for build "+deps.build+" and "+deps.dependencies.size()+" dependencies.");
  }

  private Dependency findDependencyByRHSFilename(File rhs) {
    String rhsName = rhs.getName().toLowerCase();
    // Check for files already in use.
    for(Dependency dep : deps.dependencies) {
      File f = dep.oldFilename();
      if(f == null) {
        // Not in use.
        continue;
      }
      if(rhs.equals(f)) return dep;
      if(rhsName.equals(f.getName().toLowerCase())) return dep;
    }
    // It may be already on the classpath even though it's a new file officially.
        for(Dependency dep : deps.dependencies) {
            File f = dep.newFilename();
            if(rhs.equals(f)) return dep;
            if(rhsName.equals(f.getName().toLowerCase())) return dep;
        }
        // Slightly more expensive test.
    for(Dependency dep : deps.dependencies) {
      Pattern p = dep.regex();
      if(p != null) {
        if(p.matcher(rhs.getName().toLowerCase()).matches()) return dep;
      }
    }
    return null;
  }

  public enum CHANGED {
    ALREADY, // Found the comment, so it has already been changed
    SUCCESS, // Succeeded
    FAIL // Failed e.g. due to unable to write wrapper.conf.
  }

  public static CHANGED tryIncreaseMemoryLimit(int extraMemoryMB,
      String markerComment) {
    // Rewrite wrapper.conf
    // Don't just write it out from properties; we want to keep it as close to what it was as possible.

    File oldConfig = new File("wrapper.conf");
    File newConfig = new File("wrapper.conf.new");
   
    if(!oldConfig.exists()) {
      File wrapperDir = new File("wrapper");
      if(wrapperDir.exists() && wrapperDir.isDirectory()) {
        File o = new File(wrapperDir, "wrapper.conf");
        if(o.exists()) {
          oldConfig = o;
          newConfig = new File(wrapperDir, "wrapper.conf.new");
        }
      }
    }
   
    FileInputStream fis = null;
    BufferedInputStream bis = null;
    InputStreamReader isr = null;
    BufferedReader br = null;
    FileOutputStream fos = null;
    OutputStreamWriter osw = null;
    BufferedWriter bw = null;
   
    boolean success = false;
   
    try {
   
    fis = new FileInputStream(oldConfig);
    bis = new BufferedInputStream(fis);
    isr = new InputStreamReader(bis);
    br = new BufferedReader(isr);
   
    fos = new FileOutputStream(newConfig);
    osw = new OutputStreamWriter(fos);
    bw = new BufferedWriter(osw);

    String line;
   
    while((line = br.readLine()) != null) {
     
      if(line.equals("#" + markerComment))
        return CHANGED.ALREADY;
     
      if(line.startsWith("wrapper.java.maxmemory=")) {
        try {
          int memoryLimit = Integer.parseInt(line.substring("wrapper.java.maxmemory=".length()));
          int newMemoryLimit = memoryLimit + extraMemoryMB;
          // There have been some cases where really high limits have caused the JVM to do bad things.
          if(newMemoryLimit > 2048) newMemoryLimit = 2048;
          bw.write('#' + markerComment + '\n');
          bw.write("wrapper.java.maxmemory="+newMemoryLimit+'\n');
          success = true;
          continue;
        } catch (NumberFormatException e) {
          // Grrrrr!
        }
      }
     
      bw.write(line+'\n');
    }
    br.close();
   
    } catch (IOException e) {
      newConfig.delete();
      System.err.println("Unable to rewrite wrapper.conf with new memory limit.");
      return CHANGED.FAIL;
    } finally {
      Closer.close(br);
      Closer.close(isr);
      Closer.close(bis);
      Closer.close(fis);
      Closer.close(bw);
      Closer.close(osw);
      Closer.close(fos);
    }
   
    if(success) {
      if(!newConfig.renameTo(oldConfig)) {
        if(!oldConfig.delete()) {
          System.err.println("Unable to move rewritten wrapper.conf with new memory limit "+newConfig+" over old config "+oldConfig+" : unable to delete old config");
          return CHANGED.FAIL;
        }
        if(!newConfig.renameTo(oldConfig)) {
          System.err.println("Old wrapper.conf deleted but new wrapper.conf cannot be renamed!");
          System.err.println("FREENET WILL NOT START UNTIL YOU RENAME "+newConfig+" to "+oldConfig);
          System.exit(NodeInitException.EXIT_BROKE_WRAPPER_CONF);
        }
      }
      System.err.println("Rewritten wrapper.conf for new memory limit");
      return CHANGED.SUCCESS;
    } else {
      newConfig.delete();
      return CHANGED.FAIL;
    }
  }

  public File getBackupJar() {
    return backupMainJar;
  }

}
TOP

Related Classes of freenet.node.updater.UpdateDeployContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.