001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.tools;
016    
017    import com.liferay.portal.kernel.util.ListUtil;
018    import com.liferay.portal.kernel.util.StringBundler;
019    import com.liferay.portal.kernel.util.StringPool;
020    import com.liferay.portal.kernel.util.StringUtil;
021    import com.liferay.portal.kernel.util.UniqueList;
022    import com.liferay.portal.kernel.util.Validator;
023    import com.liferay.portal.util.FileImpl;
024    import com.liferay.portal.util.PropsValues;
025    
026    import java.io.File;
027    import java.io.FileInputStream;
028    import java.io.FilenameFilter;
029    
030    import java.util.ArrayList;
031    import java.util.Collections;
032    import java.util.HashMap;
033    import java.util.List;
034    import java.util.Map;
035    import java.util.Properties;
036    import java.util.Set;
037    import java.util.TreeSet;
038    
039    import org.apache.oro.io.GlobFilenameFilter;
040    import org.apache.tools.ant.DirectoryScanner;
041    
042    /**
043     * @author Alexander Chow
044     * @author Brian Wing Shun Chan
045     */
046    public class PluginsEnvironmentBuilder {
047    
048            public static void main(String[] args) throws Exception {
049                    try {
050                            File dir = new File(System.getProperty("plugins.env.dir"));
051    
052                            new PluginsEnvironmentBuilder(dir);
053                    }
054                    catch (Exception e) {
055                            e.printStackTrace();
056                    }
057            }
058    
059            public PluginsEnvironmentBuilder(File dir) throws Exception {
060                    DirectoryScanner directoryScanner = new DirectoryScanner();
061    
062                    directoryScanner.setBasedir(dir);
063                    directoryScanner.setIncludes(
064                            new String[] {"**\\liferay-plugin-package.properties"});
065    
066                    directoryScanner.scan();
067    
068                    String dirName = dir.getCanonicalPath();
069    
070                    for (String fileName : directoryScanner.getIncludedFiles()) {
071                            setupWarProject(dirName, fileName);
072                    }
073    
074                    directoryScanner = new DirectoryScanner();
075    
076                    directoryScanner.setBasedir(dir);
077                    directoryScanner.setIncludes(new String[] {"**\\build.xml"});
078    
079                    directoryScanner.scan();
080    
081                    for (String fileName : directoryScanner.getIncludedFiles()) {
082                            String content = _fileUtil.read(dirName + "/" + fileName);
083    
084                            if (content.contains(
085                                            "<import file=\"../../build-common-osgi-plugin.xml\" />") ||
086                                    content.contains(
087                                            "<import file=\"../build-common-shared.xml\" />")) {
088    
089                                    setupJarProject(dirName, fileName);
090                            }
091                    }
092            }
093    
094            protected void addClasspathEntry(StringBundler sb, String jar) {
095                    addClasspathEntry(sb, jar, null);
096            }
097    
098            protected void addClasspathEntry(
099                    StringBundler sb, String jar, Map<String, String> attributes) {
100    
101                    sb.append("\t<classpathentry kind=\"lib\" path=\"");
102                    sb.append(jar);
103    
104                    if ((attributes == null) || attributes.isEmpty()) {
105                            sb.append("\" />\n");
106    
107                            return;
108                    }
109    
110                    sb.append("\">\n\t\t<attributes>\n");
111    
112                    for (Map.Entry<String, String> entry : attributes.entrySet()) {
113                            sb.append("\t\t\t<attribute name=\"");
114                            sb.append(entry.getKey());
115                            sb.append("\" value=\"");
116                            sb.append(entry.getValue());
117                            sb.append("\" />\n");
118                    }
119    
120                    sb.append("\t\t</attributes>\n\t</classpathentry>\n");
121            }
122    
123            protected void addOsgiClasspathEntries(StringBundler sb, String projectDir)
124                    throws Exception {
125    
126                    String content = _fileUtil.read(projectDir + "/build.xml");
127    
128                    int index = content.indexOf("plugin-lib.classpath");
129    
130                    if (index == -1) {
131                            return;
132                    }
133    
134                    int x = content.indexOf("includes=\"", index);
135    
136                    x = content.indexOf("\"", x);
137    
138                    int y = content.indexOf("\"", x + 1);
139    
140                    if ((x == -1) || (y == -1)) {
141                            return;
142                    }
143    
144                    DirectoryScanner directoryScanner = new DirectoryScanner();
145    
146                    directoryScanner.setBasedir(projectDir + "/../../lib/");
147                    directoryScanner.setIncludes(
148                            StringUtil.split(content.substring(x + 1, y)));
149    
150                    directoryScanner.scan();
151    
152                    for (String file : directoryScanner.getIncludedFiles()) {
153                            addClasspathEntry(sb, "../../lib/" + file);
154                    }
155            }
156    
157            protected List<String> getCommonJars() {
158                    List<String> jars = new ArrayList<String>();
159    
160                    jars.add("commons-logging.jar");
161                    jars.add("log4j.jar");
162                    jars.add("util-bridges.jar");
163                    jars.add("util-java.jar");
164                    jars.add("util-taglib.jar");
165    
166                    return jars;
167            }
168    
169            protected List<String> getImportSharedJars(File projectDir)
170                    throws Exception {
171    
172                    File buildXmlFile = new File(projectDir, "build.xml");
173    
174                    String content = _fileUtil.read(buildXmlFile);
175    
176                    int x = content.indexOf("import.shared");
177    
178                    if (x == -1) {
179                            return Collections.emptyList();
180                    }
181    
182                    x = content.indexOf("value=\"", x);
183                    x = content.indexOf("\"", x);
184    
185                    int y = content.indexOf("\" />", x);
186    
187                    if ((x == -1) || (y == -1)) {
188                            return Collections.emptyList();
189                    }
190    
191                    String[] importShared = StringUtil.split(content.substring(x + 1, y));
192    
193                    if (importShared.length == 0) {
194                            return Collections.emptyList();
195                    }
196    
197                    List<String> jars = new ArrayList<String>();
198    
199                    for (String currentImportShared : importShared) {
200                            jars.add(currentImportShared + ".jar");
201    
202                            File currentImportSharedLibDir = new File(
203                                    projectDir, "/../../shared/" + currentImportShared + "/lib");
204    
205                            if (!currentImportSharedLibDir.exists()) {
206                                    continue;
207                            }
208    
209                            for (File f : currentImportSharedLibDir.listFiles()) {
210                                    jars.add(f.getName());
211                            }
212                    }
213    
214                    return jars;
215            }
216    
217            protected List<String> getPortalDependencyJars(Properties properties) {
218                    String[] dependencyJars = StringUtil.split(
219                            properties.getProperty(
220                                    "portal-dependency-jars",
221                                    properties.getProperty("portal.dependency.jars")));
222    
223                    return ListUtil.toList(dependencyJars);
224            }
225    
226            protected List<String> getRequiredDeploymentContextsJars(
227                            File libDir, Properties properties)
228                    throws Exception {
229    
230                    List<String> jars = new ArrayList<String>();
231    
232                    String[] requiredDeploymentContexts = StringUtil.split(
233                            properties.getProperty("required-deployment-contexts"));
234    
235                    for (String requiredDeploymentContext : requiredDeploymentContexts) {
236                            if (_fileUtil.exists(
237                                            libDir.getCanonicalPath() + "/" +
238                                                    requiredDeploymentContext + "-service.jar")) {
239    
240                                    jars.add(requiredDeploymentContext + "-service.jar");
241                            }
242                    }
243    
244                    return jars;
245            }
246    
247            protected void setupJarProject(String dirName, String fileName)
248                    throws Exception {
249    
250                    File buildFile = new File(dirName + "/" + fileName);
251    
252                    File projectDir = new File(buildFile.getParent());
253    
254                    File libDir = new File(projectDir, "lib");
255    
256                    List<String> dependencyJars = Collections.emptyList();
257    
258                    writeEclipseFiles(libDir, projectDir, dependencyJars);
259    
260                    List<String> importSharedJars = getImportSharedJars(projectDir);
261    
262                    File gitignoreFile = new File(
263                            projectDir.getCanonicalPath() + "/.gitignore");
264    
265                    String[] gitIgnores = importSharedJars.toArray(
266                            new String[importSharedJars.size()]);
267    
268                    for (int i = 0; i < gitIgnores.length; i++) {
269                            String gitIgnore = gitIgnores[i];
270    
271                            gitIgnore = "/lib/" + gitIgnore;
272                            gitIgnore = gitIgnore.replace(".jar", "-*.jar");
273    
274                            gitIgnores[i] = gitIgnore;
275                    }
276    
277                    if (gitIgnores.length > 0) {
278                            System.out.println("Updating " + gitignoreFile);
279    
280                            _fileUtil.write(gitignoreFile, StringUtil.merge(gitIgnores, "\n"));
281                    }
282            }
283    
284            protected void setupWarProject(String dirName, String fileName)
285                    throws Exception {
286    
287                    File propertiesFile = new File(dirName + "/" + fileName);
288    
289                    Properties properties = new Properties();
290    
291                    properties.load(new FileInputStream(propertiesFile));
292    
293                    Set<String> jars = new TreeSet<String>();
294    
295                    jars.addAll(getCommonJars());
296    
297                    List<String> dependencyJars = getPortalDependencyJars(properties);
298    
299                    jars.addAll(dependencyJars);
300    
301                    File projectDir = new File(propertiesFile.getParent() + "/../..");
302    
303                    jars.addAll(getImportSharedJars(projectDir));
304    
305                    File libDir = new File(propertiesFile.getParent() + "/lib");
306    
307                    jars.addAll(getRequiredDeploymentContextsJars(libDir, properties));
308    
309                    writeEclipseFiles(libDir, projectDir, dependencyJars);
310    
311                    String libDirPath = StringUtil.replace(
312                            libDir.getPath(), StringPool.BACK_SLASH, StringPool.SLASH);
313    
314                    List<String> ignores = ListUtil.fromFile(
315                            libDir.getCanonicalPath() + "/../.gitignore");
316    
317                    if (!libDirPath.contains("/ext/") && !ignores.contains("/lib")) {
318                            File gitignoreFile = new File(
319                                    libDir.getCanonicalPath() + "/.gitignore");
320    
321                            System.out.println("Updating " + gitignoreFile);
322    
323                            String[] gitIgnores = jars.toArray(new String[jars.size()]);
324    
325                            for (int i = 0; i < gitIgnores.length; i++) {
326                                    String gitIgnore = gitIgnores[i];
327    
328                                    if (Validator.isNotNull(gitIgnore) &&
329                                            !gitIgnore.startsWith("/")) {
330    
331                                            gitIgnores[i] = "/" + gitIgnore;
332                                    }
333                            }
334    
335                            _fileUtil.write(gitignoreFile, StringUtil.merge(gitIgnores, "\n"));
336                    }
337            }
338    
339            protected void writeClasspathFile(
340                            File libDir, List<String> dependencyJars, String projectDirName,
341                            String projectName, boolean javaProject)
342                    throws Exception {
343    
344                    File classpathFile = new File(projectDirName + "/.classpath");
345    
346                    if (!javaProject) {
347                            classpathFile.delete();
348    
349                            return;
350                    }
351    
352                    List<String> globalJars = new UniqueList<String>();
353                    List<String> portalJars = new UniqueList<String>();
354    
355                    List<String> extGlobalJars = new UniqueList<String>();
356                    List<String> extPortalJars = new UniqueList<String>();
357    
358                    String libDirPath = StringUtil.replace(
359                            libDir.getPath(), StringPool.BACK_SLASH, StringPool.SLASH);
360    
361                    if (libDirPath.contains("/ext/")) {
362                            FilenameFilter filenameFilter = new GlobFilenameFilter("*.jar");
363    
364                            for (String dirName : new String[] {"global", "portal"}) {
365                                    File file = new File(libDirPath + "/../ext-lib/" + dirName);
366    
367                                    List<String> jars = ListUtil.toList(file.list(filenameFilter));
368    
369                                    if (dirName.equals("global")) {
370                                            extGlobalJars.addAll(ListUtil.sort(jars));
371    
372                                            File dir = new File(PropsValues.LIFERAY_LIB_GLOBAL_DIR);
373    
374                                            String[] fileNames = dir.list(filenameFilter);
375    
376                                            globalJars.addAll(
377                                                    ListUtil.sort(ListUtil.toList(fileNames)));
378                                            globalJars.removeAll(extGlobalJars);
379                                    }
380                                    else if (dirName.equals("portal")) {
381                                            extPortalJars.addAll(ListUtil.sort(jars));
382    
383                                            File dir = new File(PropsValues.LIFERAY_LIB_PORTAL_DIR);
384    
385                                            String[] fileNames = dir.list(filenameFilter);
386    
387                                            portalJars.addAll(
388                                                    ListUtil.sort(ListUtil.toList(fileNames)));
389                                            portalJars.removeAll(extPortalJars);
390                                    }
391                            }
392                    }
393                    else {
394                            globalJars.add("portlet.jar");
395    
396                            portalJars.addAll(dependencyJars);
397                            portalJars.add("commons-logging.jar");
398                            portalJars.add("log4j.jar");
399    
400                            Collections.sort(portalJars);
401                    }
402    
403                    String[] customJarsArray = libDir.list(new GlobFilenameFilter("*.jar"));
404    
405                    List<String> customJars = null;
406    
407                    if (customJarsArray != null) {
408                            customJars = ListUtil.toList(customJarsArray);
409    
410                            for (String jar : portalJars) {
411                                    customJars.remove(jar);
412                            }
413    
414                            customJars.remove(projectName + "-service.jar");
415                            customJars.remove("util-bridges.jar");
416                            customJars.remove("util-java.jar");
417                            customJars.remove("util-taglib.jar");
418    
419                            Collections.sort(customJars);
420                    }
421                    else {
422                            customJars = new ArrayList<String>();
423                    }
424    
425                    StringBundler sb = new StringBundler();
426    
427                    sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
428                    sb.append("<classpath>\n");
429    
430                    for (String sourceDirName : _SOURCE_DIR_NAMES) {
431                            if (_fileUtil.exists(projectDirName + "/" + sourceDirName)) {
432                                    sb.append("\t<classpathentry excluding=\"**/.svn/**|.svn/\" ");
433                                    sb.append("kind=\"src\" path=\"" + sourceDirName + "\" />\n");
434                            }
435                    }
436    
437                    sb.append("\t<classpathentry kind=\"src\" path=\"/portal\" />\n");
438                    sb.append("\t<classpathentry kind=\"con\" ");
439                    sb.append("path=\"org.eclipse.jdt.launching.JRE_CONTAINER\" />\n");
440    
441                    boolean addJunitJars = false;
442    
443                    for (String testType : _TEST_TYPES) {
444                            String testFolder = "test/" + testType;
445    
446                            if (_fileUtil.exists(projectDirName + "/" + testFolder)) {
447                                    addJunitJars = true;
448    
449                                    sb.append("\t<classpathentry excluding=\"**/.svn/**|.svn/\" ");
450                                    sb.append("kind=\"src\" path=\""+ testFolder + "\" />\n");
451                            }
452                    }
453    
454                    if (addJunitJars) {
455                            addClasspathEntry(sb, "/portal/lib/development/junit.jar");
456                            addClasspathEntry(sb, "/portal/lib/development/mockito.jar");
457                            addClasspathEntry(
458                                    sb, "/portal/lib/development/powermock-mockito.jar");
459                            addClasspathEntry(sb, "/portal/lib/development/spring-test.jar");
460                            addClasspathEntry(sb, "/portal/lib/portal/commons-io.jar");
461                    }
462    
463                    addClasspathEntry(sb, "/portal/lib/development/activation.jar");
464                    addClasspathEntry(sb, "/portal/lib/development/annotations.jar");
465                    addClasspathEntry(sb, "/portal/lib/development/jsp-api.jar");
466                    addClasspathEntry(sb, "/portal/lib/development/mail.jar");
467                    addClasspathEntry(sb, "/portal/lib/development/servlet-api.jar");
468    
469                    Map<String, String> attributes = new HashMap<String, String>();
470    
471                    if (libDirPath.contains("/ext/")) {
472                            attributes.put("optional", "true");
473                    }
474    
475                    for (String jar : globalJars) {
476                            addClasspathEntry(sb, "/portal/lib/global/" + jar, attributes);
477                    }
478    
479                    for (String jar : portalJars) {
480                            addClasspathEntry(sb, "/portal/lib/portal/" + jar, attributes);
481                    }
482    
483                    addClasspathEntry(sb, "/portal/portal-service/portal-service.jar");
484                    addClasspathEntry(sb, "/portal/util-bridges/util-bridges.jar");
485                    addClasspathEntry(sb, "/portal/util-java/util-java.jar");
486                    addClasspathEntry(sb, "/portal/util-taglib/util-taglib.jar");
487    
488                    for (String jar : extGlobalJars) {
489                            addClasspathEntry(sb, "docroot/WEB-INF/ext-lib/global/" + jar);
490                    }
491    
492                    for (String jar : extPortalJars) {
493                            addClasspathEntry(sb, "docroot/WEB-INF/ext-lib/portal/" + jar);
494                    }
495    
496                    for (String jar : customJars) {
497                            if (libDirPath.contains("/tmp/WEB-INF/lib")) {
498                                    addClasspathEntry(sb, "tmp/WEB-INF/lib/" + jar);
499                            }
500                            else if (libDirPath.contains("/docroot/WEB-INF/lib")) {
501                                    addClasspathEntry(sb, "docroot/WEB-INF/lib/" + jar);
502                            }
503                            else {
504                                    addClasspathEntry(sb, "lib/" + jar);
505                            }
506                    }
507    
508                    addOsgiClasspathEntries(sb, projectDirName);
509    
510                    sb.append("\t<classpathentry kind=\"output\" path=\"bin\" />\n");
511                    sb.append("</classpath>");
512    
513                    System.out.println("Updating " + classpathFile);
514    
515                    String content = StringUtil.replace(
516                            sb.toString(), "\"/portal", "\"/portal-" + _BRANCH);
517    
518                    _fileUtil.write(classpathFile, content);
519            }
520    
521            protected void writeEclipseFiles(
522                            File libDir, File projectDir, List<String> dependencyJars)
523                    throws Exception {
524    
525                    String projectDirName = projectDir.getCanonicalPath();
526    
527                    String projectName = StringUtil.extractLast(
528                            projectDirName, File.separatorChar);
529    
530                    boolean javaProject = false;
531    
532                    for (String sourceDirName : _SOURCE_DIR_NAMES) {
533                            if (_fileUtil.exists(projectDirName + "/" + sourceDirName)) {
534                                    javaProject = true;
535    
536                                    break;
537                            }
538                    }
539    
540                    if (!javaProject) {
541                            System.out.println(
542                                    "Eclipse Java project will not be used because a source " +
543                                            "folder does not exist");
544                    }
545    
546                    writeProjectFile(projectDirName, projectName, javaProject);
547    
548                    writeClasspathFile(
549                            libDir, dependencyJars, projectDirName, projectName, javaProject);
550    
551                    for (String sourceDirName : _SOURCE_DIR_NAMES) {
552                            if (_fileUtil.exists(projectDirName + "/" + sourceDirName)) {
553                                    List<String> gitIgnores = new ArrayList<String>();
554    
555                                    if (sourceDirName.endsWith("ext-impl/src")) {
556                                            gitIgnores.add("/classes");
557                                            gitIgnores.add("/ext-impl.jar");
558                                    }
559                                    else if (sourceDirName.endsWith("ext-service/src")) {
560                                            gitIgnores.add("/classes");
561                                            gitIgnores.add("/ext-service.jar");
562                                    }
563                                    else if (sourceDirName.endsWith("ext-util-bridges/src")) {
564                                            gitIgnores.add("/classes");
565                                            gitIgnores.add("/ext-util-bridges.jar");
566                                    }
567                                    else if (sourceDirName.endsWith("ext-util-java/src")) {
568                                            gitIgnores.add("/classes");
569                                            gitIgnores.add("/ext-util-java.jar");
570                                    }
571                                    else if (sourceDirName.endsWith("ext-util-taglib/src")) {
572                                            gitIgnores.add("/classes");
573                                            gitIgnores.add("/ext-util-taglib.jar");
574                                    }
575                                    else {
576                                            continue;
577                                    }
578    
579                                    String dirName = projectDirName + "/" + sourceDirName + "/../";
580    
581                                    if (gitIgnores.isEmpty()) {
582                                            _fileUtil.delete(dirName + ".gitignore");
583                                    }
584                                    else {
585                                            String gitIgnoresString = StringUtil.merge(
586                                                    gitIgnores, "\n");
587    
588                                            _fileUtil.write(dirName + ".gitignore", gitIgnoresString);
589                                    }
590                            }
591                    }
592    
593                    if (_fileUtil.exists(projectDirName + "/test")) {
594                            _fileUtil.write(
595                                    projectDirName + "/.gitignore", "/test-classes\n/test-results");
596                    }
597                    else {
598                            _fileUtil.delete(projectDirName + "/.gitignore");
599                    }
600            }
601    
602            protected void writeProjectFile(
603                            String projectDirName, String projectName, boolean javaProject)
604                    throws Exception {
605    
606                    StringBundler sb = new StringBundler(17);
607    
608                    sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
609                    sb.append("<projectDescription>\n");
610                    sb.append("\t<name>");
611                    sb.append(projectName);
612                    sb.append("-");
613                    sb.append(_BRANCH);
614                    sb.append("</name>\n");
615                    sb.append("\t<comment></comment>\n");
616                    sb.append("\t<projects></projects>\n");
617                    sb.append("\t<buildSpec>\n");
618    
619                    if (javaProject) {
620                            sb.append("\t\t<buildCommand>\n");
621                            sb.append("\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n");
622                            sb.append("\t\t\t<arguments></arguments>\n");
623                            sb.append("\t\t</buildCommand>\n");
624                    }
625    
626                    sb.append("\t</buildSpec>\n");
627                    sb.append("\t<natures>\n");
628    
629                    if (javaProject) {
630                            sb.append("\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n");
631                    }
632    
633                    sb.append("\t</natures>\n");
634                    sb.append("</projectDescription>");
635    
636                    File projectFile = new File(projectDirName + "/.project");
637    
638                    System.out.println("Updating " + projectFile);
639    
640                    _fileUtil.write(projectFile, sb.toString());
641            }
642    
643            private static final String _BRANCH = "master";
644    
645            private static final String[] _SOURCE_DIR_NAMES = new String[] {
646                    "docroot/WEB-INF/ext-impl/src", "docroot/WEB-INF/ext-service/src",
647                    "docroot/WEB-INF/ext-util-bridges/src",
648                    "docroot/WEB-INF/ext-util-java/src",
649                    "docroot/WEB-INF/ext-util-taglib/src", "docroot/WEB-INF/service",
650                    "docroot/WEB-INF/src", "src"
651            };
652    
653            private static final String[] _TEST_TYPES = {"integration", "unit"};
654    
655            private static FileImpl _fileUtil = FileImpl.getInstance();
656    
657    }