package org.ecgine.gradle; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.ecgine.gradle.extensions.EcgineExtension; import org.gradle.api.GradleException; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.file.FileCollection; import org.gradle.api.internal.file.copy.CopySpecInternal; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.osgi.OsgiPlugin; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.compile.AbstractCompile; import org.gradle.plugins.ide.eclipse.EclipsePlugin; import org.json.JSONArray; import org.json.JSONObject; import org.xtend.gradle.XtendPlugin; import org.xtend.gradle.tasks.XtendCompile; /** * */ public class EcginePlugin implements Plugin<Project> { private Project project; @Override public void apply(final Project project) { this.project = project; // We need the node plugin to run and install the react-tools. project.getPlugins().apply(JavaPlugin.class); project.getPlugins().apply(EclipsePlugin.class); project.getPlugins().apply(OsgiPlugin.class); project.getPlugins().apply(XtendPlugin.class); // Add the install task to this project. // project.tasks.create(ReactInstallTask.NAME, ReactInstallTask.class ) // replace the jar task to this project. if (project.getRootProject() == project) { project.getTasks().create("ecgineLogin", EcgineLoginTask.class); project.getTasks().create("ecgineDeploy", EcgineDeployTask.class); project.getTasks().create("ecginePrepare", EcgineBundlesTask.class); project.getTasks().create("ecgineClientStart", EcgineClientStart.class); project.getTasks().create("ecgineServerStart", EcgineServerStart.class); project.getExtensions().create(EcgineExtension.NAME, EcgineExtension.class, project); project.getTasks().remove(project.getTasks().getByName("build")); } else { project.getTasks().getByName("jar").doFirst(this::jar); project.getTasks().getByName("compileXtend").doFirst(this::compileXtend); project.getTasks().getByName("compileJava").doFirst(this::compileJava); } } private void compileXtend(Task t) { XtendCompile c = (XtendCompile) t; List<File> existed = new ArrayList<>(); // Existing jars c.getXtendClasspath().forEach(existed::add); c.getClasspath().forEach(existed::add); List<File> collect = collectAllJars(t, existed); collect.sort((f1, f2) -> f1.getName().compareTo(f2.getName())); FileCollection allJars = project.files(collect); c.setClasspath(allJars); } private void compileJava(Task t) { AbstractCompile c = (AbstractCompile) t; List<File> existed = new ArrayList<>(); // Existing jars c.getClasspath().forEach(existed::add); List<File> collect = collectAllJars(t, existed); FileCollection allJars = project.files(collect); c.setClasspath(allJars); } private List<File> collectAllJars(Task c, List<File> existed) { Set<String> existedNames = new HashSet<>(); List<File> all = new ArrayList<>(); EcgineExtension root = (EcgineExtension) project.getRootProject().getExtensions().getByName("ecgine"); File plugins = new File(project.getRootDir(), root.getPlugins()); if (!plugins.exists()) { plugins.mkdirs(); } Map<String, String> dependencies = root.getBundles(); Map<String, JSONObject> jarDepends = EcgineUtils.readJarDependencies(project.getRootProject()); Set<String> allJarNames = new HashSet<>(); dependencies.forEach((k, v) -> { JSONObject json = jarDepends.get(k); if (json == null) { return; } if (!json.getString("specifiedVersion").equals(v)) { return; } if (!json.has("version")) { return; } addDependencies(jarDepends, allJarNames, existedNames, k, json); }); allJarNames.stream().map(s -> new File(plugins, s + ".jar")).filter(f -> f.exists()) .collect(Collectors.toCollection(() -> all)); existed.stream().filter(f -> !existedNames.contains(f.getName().split("-")[0])).forEach(all::add); return all; } private void addDependencies(Map<String, JSONObject> jarDepends, Set<String> allJarNames, Set<String> existedNames, String k, JSONObject json) { if (!json.has("version")) { return; } // Exists, add all dependencies if (!existedNames.contains(k)) { allJarNames.add(k + "_" + json.getString("version")); existedNames.add(k); } JSONArray array = json.getJSONArray("dependents"); array.forEach(a -> { JSONObject obj = jarDepends.get(a.toString()); addDependencies(jarDepends, allJarNames, existedNames, a.toString(), obj); }); } private final class Holder<T> { private T val; private Holder(T init) { val = init; } public T getVal() { return val; } public void setVal(T val) { this.val = val; } } private void jar(Task t) { File propsFile = new File(project.getProjectDir(), "build.properties"); if (!propsFile.exists()) { throw new GradleException("Can not create jar, 'build.properties' file is missing in project folder."); } Jar jar = (Jar) t; Holder<Boolean> manifestExculde = new Holder<Boolean>(false); // We need to exclude existing manifest Iterator<CopySpecInternal> it = jar.getRootSpec().getChildren().iterator(); it.hasNext(); it.next().eachFile(f -> { // Need to skip first manifest(system) if (!manifestExculde.getVal()) { manifestExculde.setVal(true); f.exclude(); } }); jar.getMetaInf().from(new File("./META-INF/MANIFEST.MF")); try { Properties props = new Properties(); props.load(new FileReader(propsFile)); String includes = props.getProperty("bin.includes"); String[] files = includes.split(","); File ecg = new File(project.getProjectDir(), "build/ecg"); ecg.mkdirs(); for (String s : files) { if (s.equals(".")) { continue; } File f = new File(project.getProjectDir(), s); File d = new File(ecg, s); if (f.isDirectory()) { FileUtils.copyDirectory(f, d); } else { FileUtils.copyFile(f, d); } } jar.from(ecg); // jar.from("./.").include(files); } catch (Exception e) { throw new GradleException("Unable to read properties file.", e); } jar.setArchiveName(project.getName() + ".jar"); jar.setDestinationDir(new File(project.getRootDir(), "build")); } }