/* * Copyright 2015 Shashank Tulsyan <shashaank at neembuu.com>. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package neembuu.uploader.external; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.Logger; import neembuu.reactivethread.CompletionCallback; import neembuu.reactivethread.ReactiveThread; import static neembuu.reactivethread.Utils.waitTill; import neembuu.uploader.external.UpdateProgressUI.Content; import neembuu.uploader.interfaces.Account; import neembuu.uploader.interfaces.Uploader; import neembuu.uploader.utils.HashUtil; import org.json.JSONException; /** * * @author Shashank */ public class UploaderPlugin { private final SmallModuleEntry sme; private final Path root; private volatile Class<? extends Uploader> uploader; private volatile Class<? extends Account> account; private volatile ZipFSClassLoader zfscl; private final LinkedList<PluginDestructionListener> listeners = new LinkedList<PluginDestructionListener>(); public UploaderPlugin(SmallModuleEntry sme, Path root) { this.sme = sme; this.root = root; } public SmallModuleEntry getSme() { return sme; } boolean intitalized(){ synchronized (sme){return uploader!=null;} } public Class<? extends Uploader> getUploader(PluginDestructionListener pdl) { if(pdl==null)throw new NullPointerException("If you are taking a reference of " + " uploader class, also make sure you remove reference once destory is called."); if(!listeners.contains(pdl)) listeners.add(pdl); return uploader; } public Class<? extends Account> getAccount(PluginDestructionListener pdl) { if(pdl==null)throw new NullPointerException("If you are taking a reference of " + " account class, also make sure you remove reference once destory is called."); if(!listeners.contains(pdl)) listeners.add(pdl); return account; } volatile ReactiveThread rt=null; private final double httpDone = 0.6d; void create(final Content c)throws IOException,JSONException,ClassNotFoundException{ synchronized (sme){ if(uploader!=null)return; // already created if(locallyPresent(root,sme)!=LocallyPresent.PRESENT){ downloadPlugin(c); }else{ try { postCreateImpl(getLocalPath(root,sme),c); } catch (Exception ex) { Logger.getLogger(UploaderPlugin.class.getName()).log(Level.SEVERE, null, ex); } } } } private void downloadPlugin(final Content c){ final String url = sme.getIndex().getBasepath()+sme.getRelpth(); final Path zipPath = getLocalPath(root,sme); rt = ReactiveThread.create(new Runnable() { @Override public void run() { try{ //System.out.println("doing "+sme.getName()); HttpUtil.update(url, zipPath,c); ReactiveThread.get().updateProgress(httpDone); postCreateImpl(zipPath,c); //System.out.println("done "+sme.getName()); }catch(Exception a){throw new IllegalStateException(a);} } }, new CompletionCallback() { @Override public void completed(ReactiveThread rt) {rt = null; c.done();} @Override public void canceled(ReactiveThread rt) {rt = null; c.done();} @Override public void progressed(ReactiveThread rt) {c.setProgress(rt.getProgress());} }); rt.setName(sme.getName()+" updating"); rt.start(); try{ if(!waitTill(rt, 8000, 100, httpDone)){ rt.cancel(); } if(!waitTill(rt, 24000, 100, 1d)){ rt.cancel(); } }catch(Exception ie){/*ignore*/} } public enum LocallyPresent { ABSENT, HASH_FAIL, PRESENT } public static LocallyPresent locallyPresent(Path root,SmallModuleEntry sme)throws IOException{ Path zipPath = getLocalPath(root, sme); if(!Files.exists(zipPath.getParent())){ Files.createDirectories(zipPath.getParent()); return LocallyPresent.ABSENT; } return (checkHash(zipPath,sme)); } private void postCreateImpl(Path zipPath,Content c)throws Exception{ zfscl = new ZipFSClassLoader(zipPath); SmallModuleMetadata metadata = new SmallModuleMetadata(zfscl.getFs().getPath("SmallModule.json")); uploader = (Class<Uploader>)zfscl.findClass(metadata.getUploaderClassName()); account = metadata.getAccountsClassName()==null?null: (Class<Account>)zfscl.findClass(metadata.getAccountsClassName()); c.done(); } public void destroy()throws Exception{ Exception total = new Exception("Destory failed"); try{zfscl.getFs().close();}catch(Exception a){total.addSuppressed(a);} try{Files.delete(zfscl.getZipPath());}catch(Exception a){total.addSuppressed(a);} try { for (PluginDestructionListener pdl : listeners) { try { pdl.destroyed(); } catch (Exception a) { total.addSuppressed(a); } } } catch (NullPointerException e) { total.addSuppressed(e);} listeners.clear(); if(total.getSuppressed().length > 0){ throw total; } } static Path getLocalPath(Path root,SmallModuleEntry sme){ return root.resolve(sme.getRelpth()); } private static LocallyPresent checkHash(Path localCopy,SmallModuleEntry sme){ if(!Files.exists(localCopy))return LocallyPresent.ABSENT; String hash = HashUtil.hashFile(localCopy.toFile(), sme.getIndex().getHashalgorithm()); return (hash.equals(sme.getHash()))?LocallyPresent.PRESENT:LocallyPresent.HASH_FAIL; } }