/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.tools; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.FileComparator; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.xml.Document; import com.liferay.portal.kernel.xml.Element; import com.liferay.portal.kernel.xml.SAXReader; import com.liferay.portal.util.FileImpl; import com.liferay.portal.util.PropsValues; import com.liferay.portal.xml.SAXReaderImpl; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.apache.oro.io.GlobFilenameFilter; import org.apache.tools.ant.DirectoryScanner; /** * @author Alexander Chow * @author Brian Wing Shun Chan */ public class PluginsEnvironmentBuilder { public static void main(String[] args) throws Exception { try { File dir = new File(System.getProperty("plugins.env.dir")); new PluginsEnvironmentBuilder(dir); } catch (Exception e) { e.printStackTrace(); } } public PluginsEnvironmentBuilder(File dir) throws Exception { DirectoryScanner directoryScanner = new DirectoryScanner(); directoryScanner.setBasedir(dir); directoryScanner.setIncludes( new String[] {"**\\liferay-plugin-package.properties"}); directoryScanner.scan(); String dirName = dir.getCanonicalPath(); for (String fileName : directoryScanner.getIncludedFiles()) { setupWarProject(dirName, fileName); } directoryScanner = new DirectoryScanner(); directoryScanner.setBasedir(dir); directoryScanner.setIncludes(new String[] {"**\\build.xml"}); directoryScanner.scan(); for (String fileName : directoryScanner.getIncludedFiles()) { String content = _fileUtil.read(dirName + "/" + fileName); boolean osgiProject = false; if (content.contains("../build-common-osgi-plugin.xml\" />") || content.contains( "../tools/sdk/build-common-osgi-plugin.xml\" />")) { osgiProject = true; } boolean sharedProject = false; if (content.contains( "<import file=\"../build-common-shared.xml\" />") || content.contains("../tools/sdk/build-common-shared.xml\" />")) { sharedProject = true; } List<String> dependencyJars = Collections.emptyList(); if (osgiProject) { int x = content.indexOf("osgi.ide.dependencies"); if (x != -1) { x = content.indexOf("value=\"", x); x = content.indexOf("\"", x); int y = content.indexOf("\"", x + 1); dependencyJars = Arrays.asList( StringUtil.split(content.substring(x + 1, y))); } } if (osgiProject || sharedProject) { setupJarProject( dirName, fileName, dependencyJars, sharedProject); } } } protected void addClasspathEntry(StringBundler sb, String jar) { addClasspathEntry(sb, jar, null); } protected void addClasspathEntry( StringBundler sb, String jar, Map<String, String> attributes) { sb.append("\t<classpathentry kind=\"lib\" path=\""); sb.append(jar); if ((attributes == null) || attributes.isEmpty()) { sb.append("\" />\n"); return; } sb.append("\">\n\t\t<attributes>\n"); for (Map.Entry<String, String> entry : attributes.entrySet()) { sb.append("\t\t\t<attribute name=\""); sb.append(entry.getKey()); sb.append("\" value=\""); sb.append(entry.getValue()); sb.append("\" />\n"); } sb.append("\t\t</attributes>\n\t</classpathentry>\n"); } protected void addIvyCacheJar( StringBundler sb, String ivyDirName, String dependencyName, String version) throws Exception { String string = sb.toString(); if (string.contains(dependencyName)) { System.out.println( "Skipping duplicate " + dependencyName + " " + version); return; } System.out.println("Adding " + dependencyName + " " + version); if (version.equals("latest.integration")) { File dir = new File(ivyDirName + "/cache/" + dependencyName); File[] files = dir.listFiles(); Arrays.sort(files, new FileComparator()); for (int i = files.length - 1; i >= 0; i--) { File file = files[i]; if (!file.isFile()) { continue; } String fileName = file.getName(); if (!fileName.endsWith(".xml")) { continue; } version = fileName.substring(4, fileName.length() - 4); System.out.println( "Substituting " + version + " for latest.integration"); } } String ivyFileName = ivyDirName + "/cache/" + dependencyName + "/ivy-" + version + ".xml"; if (_fileUtil.exists(ivyFileName)) { Document document = _saxReader.read(new File(ivyFileName)); Element rootElement = document.getRootElement(); Element dependenciesElement = rootElement.element("dependencies"); if (dependenciesElement != null) { List<Element> dependencyElements = dependenciesElement.elements( "dependency"); for (Element dependencyElement : dependencyElements) { String conf = GetterUtil.getString( dependencyElement.attributeValue("conf")); if (!conf.startsWith("compile")) { continue; } String name = GetterUtil.getString( dependencyElement.attributeValue("name")); String org = GetterUtil.getString( dependencyElement.attributeValue("org")); String rev = GetterUtil.getString( dependencyElement.attributeValue("rev")); string = sb.toString(); if (string.contains(name)) { continue; } addIvyCacheJar(sb, ivyDirName, org + "/" + name, rev); } } } String dirName = ivyDirName + "/cache/" + dependencyName + "/bundles"; if (!_fileUtil.exists(dirName)) { dirName = ivyDirName + "/cache/" + dependencyName + "/jars"; if (!_fileUtil.exists(dirName)) { System.out.println("Unable to find jars in " + dirName); return; } } File dir = new File(dirName); File[] files = dir.listFiles(); for (File file : files) { if (!file.isFile()) { continue; } String fileName = file.getName(); if (!fileName.endsWith("-" + version + ".jar")) { continue; } int index = dirName.indexOf("/.ivy"); String eclipseRelativeDirName = "/portal" + dirName.substring(index); addClasspathEntry(sb, eclipseRelativeDirName + "/" + fileName); return; } System.out.println( "Unable to find jars in " + dirName + " for " + version); } protected void addIvyCacheJars( StringBundler sb, String content, String ivyDirName) throws Exception { Document document = _saxReader.read(content); Element rootElement = document.getRootElement(); Element dependenciesElement = rootElement.element("dependencies"); List<Element> dependencyElements = dependenciesElement.elements( "dependency"); for (Element dependencyElement : dependencyElements) { String conf = GetterUtil.getString( dependencyElement.attributeValue("conf")); if (!conf.equals("test->default")) { continue; } String name = GetterUtil.getString( dependencyElement.attributeValue("name")); String org = GetterUtil.getString( dependencyElement.attributeValue("org")); String rev = GetterUtil.getString( dependencyElement.attributeValue("rev")); addIvyCacheJar(sb, ivyDirName, org + "/" + name, rev); } } protected List<String> getCommonJars() { List<String> jars = new ArrayList<>(); jars.add("commons-logging.jar"); jars.add("log4j.jar"); jars.add("util-bridges.jar"); jars.add("util-java.jar"); jars.add("util-taglib.jar"); return jars; } protected List<String> getImportSharedJars(File projectDir) throws Exception { File buildXmlFile = new File(projectDir, "build.xml"); String content = _fileUtil.read(buildXmlFile); int x = content.indexOf("import.shared"); if (x == -1) { return new ArrayList<>(); } x = content.indexOf("value=\"", x); x = content.indexOf("\"", x); int y = content.indexOf("\" />", x); if ((x == -1) || (y == -1)) { return new ArrayList<>(); } String[] importShared = StringUtil.split(content.substring(x + 1, y)); if (importShared.length == 0) { return new ArrayList<>(); } List<String> jars = new ArrayList<>(); for (String currentImportShared : importShared) { jars.add(currentImportShared + ".jar"); File currentImportSharedLibDir = new File( projectDir, "../../shared/" + currentImportShared + "/lib"); if (!currentImportSharedLibDir.exists()) { continue; } for (File file : currentImportSharedLibDir.listFiles()) { jars.add(file.getName()); } } return jars; } protected List<String> getPortalDependencyJars(Properties properties) { String[] dependencyJars = StringUtil.split( properties.getProperty( "portal-dependency-jars", properties.getProperty("portal.dependency.jars"))); return ListUtil.toList(dependencyJars); } protected List<String> getRequiredDeploymentContextsJars( File libDir, Properties properties) throws Exception { List<String> jars = new ArrayList<>(); String[] requiredDeploymentContexts = StringUtil.split( properties.getProperty("required-deployment-contexts")); for (String requiredDeploymentContext : requiredDeploymentContexts) { if (_fileUtil.exists( libDir.getCanonicalPath() + "/" + requiredDeploymentContext + "-service.jar")) { jars.add(requiredDeploymentContext + "-service.jar"); } } return jars; } protected boolean hasModulesGitIgnore(String dirName) { int index = dirName.indexOf("/modules/"); if (index == -1) { return false; } return _fileUtil.exists( dirName.substring(0, index) + "/modules/.gitignore"); } protected void setupJarProject( String dirName, String fileName, List<String> dependencyJars, boolean sharedProject) throws Exception { File buildFile = new File(dirName + "/" + fileName); File projectDir = new File(buildFile.getParent()); File libDir = new File(projectDir, "lib"); if (!libDir.exists()) { libDir = new File(projectDir, "docroot/WEB-INF/lib"); } writeEclipseFiles(libDir, projectDir, dependencyJars); List<String> importSharedJars = getImportSharedJars(projectDir); if (sharedProject) { if (!importSharedJars.contains("portal-compat-shared.jar")) { importSharedJars.add("portal-compat-shared.jar"); } } File gitignoreFile = new File( projectDir.getCanonicalPath() + "/.gitignore"); if (hasModulesGitIgnore(dirName)) { gitignoreFile.delete(); return; } String[] gitIgnores = importSharedJars.toArray( new String[importSharedJars.size()]); for (int i = 0; i < gitIgnores.length; i++) { String gitIgnore = gitIgnores[i]; gitIgnore = "/lib/" + gitIgnore; gitIgnores[i] = gitIgnore; } if (gitIgnores.length > 0) { System.out.println("Updating " + gitignoreFile); _fileUtil.write(gitignoreFile, StringUtil.merge(gitIgnores, "\n")); } } protected void setupWarProject(String dirName, String fileName) throws Exception { File propertiesFile = new File(dirName + "/" + fileName); Properties properties = new Properties(); properties.load(new FileInputStream(propertiesFile)); Set<String> jars = new TreeSet<>(); jars.addAll(getCommonJars()); List<String> dependencyJars = getPortalDependencyJars(properties); jars.addAll(dependencyJars); File projectDir = new File(propertiesFile.getParent() + "/../.."); jars.addAll(getImportSharedJars(projectDir)); File libDir = new File(propertiesFile.getParent() + "/lib"); jars.addAll(getRequiredDeploymentContextsJars(libDir, properties)); writeEclipseFiles(libDir, projectDir, dependencyJars); String libDirPath = StringUtil.replace( libDir.getPath(), CharPool.BACK_SLASH, CharPool.SLASH); List<String> ignores = ListUtil.fromFile( libDir.getCanonicalPath() + "/../.gitignore"); if (libDirPath.contains("/ext/") || ignores.contains("/lib")) { return; } File gitignoreFile = new File( libDir.getCanonicalPath() + "/.gitignore"); System.out.println("Updating " + gitignoreFile); String[] gitIgnores = jars.toArray(new String[jars.size()]); for (int i = 0; i < gitIgnores.length; i++) { String gitIgnore = gitIgnores[i]; if (Validator.isNotNull(gitIgnore) && !gitIgnore.startsWith("/")) { gitIgnores[i] = "/" + gitIgnore; } } _fileUtil.write(gitignoreFile, StringUtil.merge(gitIgnores, "\n")); } protected void writeClasspathFile( File libDir, List<String> dependencyJars, String projectDirName, String projectName, boolean javaProject) throws Exception { File classpathFile = new File(projectDirName + "/.classpath"); if (!javaProject) { classpathFile.delete(); return; } Set<String> globalJars = new LinkedHashSet<>(); List<String> portalJars = new ArrayList<>(); Set<String> extGlobalJars = new LinkedHashSet<>(); Set<String> extPortalJars = new LinkedHashSet<>(); String libDirPath = StringUtil.replace( libDir.getPath(), CharPool.BACK_SLASH, CharPool.SLASH); if (libDirPath.contains("/ext/")) { FilenameFilter filenameFilter = new GlobFilenameFilter("*.jar"); for (String dirName : new String[] {"global", "portal"}) { File file = new File(libDirPath + "/../ext-lib/" + dirName); List<String> jars = ListUtil.toList(file.list(filenameFilter)); if (dirName.equals("global")) { extGlobalJars.addAll(ListUtil.sort(jars)); File dir = new File(PropsValues.LIFERAY_LIB_GLOBAL_DIR); String[] fileNames = dir.list(filenameFilter); globalJars.addAll( ListUtil.sort(ListUtil.toList(fileNames))); globalJars.removeAll(extGlobalJars); } else if (dirName.equals("portal")) { extPortalJars.addAll(ListUtil.sort(jars)); File dir = new File(PropsValues.LIFERAY_LIB_PORTAL_DIR); String[] fileNames = dir.list(filenameFilter); portalJars.addAll( ListUtil.sort(ListUtil.toList(fileNames))); portalJars.removeAll(extPortalJars); } } } else { globalJars.add("portlet.jar"); portalJars.addAll(dependencyJars); portalJars.add("bnd.jar"); portalJars.add("commons-logging.jar"); portalJars.add("log4j.jar"); portalJars = ListUtil.unique(portalJars); Collections.sort(portalJars); } String[] customJarsArray = libDir.list(new GlobFilenameFilter("*.jar")); List<String> customJars = null; if (customJarsArray != null) { customJars = ListUtil.toList(customJarsArray); for (String jar : portalJars) { customJars.remove(jar); } customJars.remove(projectName + "-service.jar"); customJars.remove("util-bridges.jar"); customJars.remove("util-java.jar"); customJars.remove("util-taglib.jar"); Collections.sort(customJars); } else { customJars = new ArrayList<>(); } StringBundler sb = new StringBundler(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n"); sb.append("<classpath>\n"); for (String sourceDirName : _SOURCE_DIR_NAMES) { if (_fileUtil.exists(projectDirName + "/" + sourceDirName)) { sb.append("\t<classpathentry excluding=\"**/.svn/**|.svn/\" "); sb.append("kind=\"src\" path=\""); sb.append(sourceDirName); sb.append("\" />\n"); } } sb.append("\t<classpathentry kind=\"src\" path=\"/portal\" />\n"); sb.append("\t<classpathentry kind=\"con\" "); sb.append("path=\"org.eclipse.jdt.launching.JRE_CONTAINER\" />\n"); boolean addJunitJars = false; for (String testType : _TEST_TYPES) { String testFolder = "test/" + testType; if (_fileUtil.exists(projectDirName + "/" + testFolder)) { addJunitJars = true; sb.append("\t<classpathentry excluding=\"**/.svn/**|.svn/\" "); sb.append("kind=\"src\" path=\""); sb.append(testFolder); sb.append("\" />\n"); } } if (addJunitJars) { addClasspathEntry(sb, "/portal/lib/development/junit.jar"); addClasspathEntry(sb, "/portal/lib/development/mockito.jar"); addClasspathEntry( sb, "/portal/lib/development/powermock-api-mockito.jar"); addClasspathEntry( sb, "/portal/lib/development/powermock-api-support.jar"); addClasspathEntry(sb, "/portal/lib/development/powermock-core.jar"); addClasspathEntry( sb, "/portal/lib/development/powermock-module-junit4.jar"); addClasspathEntry( sb, "/portal/lib/development/powermock-module-junit4-common.jar"); addClasspathEntry(sb, "/portal/lib/development/spring-test.jar"); portalJars.add("commons-io.jar"); portalJars.add("commons-lang.jar"); } addClasspathEntry(sb, "/portal/lib/development/activation.jar"); addClasspathEntry(sb, "/portal/lib/development/annotations.jar"); addClasspathEntry(sb, "/portal/lib/development/jsp-api.jar"); addClasspathEntry(sb, "/portal/lib/development/mail.jar"); addClasspathEntry(sb, "/portal/lib/development/servlet-api.jar"); Map<String, String> attributes = new HashMap<>(); if (libDirPath.contains("/ext/")) { attributes.put("optional", "true"); } for (String jar : globalJars) { addClasspathEntry(sb, "/portal/lib/global/" + jar, attributes); } portalJars = ListUtil.unique(portalJars); Collections.sort(portalJars); for (String jar : portalJars) { if (!jar.equals("util-slf4j.jar")) { addClasspathEntry(sb, "/portal/lib/portal/" + jar, attributes); } } addClasspathEntry(sb, "/portal/portal-kernel/portal-kernel.jar"); addClasspathEntry(sb, "/portal/util-bridges/util-bridges.jar"); addClasspathEntry(sb, "/portal/util-java/util-java.jar"); if (portalJars.contains("util-slf4j.jar")) { addClasspathEntry(sb, "/portal/util-slf4j/util-slf4j.jar"); } addClasspathEntry(sb, "/portal/util-taglib/util-taglib.jar"); for (String jar : extGlobalJars) { addClasspathEntry(sb, "docroot/WEB-INF/ext-lib/global/" + jar); } for (String jar : extPortalJars) { addClasspathEntry(sb, "docroot/WEB-INF/ext-lib/portal/" + jar); } for (String jar : customJars) { if (libDirPath.contains("/tmp/WEB-INF/lib")) { addClasspathEntry(sb, "tmp/WEB-INF/lib/" + jar); } else if (libDirPath.contains("/docroot/WEB-INF/lib")) { addClasspathEntry(sb, "docroot/WEB-INF/lib/" + jar); } else { addClasspathEntry(sb, "lib/" + jar); } } File ivyXmlFile = new File(projectDirName, "ivy.xml"); if (ivyXmlFile.exists()) { String content = _fileUtil.read(ivyXmlFile); if (content.contains("test->default")) { String ivyDirName = ".ivy"; for (int i = 0; i < 10; i++) { if (_fileUtil.exists(ivyDirName)) { break; } ivyDirName = "../" + ivyDirName; } addIvyCacheJars(sb, content, ivyDirName); } } sb.append("\t<classpathentry kind=\"output\" path=\"bin\" />\n"); sb.append("</classpath>"); System.out.println("Updating " + classpathFile); String content = StringUtil.replace( sb.toString(), "\"/portal", "\"/portal-" + _BRANCH); _fileUtil.write(classpathFile, content); } protected void writeEclipseFiles( File libDir, File projectDir, List<String> dependencyJars) throws Exception { String projectDirName = projectDir.getCanonicalPath(); String projectName = StringUtil.extractLast( projectDirName, File.separatorChar); boolean javaProject = false; for (String sourceDirName : _SOURCE_DIR_NAMES) { if (_fileUtil.exists(projectDirName + "/" + sourceDirName)) { javaProject = true; break; } } if (!javaProject) { System.out.println( "Eclipse Java project will not be used because a source " + "folder does not exist"); } writeProjectFile(projectDirName, projectName, javaProject); writeClasspathFile( libDir, dependencyJars, projectDirName, projectName, javaProject); for (String sourceDirName : _SOURCE_DIR_NAMES) { if (_fileUtil.exists(projectDirName + "/" + sourceDirName)) { List<String> gitIgnores = new ArrayList<>(); if (sourceDirName.endsWith("ext-impl/src")) { gitIgnores.add("/classes"); gitIgnores.add("/ext-impl.jar"); } else if (sourceDirName.endsWith("ext-kernel/src")) { gitIgnores.add("/classes"); gitIgnores.add("/ext-kernel.jar"); } else if (sourceDirName.endsWith("ext-util-bridges/src")) { gitIgnores.add("/classes"); gitIgnores.add("/ext-util-bridges.jar"); } else if (sourceDirName.endsWith("ext-util-java/src")) { gitIgnores.add("/classes"); gitIgnores.add("/ext-util-java.jar"); } else if (sourceDirName.endsWith("ext-util-taglib/src")) { gitIgnores.add("/classes"); gitIgnores.add("/ext-util-taglib.jar"); } else { continue; } String dirName = projectDirName + "/" + sourceDirName + "/../"; if (gitIgnores.isEmpty()) { _fileUtil.delete(dirName + ".gitignore"); } else { String gitIgnoresString = StringUtil.merge( gitIgnores, "\n"); _fileUtil.write(dirName + ".gitignore", gitIgnoresString); } } } if (_fileUtil.exists(projectDirName + "/test")) { _fileUtil.write( projectDirName + "/.gitignore", "/test-classes\n/test-results"); } else { _fileUtil.delete(projectDirName + "/.gitignore"); } } protected void writeProjectFile( String projectDirName, String projectName, boolean javaProject) throws Exception { StringBundler sb = new StringBundler(19); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n"); sb.append("<projectDescription>\n"); sb.append("\t<name>"); sb.append(projectName); sb.append("-"); sb.append(_BRANCH); sb.append("</name>\n"); sb.append("\t<comment></comment>\n"); sb.append("\t<projects></projects>\n"); sb.append("\t<buildSpec>\n"); if (javaProject) { sb.append("\t\t<buildCommand>\n"); sb.append("\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n"); sb.append("\t\t\t<arguments></arguments>\n"); sb.append("\t\t</buildCommand>\n"); } sb.append("\t</buildSpec>\n"); sb.append("\t<natures>\n"); if (javaProject) { sb.append("\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n"); } sb.append("\t</natures>\n"); sb.append("</projectDescription>"); File projectFile = new File(projectDirName + "/.project"); System.out.println("Updating " + projectFile); _fileUtil.write(projectFile, sb.toString()); } private static final String _BRANCH = "master"; private static final String[] _SOURCE_DIR_NAMES = new String[] { "docroot/WEB-INF/ext-impl/src", "docroot/WEB-INF/ext-kernel/src", "docroot/WEB-INF/ext-util-bridges/src", "docroot/WEB-INF/ext-util-java/src", "docroot/WEB-INF/ext-util-taglib/src", "docroot/WEB-INF/service", "docroot/WEB-INF/src", "src" }; private static final String[] _TEST_TYPES = {"integration", "unit"}; private static final FileImpl _fileUtil = FileImpl.getInstance(); private static final SAXReader _saxReader = new SAXReaderImpl(); }