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