Package freenet.node.updater

Source Code of freenet.node.updater.MainJarUpdater$DependencyJarFetcher

package freenet.node.updater;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Properties;

import freenet.client.FetchContext;
import freenet.client.FetchException;
import freenet.client.FetchException.FetchExceptionMode;
import freenet.client.FetchResult;
import freenet.client.async.ClientContext;
import freenet.client.async.ClientGetCallback;
import freenet.client.async.ClientGetter;
import freenet.client.events.ClientEvent;
import freenet.client.events.ClientEventListener;
import freenet.client.events.SplitfileProgressEvent;
import freenet.clients.fcp.FCPMessage;
import freenet.clients.fcp.ClientPut.COMPRESS_STATE;
import freenet.clients.http.QueueToadlet;
import freenet.keys.FreenetURI;
import freenet.l10n.NodeL10n;
import freenet.node.RequestClient;
import freenet.node.RequestStarter;
import freenet.node.Version;
import freenet.node.updater.MainJarDependenciesChecker.AtomicDeployer;
import freenet.node.updater.MainJarDependenciesChecker.Deployer;
import freenet.node.updater.MainJarDependenciesChecker.JarFetcher;
import freenet.node.updater.MainJarDependenciesChecker.JarFetcherCallback;
import freenet.node.updater.MainJarDependenciesChecker.MainJarDependencies;
import freenet.node.updater.UpdateOverMandatoryManager.UOMDependencyFetcher;
import freenet.node.updater.UpdateOverMandatoryManager.UOMDependencyFetcherCallback;
import freenet.node.useralerts.UserAlert;
import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.io.Closer;
import freenet.support.io.FileBucket;
import freenet.support.io.FileUtil;
import freenet.support.io.InsufficientDiskSpaceException;

public class MainJarUpdater extends NodeUpdater implements Deployer {
 
  static private volatile boolean logMINOR;
  static {
    Logger.registerClass(MainJarUpdater.class);
  }
 
  private final FetchContext dependencyCtx;
  private final ClientContext clientContext;

  MainJarUpdater(NodeUpdateManager manager, FreenetURI URI, int current, int min, int max, String blobFilenamePrefix) {
    super(manager, URI, current, min, max, blobFilenamePrefix);
    dependencyCtx = core.makeClient((short) 0, true, false).getFetchContext();
    dependencyCtx.allowSplitfiles = true;
    dependencyCtx.dontEnterImplicitArchives = false;
    dependencyCtx.maxNonSplitfileRetries = -1;
    dependencyCtx.maxSplitfileBlockRetries = -1;
    clientContext = core.clientContext;
    dependencies = new MainJarDependenciesChecker(this, manager.node.executor);
  }

  private final MainJarDependenciesChecker dependencies;
 
  @Override
  public String jarName() {
    return "freenet.jar";
  }
 
  public void start() {
    maybeProcessOldBlob();
    super.start();
  }
 
  @Override
  protected void maybeParseManifest(FetchResult result, int build) {
    // Do nothing.
  }

  @Override
  protected void processSuccess(int fetched, FetchResult result, File blob) {
    manager.onDownloadedNewJar(result.asBucket(), fetched, blob);
    // NodeUpdateManager expects us to dependencies *AFTER* we tell it about the new jar.
    parseDependencies(result, fetched);
  }

  @Override
  protected void onStartFetching() {
    manager.onStartFetching();
  }
 
  // Dependency handling.
 
  private HashSet<DependencyJarFetcher> fetchers = new HashSet<DependencyJarFetcher>();
  private HashSet<DependencyJarFetcher> essentialFetchers = new HashSet<DependencyJarFetcher>();

  protected void parseDependencies(Properties props, int build) {
    synchronized(fetchers) {
      fetchers.clear();
    }
    MainJarDependencies deps = dependencies.handle(props, build);
    if(deps != null) {
      manager.onDependenciesReady(deps);
    }
  }

  @Override
  public void deploy(MainJarDependencies deps) {
    manager.onDependenciesReady(deps);
  }
 
  /** Glue code. */
  private class DependencyJarFetcher implements JarFetcher, ClientGetCallback, RequestClient, ClientEventListener {

    private final File filename;
    private final ClientGetter getter;
    private SplitfileProgressEvent lastProgress;
    private final JarFetcherCallback cb;
    private boolean fetched;
    private final byte[] expectedHash;
    private final long expectedLength;
    private final boolean essential;
    private final File tempFile;
    private UOMDependencyFetcher uomFetcher;
    private final boolean executable;
   
    DependencyJarFetcher(File filename, FreenetURI chk, long expectedLength, byte[] expectedHash, JarFetcherCallback cb, boolean essential, boolean executable) throws FetchException {
      FetchContext myCtx = new FetchContext(dependencyCtx, FetchContext.IDENTICAL_MASK);
      File parent = filename.getParentFile();
      if(parent == null) parent = new File(".");
      try {
        tempFile = File.createTempFile(filename.getName(), NodeUpdateManager.TEMP_FILE_SUFFIX, parent);
      } catch (InsufficientDiskSpaceException e) {
          throw new FetchException(FetchExceptionMode.NOT_ENOUGH_DISK_SPACE);
      } catch (IOException e) {
        throw new FetchException(FetchExceptionMode.BUCKET_ERROR, "Cannot create temp file for "+filename+" in "+parent+" - disk full? permissions problem?");
      }
      getter = new ClientGetter(this, 
          chk, myCtx, RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS,
          new FileBucket(tempFile, false, false, false, false), null, null);
      myCtx.eventProducer.addEventListener(this);
      this.cb = cb;
      this.filename = filename;
      this.expectedHash = expectedHash;
      this.expectedLength = expectedLength;
      this.essential = essential;
      this.executable = executable;
    }
   
    @Override
    public void cancel() {
      final UOMDependencyFetcher f;
      synchronized(this) {
        fetched = true;
        f = uomFetcher;
      }
      MainJarUpdater.this.node.executor.execute(new Runnable() {

        @Override
        public void run() {
          getter.cancel(clientContext);
          if(f != null) f.cancel();
        }
       
      });
    }
   
    @Override
    public boolean persistent() {
      return false;
    }
   
    @Override
    public boolean realTimeFlag() {
      return false;
    }
   
    @Override
    public void onSuccess(FetchResult result, ClientGetter state) {
      synchronized(this) {
        if(fetched) {
          tempFile.delete();
          return;
        }
        fetched = true;
      }
            if(!MainJarDependenciesChecker.validFile(tempFile, expectedHash, expectedLength, executable)) {
                Logger.error(this, "Unable to download dependency "+filename+" : not the expected size or hash!");
                System.err.println("Download of "+filename+" for update failed because temp file appears to be corrupted!");
                if(cb != null)
                    cb.onFailure(new FetchException(FetchExceptionMode.BUCKET_ERROR, "Downloaded jar from Freenet but failed consistency check: "+tempFile+" length "+tempFile.length()+" "));
                tempFile.delete();
                return;
            }
      if(!FileUtil.renameTo(tempFile, filename)) {
        Logger.error(this, "Unable to rename temp file "+tempFile+" to "+filename);
        System.err.println("Download of "+filename+" for update failed because cannot rename from "+tempFile);
        if(cb != null)
            cb.onFailure(new FetchException(FetchExceptionMode.BUCKET_ERROR, "Unable to rename temp file "+tempFile+" to "+filename));
                tempFile.delete();
        return;
      }
      if(cb != null) cb.onSuccess();
    }
   
    @Override
    public void onFailure(FetchException e, ClientGetter state) {
      tempFile.delete();
      synchronized(this) {
        if(fetched) return;
      }
      if(cb != null) cb.onFailure(e);
    }
   
    @Override
    public synchronized void receive(ClientEvent ce, ClientContext context) {
      if(ce instanceof SplitfileProgressEvent)
        lastProgress = (SplitfileProgressEvent) ce;
    }
   
    private void start() throws FetchException {
      getter.start(clientContext);
    }

    public synchronized HTMLNode renderRow() {
      HTMLNode row = new HTMLNode("tr");
      row.addChild("td").addChild("p", filename.toString());
     
      if(uomFetcher != null)
        row.addChild("td").addChild("#", l10n("fetchingFromUOM"));
      else if(lastProgress == null)
        row.addChild(QueueToadlet.createProgressCell(false, true, COMPRESS_STATE.WORKING, 0, 0, 0, 0, 0, false, false));
      else
        row.addChild(QueueToadlet.createProgressCell(false,
            true, COMPRESS_STATE.WORKING, lastProgress.succeedBlocks, lastProgress.failedBlocks, lastProgress.fatallyFailedBlocks, lastProgress.minSuccessfulBlocks, lastProgress.totalBlocks, lastProgress.finalizedTotal, false));
      return row;
    }

    public void fetchFromUOM() {
      synchronized(this) {
        if(fetched) return;
        if(!essential) return;
      }
      UOMDependencyFetcher f = manager.uom.fetchDependency(expectedHash, expectedLength, filename, executable,
          new UOMDependencyFetcherCallback() {

            @Override
            public void onSuccess() {
              synchronized(DependencyJarFetcher.this) {
                if(fetched) return;
                fetched = true;
              }
              if(cb != null) cb.onSuccess();
            }
           
      });
      synchronized(this) {
        if(uomFetcher != null) {
          Logger.error(this, "Started UOMFetcher twice for "+filename, new Exception("error"));
          return;
        }
        uomFetcher = f;
      }
    }

      @Override
      public void onResume(ClientContext context) {
          // Do nothing. Not persistent.
      }

        @Override
        public RequestClient getRequestClient() {
            return this;
        }

  }
 
  @Override
  public JarFetcher fetch(FreenetURI uri, File downloadTo,
      long expectedLength, byte[] expectedHash, JarFetcherCallback cb, int build, boolean essential, boolean executable) throws FetchException {
    if(essential)
      System.out.println("Fetching "+downloadTo+" needed for new Freenet update "+build);
    else if(build != 0) // build 0 means it's a preload or a multi-file update.
      System.out.println("Preloading "+downloadTo+" needed for new Freenet update "+build);
    if(logMINOR) Logger.minor(this, "Fetching "+uri+" to "+downloadTo+" for next update");
    DependencyJarFetcher fetcher = new DependencyJarFetcher(downloadTo, uri, expectedLength, expectedHash, cb, essential, executable);
    synchronized(fetchers) {
      fetchers.add(fetcher);
      if(essential)
        essentialFetchers.add(fetcher);
    }
    fetcher.start();
    if(manager.uom.fetchingUOM()) {
      if(essential)
        fetcher.fetchFromUOM();
    }
    return fetcher;
  }
 
  public void onStartFetchingUOM() {
    DependencyJarFetcher[] f;
    synchronized(fetchers) {
      f = fetchers.toArray(new DependencyJarFetcher[fetchers.size()]);
    }
    for(DependencyJarFetcher fetcher : f)
      fetcher.fetchFromUOM();
  }
 
  public void renderProperties(HTMLNode alertNode) {
    synchronized(fetchers) {
      if(!fetchers.isEmpty()) {
        alertNode.addChild("p", l10n("fetchingDependencies")+":");
        HTMLNode table = alertNode.addChild("table");
        for(DependencyJarFetcher f : fetchers) {
          table.addChild(f.renderRow());
        }
      }
    }
  }

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

  public boolean brokenDependencies() {
    return dependencies.isBroken();
  }

  public void cleanupDependencies() {
    InputStream is = getClass().getResourceAsStream("/"+DEPENDENCIES_FILE);
    if(is == null) {
      System.err.println("Can't find dependencies file. Other nodes will not be able to use Update Over Mandatory through this one.");
      return;
    }
    Properties props = new Properties();
    try {
      props.load(is);
    } catch (IOException e) {
      System.err.println("Can't read dependencies file. Other nodes will not be able to use Update Over Mandatory through this one.");
      return;
    } finally {
      Closer.close(is);
    }
    dependencies.cleanup(props, this, Version.buildNumber());
  }

  @Override
  public void addDependency(byte[] expectedHash, File filename) {
    manager.uom.addDependency(expectedHash, filename);
  }

    @Override
    public void reannounce() {
        this.manager.broadcastUOMAnnouncesNew();
        this.manager.broadcastUOMAnnouncesOld();
    }

    @Override
    public void multiFileReplaceReadyToDeploy(final MainJarDependenciesChecker.AtomicDeployer atomicDeployer) {
        if(this.manager.isAutoUpdateAllowed()) {
            atomicDeployer.deployMultiFileUpdateOffThread();
        } else {
            final long now = System.currentTimeMillis();
            System.err.println("Not deploying multi-file update for "+atomicDeployer.name+" because auto-update is not enabled.");
            node.clientCore.alerts.register(new UserAlert() {

                private String l10n(String key) {
                    return NodeL10n.getBase().getString("MainJarUpdater.ConfirmMultiFileUpdater."+key);
                }
               
                @Override
                public boolean userCanDismiss() {
                    return true;
                }

                @Override
                public String getTitle() {
                    return l10n("title."+atomicDeployer.name);
                }

                @Override
                public String getText() {
                    return l10n("text."+atomicDeployer.name);
                }

                @Override
                public HTMLNode getHTMLText() {
                    return new HTMLNode("p", getText());
                    // FIXME separate button, then the alert could be dismissable? Only useful if it's permanently dismissable though, which means a config setting as well...
                }

                @Override
                public String getShortText() {
                    return getTitle();
                }

                @Override
                public short getPriorityClass() {
                    return UserAlert.ERROR;
                }

                @Override
                public boolean isValid() {
                    return true;
                }

                @Override
                public void isValid(boolean validity) {
                    // Ignore
                }

                @Override
                public String dismissButtonText() {
                    return NodeL10n.getBase().getDefaultString("UpdatedVersionAvailableUserAlert.updateNowButton");
                }

                @Override
                public boolean shouldUnregisterOnDismiss() {
                    return true;
                }

                @Override
                public void onDismiss() {
                    atomicDeployer.deployMultiFileUpdateOffThread();
                }

                @Override
                public String anchor() {
                    return "multi-file-update-confirm-"+atomicDeployer.name;
                }

                @Override
                public boolean isEventNotification() {
                    return false;
                }

                @Override
                public FCPMessage getFCPMessage() {
                    return null;
                }

                @Override
                public long getUpdatedTime() {
                    return now;
                }
               
            });
        }
    }

}
TOP

Related Classes of freenet.node.updater.MainJarUpdater$DependencyJarFetcher

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.