/* * 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.zip.generator; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import neembuu.uploader.utils.HashUtil; import org.apache.commons.io.FilenameUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import shashaank.smallmodule.SmallModule; /** * Make operations to create the zip files. * @author davidepastore */ public class NUZipFileGenerator { private final Path[]uploadersDirectories; private final Path outputDirectory; private final Index index; private final URLClassLoader classLoader; private final Environment env; /*public NUZipFileGenerator(File gitDirectory, File outputDirectory,String[]modules, String[]uploaderModuleName) {*/ public NUZipFileGenerator(Environment env) { this.env = env; uploadersDirectories = new Path[env.modulesToCheckForExportibles().length]; Path gitDirectory = Paths.get(env.gitDirectory()); for (int i = 0; i < env.modulesToCheckForExportibles().length; i++) { String uploadermod = env.modulesToCheckForExportibles()[i]; uploadersDirectories[i] = gitDirectory .resolve("modules/"+uploadermod+ "/build/"); } this.outputDirectory = Paths.get(env.outputDirectory()); classLoader = l(env.sortedListOfModulesToCompile(),gitDirectory); index = new Index(this.outputDirectory.resolve("index.json"),env); } private static URLClassLoader l(String[]modules,Path pth){ try{ URL[]u = new URL[modules.length]; for (int i = 0; i < u.length; i++) { u[i]=pth.resolve("modules/"+modules[i]+ "/build/").toUri().toURL(); } return new URLClassLoader(u); }catch(Exception a){ throw new IllegalStateException(a); } } public void createZipFiles() { Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.INFO, "Create the zip files"); try { index.intialize(); walkOverAllFiles(); index.complete(); } catch (Exception ex) { Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.SEVERE, null, ex); } } private void walkOverAllFiles()throws IOException{ for (final Path uploadersDirectory : uploadersDirectories) { Files.walkFileTree(uploadersDirectory, new FileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {return FileVisitResult.CONTINUE;} @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { exc.printStackTrace();return FileVisitResult.CONTINUE;} @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {return FileVisitResult.CONTINUE;} @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if(file.getFileName().toString().endsWith(".class")){ visitClassFile(file, attrs,uploadersDirectory); } return FileVisitResult.CONTINUE; } }); } } private Class load(Path f,Path uploadersDirectory)throws Exception{ String p = uploadersDirectory.normalize().relativize(f.normalize()).toString(); p = p.replace(File.separatorChar, '.'); p = p.substring(0,p.lastIndexOf('.'));//to remove .class return classLoader.loadClass(p); } private void visitClassFile(Path file, BasicFileAttributes attrs, Path uploadersDirectory)throws IOException{ Class c; try{ c = load(file,uploadersDirectory); }catch(Throwable a){ System.err.println("skipping "+file); a.printStackTrace(); return; } SmallModule moduleDescription = (SmallModule)c.getAnnotation(SmallModule.class); if(moduleDescription==null){ System.out.println("not a small module"+file); return; } if(moduleDescription.ignore()){ Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.INFO, "Ignoring : {0}", FilenameUtils.removeExtension(c.getName())); return; } handleSmallModule(moduleDescription, c,uploadersDirectory); } private void handleSmallModule(SmallModule moduleDescription,Class clzz,Path uploadersDirectory)throws IOException{ Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.INFO, "Create zip for: {0}", clzz.getName()); Path outputModulePath = outputDirectory .resolve("sm") .resolve(moduleDescription.name()+".zip"); Files.createDirectories(outputModulePath.getParent()); while(Files.exists(outputModulePath)){ try{Files.deleteIfExists(outputModulePath);}catch(Exception a){a.printStackTrace();} } Map<String, String> env = new HashMap<>(); env.put("create", "true");boolean destroyZipIsCorrupt = false; URI uri = URI.create("jar:" + outputModulePath.toUri()); try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { smallModuleCreateZip(fs, moduleDescription, clzz,uploadersDirectory); }catch(Exception e){ e.printStackTrace(); destroyZipIsCorrupt = true; } if(destroyZipIsCorrupt){ Files.delete(outputModulePath); }else { String hash = HashUtil.hashFile(outputModulePath.toFile(), index.getHashalgorithm()); try{ index.addSmallModule(moduleDescription,hash); }catch(Exception a){ a.printStackTrace();//ignore } } } private void smallModuleCreateZip(FileSystem fs,SmallModule moduleDescription,Class clzz,Path uploadersDirectory)throws IOException,JSONException{ JSONObject metaData = makeMetaData(moduleDescription); Files.write(fs.getPath("SmallModule.json"),metaData.toString(3).getBytes()); //zip(fs, moduleDescription.interfaces()); these are already //in the classpath of NU, so we need not repackage them in zip zip(fs, moduleDescription.exports(),uploadersDirectory); zip(fs, moduleDescription.dependsOn(),uploadersDirectory); if(moduleDescription.jarsRequired()!=null && moduleDescription.jarsRequired().length > 0){ throw new IllegalStateException("jars not supported as of now"); /*Path extraJarRequiredPath = outputDirectory.resolve("jarsRequired"); Files.createDirectories(extraJarRequiredPath);*/ } } private void zip(FileSystem fs,Class[]z,Path uploadersDirectory)throws IOException{ for (Class c : z) { String relClassPath = c.getName().replace('.', File.separatorChar); Path pathInZip = fs.getPath(relClassPath).getParent(); Files.createDirectories(pathInZip);//creates all directory entires if required. handleClassEntry(pathInZip, c, fs,uploadersDirectory); } } private void handleClassEntry(Path pathInZip,final Class c,FileSystem fs,Path uploadersDirectory)throws IOException { Path classLocationOnDisk = uploadersDirectory.resolve(pathInZip.toString()); DirectoryStream<Path> ds = Files.newDirectoryStream(classLocationOnDisk,new DirectoryStream.Filter<Path>() { @Override public boolean accept(Path entry) throws IOException { String fn = entry.getFileName().toString(); String cn = c.getSimpleName(); return fn.equals(cn+".class") || fn.startsWith(cn+"$"); } }); for(Path p : ds){ byte[]b=Files.readAllBytes(p); Files.write(pathInZip.resolve(p.getFileName().toString()), b); } // say we want to zie SomeClass.class // then we also need to zip SomeClass$1.class // That is, we also need to zip inner classes and inner annoymous classes // into the zip as well } private JSONObject makeMetaData(SmallModule moduleDescription)throws JSONException{ JSONObject metaData = new JSONObject(); metaData.put("name", moduleDescription.name()); JSONArray exports = new JSONArray(); assert moduleDescription.exports().length!=moduleDescription.interfaces().length; for (int j = 0; j < moduleDescription.exports().length; j++) { JSONObject exportableItem = new JSONObject(); exportableItem.put("implementation", moduleDescription.exports()[j].getName()); exportableItem.put("interface", moduleDescription.interfaces()[j].getName()); exports.put(exportableItem); } metaData.put("exports", exports); return metaData; } }