001
014
015 package com.liferay.portal.tools;
016
017 import com.liferay.portal.kernel.util.FileUtil;
018 import com.liferay.portal.kernel.util.GetterUtil;
019 import com.liferay.portal.kernel.util.NaturalOrderStringComparator;
020 import com.liferay.portal.kernel.util.OSDetector;
021 import com.liferay.portal.kernel.util.PropertiesUtil;
022 import com.liferay.portal.kernel.util.StringBundler;
023 import com.liferay.portal.kernel.util.StringPool;
024 import com.liferay.portal.kernel.util.StringUtil;
025 import com.liferay.portal.kernel.util.Validator;
026 import com.liferay.portal.util.FileImpl;
027
028 import java.io.File;
029
030 import java.util.ArrayList;
031 import java.util.Arrays;
032 import java.util.List;
033 import java.util.Properties;
034 import java.util.Set;
035 import java.util.TreeSet;
036
037 import org.apache.tools.ant.DirectoryScanner;
038
039
042 public class PluginsSummaryBuilder {
043
044 public static void main(String[] args) {
045 ToolDependencies.wireBasic();
046
047 File pluginsDir = new File(System.getProperty("plugins.dir"));
048
049 new PluginsSummaryBuilder(pluginsDir);
050 }
051
052 public PluginsSummaryBuilder(File pluginsDir) {
053 try {
054 _pluginsDir = pluginsDir;
055
056 _latestHASH = _getLatestHASH(pluginsDir);
057
058 _createPluginsSummary();
059 }
060 catch (Exception e) {
061 e.printStackTrace();
062 }
063 }
064
065 private void _createPluginsSummary() throws Exception {
066 DirectoryScanner directoryScanner = new DirectoryScanner();
067
068 directoryScanner.setBasedir(_pluginsDir);
069 directoryScanner.setExcludes(
070 new String[] {"**\\tmp\\**", "**\\tools\\**"});
071 directoryScanner.setIncludes(
072 new String[] {
073 "**\\liferay-plugin-package.properties"
074 });
075
076 directoryScanner.scan();
077
078 String[] fileNames = directoryScanner.getIncludedFiles();
079
080 Arrays.sort(fileNames);
081
082 _createPluginsSummary(fileNames);
083 }
084
085 private void _createPluginsSummary(String[] fileNames) throws Exception {
086 StringBundler sb = new StringBundler();
087
088 sb.append("<plugins-summary>\n");
089
090 for (String fileName : fileNames) {
091 fileName = StringUtil.replace(
092 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
093
094 _createPluginSummary(sb, fileName);
095 }
096
097 for (String author : _distinctAuthors) {
098 sb.append("\t<author>");
099 sb.append(author);
100 sb.append("</author>\n");
101 }
102
103 for (String license : _distinctLicenses) {
104 sb.append("\t<license>");
105 sb.append(license);
106 sb.append("</license>\n");
107 }
108
109 sb.append("</plugins-summary>");
110
111 FileUtil.write(_pluginsDir + "/summary.xml", sb.toString());
112 }
113
114 private void _createPluginSummary(StringBundler sb, String fileName)
115 throws Exception {
116
117 String content = FileUtil.read(fileName);
118
119 int x = fileName.indexOf(StringPool.SLASH);
120
121 String type = fileName.substring(0, x);
122
123 if (type.endsWith("s")) {
124 type = type.substring(0, type.length() - 1);
125 }
126
127 x = fileName.indexOf(StringPool.SLASH, x) + 1;
128
129 int y = fileName.indexOf(StringPool.SLASH, x);
130
131 String artifactId = fileName.substring(x, y);
132
133 Properties properties = PropertiesUtil.load(content);
134
135 String name = _readProperty(properties, "name");
136 String tags = _readProperty(properties, "tags");
137 String shortDescription = _readProperty(
138 properties, "short-description");
139 String longDescription = _readProperty(properties, "long-description");
140 String changeLog = _readProperty(properties, "change-log");
141 String pageURL = _readProperty(properties, "page-url");
142 String author = _readProperty(properties, "author");
143 String licenses = _readProperty(properties, "licenses");
144 String liferayVersions = _readProperty(properties, "liferay-versions");
145
146 _distinctAuthors.add(author);
147 _distinctLicenses.add(licenses);
148
149 sb.append("\t<plugin>\n");
150
151 _writeElement(sb, "artifact-id", artifactId, 2);
152 _writeElement(sb, "name", name, 2);
153 _writeElement(sb, "type", type, 2);
154 _writeElement(sb, "tags", tags, 2);
155 _writeElement(sb, "short-description", shortDescription, 2);
156 _writeElement(sb, "long-description", longDescription, 2);
157 _writeElement(sb, "change-log", changeLog, 2);
158 _writeElement(sb, "page-url", pageURL, 2);
159 _writeElement(sb, "author", author, 2);
160 _writeElement(sb, "licenses", licenses, 2);
161 _writeElement(sb, "liferay-versions", liferayVersions, 2);
162
163 sb.append("\t\t<releng>\n");
164 sb.append(_readReleng(fileName, properties));
165 sb.append("\t\t</releng>\n");
166 sb.append("\t</plugin>\n");
167 }
168
169 private Set<String> _extractTicketIds(File pluginDir, String range)
170 throws Exception {
171
172 Set<String> ticketIds = new TreeSet<String>(
173 new NaturalOrderStringComparator()
174 );
175
176 Runtime runtime = Runtime.getRuntime();
177
178 String command = "git log " + range + " .";
179
180 if (OSDetector.isWindows()) {
181 command = "cmd /c " + command;
182 }
183
184 Process process = runtime.exec(command, null, pluginDir);
185
186 String content = StringUtil.read(process.getInputStream());
187
188 content = StringUtil.replace(content, "\n", " ");
189
190 for (String ticketIdPrefix : _TICKET_ID_PREFIXES) {
191 int x = 0;
192
193 while (true) {
194 x = content.indexOf(ticketIdPrefix + "-", x);
195
196 if (x == -1) {
197 break;
198 }
199
200 int y = x + ticketIdPrefix.length() + 1;
201
202 while (true) {
203 if ((y + 1) > content.length()) {
204 break;
205 }
206
207 if (Character.isDigit(content.charAt(y))) {
208 y++;
209 }
210 else {
211 break;
212 }
213 }
214
215 String ticketId = content.substring(x, y);
216
217 ticketIds.add(ticketId);
218
219 x = y;
220 }
221 }
222
223 File buildXmlFile = new File(pluginDir, "build.xml");
224 System.out.println("## read a " + buildXmlFile);
225
226 String buildXmlContent = _fileUtil.read(buildXmlFile);
227
228 int x = buildXmlContent.indexOf("import.shared");
229
230 if (x == -1) {
231 return ticketIds;
232 }
233
234 x = buildXmlContent.indexOf("value=\"", x);
235 x = buildXmlContent.indexOf("\"", x);
236
237 int y = buildXmlContent.indexOf("\" />", x);
238
239 if ((x == -1) || (y == -1)) {
240 return ticketIds;
241 }
242
243 String[] importShared = StringUtil.split(
244 buildXmlContent.substring(x + 1, y));
245
246 if (importShared.length == 0) {
247 return ticketIds;
248 }
249
250 for (String currentImportShared : importShared) {
251 File currentImportSharedDir = new File(
252 pluginDir, "../../shared/" + currentImportShared);
253
254 if (!currentImportSharedDir.exists()) {
255 continue;
256 }
257
258 ticketIds.addAll(_extractTicketIds(currentImportSharedDir, range));
259 }
260
261 return ticketIds;
262 }
263
264 private String _getChangeLogEntry(
265 int changeLogVersion, String range, String ticketIdsString) {
266
267 StringBundler sb = new StringBundler(8);
268
269 if (changeLogVersion > 1) {
270 sb.append("\n\n");
271 }
272
273 sb.append("#\n");
274 sb.append("# Module Incremental Version ");
275 sb.append(changeLogVersion);
276 sb.append("\n#\n");
277 sb.append(range);
278 sb.append("=");
279 sb.append(ticketIdsString);
280
281 return sb.toString();
282 }
283
284 private String _getLatestHASH(File pluginDir) throws Exception {
285 Runtime runtime = Runtime.getRuntime();
286
287 String command = "git rev-parse HEAD";
288
289 if (OSDetector.isWindows()) {
290 command = "cmd /c " + command;
291 }
292
293 Process process = runtime.exec(command, null, pluginDir);
294
295 return StringUtil.read(process.getInputStream());
296 }
297
298 private String _readProperty(Properties properties, String key) {
299 return GetterUtil.getString(properties.getProperty(key));
300 }
301
302 private String _readReleng(
303 String fileName, Properties pluginPackageProperties)
304 throws Exception {
305
306 int x = fileName.indexOf("WEB-INF");
307
308 String relativeWebInfDirName = fileName.substring(0, x + 8);
309
310 String fullWebInfDirName =
311 _pluginsDir + StringPool.SLASH + relativeWebInfDirName;
312
313 String relengPropertiesFileName =
314 fullWebInfDirName + "liferay-releng.properties";
315
316 Properties relengProperties = null;
317
318 if (FileUtil.exists(relengPropertiesFileName)) {
319 String relengPropertiesContent = FileUtil.read(
320 relengPropertiesFileName);
321
322 relengProperties = PropertiesUtil.load(relengPropertiesContent);
323 }
324 else {
325 relengProperties = new Properties();
326 }
327
328 String relengPropertiesContent = _updateRelengPropertiesFile(
329 relengPropertiesFileName, relengProperties);
330
331 relengProperties = PropertiesUtil.load(relengPropertiesContent);
332
333 StringBundler sb = new StringBundler();
334
335 _writeElement(sb, "bundle", relengProperties, 3);
336 _writeElement(sb, "category", relengProperties, 3);
337 _writeElement(sb, "demo-url", relengProperties, 3);
338 _writeElement(sb, "dependent-apps", relengProperties, 3);
339
340 if (FileUtil.exists(fullWebInfDirName + "releng/icons/90x90.png")) {
341 _writeElement(
342 sb, "icon", relativeWebInfDirName + "releng/icons/90x90.png",
343 3);
344 }
345
346 _writeElement(sb, "labs", relengProperties, 3);
347 _writeElement(sb, "marketplace", relengProperties, 3);
348 _writeElement(sb, "public", relengProperties, 3);
349
350 String fullScreenshotsDirName =
351 fullWebInfDirName + "releng/screenshots/";
352 String relativeScreenshotsDirName =
353 relativeWebInfDirName + "releng/screenshots/";
354
355 if (FileUtil.exists(fullScreenshotsDirName)) {
356 String[] screenshotsFileNames = FileUtil.listFiles(
357 fullScreenshotsDirName);
358
359 Arrays.sort(screenshotsFileNames);
360
361 for (String screenshotsFileName : screenshotsFileNames) {
362 if (screenshotsFileName.equals("Thumbs.db") ||
363 screenshotsFileName.endsWith(".png")) {
364
365 FileUtil.delete(
366 fullScreenshotsDirName + screenshotsFileName);
367 }
368
369 if (!screenshotsFileName.endsWith(".jpg")) {
370 continue;
371 }
372
373 _writeElement(
374 sb, "screenshot",
375 relativeScreenshotsDirName + screenshotsFileName, 3);
376 }
377 }
378
379 _writeElement(sb, "support-url", relengProperties, 3);
380 _writeElement(sb, "supported", relengProperties, 3);
381
382 File relengChangeLogFile = new File(
383 fullWebInfDirName + "liferay-releng.changelog");
384
385 if (GetterUtil.getBoolean(
386 relengProperties.getProperty("marketplace"))) {
387
388 _updateRelengChangeLogFile(
389 pluginPackageProperties, relengChangeLogFile, relengProperties);
390 }
391 else {
392 relengChangeLogFile.delete();
393 }
394
395 return sb.toString();
396 }
397
398 private void _updateRelengChangeLogFile(
399 Properties pluginPackageProperties, File relengChangeLogFile,
400 Properties relengProperties)
401 throws Exception {
402
403 StringBundler sb = new StringBundler();
404
405 int changeLogVersion = 0;
406
407 int moduleIncrementalVersion = GetterUtil.getInteger(
408 pluginPackageProperties.getProperty("module-incremental-version"));
409
410 if (!relengChangeLogFile.exists()) {
411 FileUtil.write(relengChangeLogFile, "TEMP=");
412 }
413
414 String relengChangeLogContent = FileUtil.read(relengChangeLogFile);
415
416 List<String> relengChangeLogEntries = new ArrayList<String>();
417
418 String[] relengChangeLogEntriesArray = StringUtil.split(
419 relengChangeLogContent, "\n");
420
421 for (int i = 0; i < relengChangeLogEntriesArray.length; i++) {
422 String relengChangeLogEntry = relengChangeLogEntriesArray[i];
423
424 if (Validator.isNull(relengChangeLogEntry) ||
425 relengChangeLogEntry.startsWith("#")) {
426
427 continue;
428 }
429
430 relengChangeLogEntries.add(relengChangeLogEntry);
431
432 if (((i + 1) == relengChangeLogEntriesArray.length) &&
433 !relengChangeLogEntry.contains("HEAD=") &&
434 !relengChangeLogEntry.contains("TEMP=") &&
435 !relengChangeLogEntry.contains(_latestHASH) &&
436 !relengChangeLogEntries.isEmpty()) {
437
438 int x = relengChangeLogEntry.indexOf("..");
439 int y = relengChangeLogEntry.indexOf("=", x);
440
441 String range =
442 relengChangeLogEntry.substring(x + 2, y) + "^.." +
443 _latestHASH;
444
445 relengChangeLogEntries.add(range);
446
447 continue;
448 }
449 }
450
451 File webInfDir = relengChangeLogFile.getParentFile();
452
453 File docrootDir = webInfDir.getParentFile();
454
455 File pluginDir = docrootDir.getParentFile();
456
457 for (int i = 0; i < relengChangeLogEntries.size(); i++) {
458 String relengChangeLogEntry = relengChangeLogEntries.get(i);
459
460 String[] relengChangeLogEntryParts = StringUtil.split(
461 relengChangeLogEntry, "=");
462
463 String range = relengChangeLogEntryParts[0];
464
465 if (range.equals("TEMP")) {
466 changeLogVersion++;
467
468 sb.append(
469 _getChangeLogEntry(
470 changeLogVersion, range, StringPool.BLANK));
471
472 break;
473 }
474
475 Set<String> ticketIds = _extractTicketIds(pluginDir, range);
476
477 if (range.endsWith("^.." + _latestHASH) && ticketIds.isEmpty() &&
478 (relengChangeLogEntries.size() > 1)) {
479
480 continue;
481 }
482
483 if (ticketIds.isEmpty()) {
484 System.out.println(
485 pluginDir + " does not have changes for range " + range);
486 }
487
488 String[] dependentApps = StringUtil.split(
489 relengProperties.getProperty("dependent-apps"));
490
491 for (String dependentApp : dependentApps) {
492 dependentApp = dependentApp.trim();
493
494 if (dependentApp.equals("resources-impoter-web")) {
495 continue;
496 }
497
498 String dependentAppDirName = null;
499
500 if (dependentApp.endsWith("-hook")) {
501 dependentAppDirName = "hooks";
502 }
503 else if (dependentApp.endsWith("-layouttpl")) {
504 dependentAppDirName = "layouttpl";
505 }
506 else if (dependentApp.endsWith("-portlet")) {
507 dependentAppDirName = "portlets";
508 }
509 else if (dependentApp.endsWith("-theme")) {
510 dependentAppDirName = "themes";
511 }
512 else if (dependentApp.endsWith("-web")) {
513 dependentAppDirName = "webs";
514 }
515
516 File dependentAppDir = new File(
517 _pluginsDir, dependentAppDirName + "/" + dependentApp);
518
519 if (!dependentAppDir.exists()) {
520 throw new RuntimeException(
521 dependentAppDir + " does not exist");
522 }
523
524 ticketIds.addAll(_extractTicketIds(dependentAppDir, range));
525 }
526
527 String ticketIdsString = StringUtil.merge(
528 ticketIds.toArray(new String[ticketIds.size()]), " ");
529
530 changeLogVersion++;
531
532 sb.append(
533 _getChangeLogEntry(changeLogVersion, range, ticketIdsString));
534 }
535
536 File pluginPackagePropertiesFile = new File(
537 webInfDir, "liferay-plugin-package.properties");
538
539 String pluginPackagePropertiesContent = FileUtil.read(
540 pluginPackagePropertiesFile);
541
542 if (!pluginPackagePropertiesContent.contains("long-description")) {
543 int x = pluginPackagePropertiesContent.indexOf("change-log=");
544
545 pluginPackagePropertiesContent =
546 pluginPackagePropertiesContent.substring(0, x) +
547 "long-description=\n" +
548 pluginPackagePropertiesContent.substring(x);
549 }
550
551 if (moduleIncrementalVersion != changeLogVersion) {
552 pluginPackagePropertiesContent = StringUtil.replace(
553 pluginPackagePropertiesContent,
554 "module-incremental-version=" + moduleIncrementalVersion,
555 "module-incremental-version=" + changeLogVersion);
556 }
557
558 FileUtil.write(
559 pluginPackagePropertiesFile, pluginPackagePropertiesContent);
560
561 FileUtil.write(relengChangeLogFile, sb.toString());
562
563 File relengChangeLogMD5File = new File(
564 webInfDir, "liferay-releng.changelog.md5");
565
566 String md5Checksum = FileUtil.getMD5Checksum(relengChangeLogFile);
567
568 FileUtil.write(relengChangeLogMD5File, md5Checksum);
569 }
570
571 private String _updateRelengPropertiesFile(
572 String relengPropertiesFileName, Properties relengProperties)
573 throws Exception {
574
575 StringBundler sb = new StringBundler();
576
577 _writeProperty(sb, relengProperties, "bundle", "false");
578 _writeProperty(sb, relengProperties, "category", "");
579 _writeProperty(sb, relengProperties, "demo-url", "");
580 _writeProperty(sb, relengProperties, "dependent-apps", "");
581 _writeProperty(sb, relengProperties, "labs", "true");
582 _writeProperty(sb, relengProperties, "marketplace", "false");
583 _writeProperty(sb, relengProperties, "public", "true");
584 _writeProperty(sb, relengProperties, "support-url", "");
585 _writeProperty(sb, relengProperties, "supported", "false");
586
587 String relengPropertiesContent = sb.toString();
588
589 FileUtil.write(relengPropertiesFileName, relengPropertiesContent);
590
591 return relengPropertiesContent;
592 }
593
594 private void _writeElement(
595 StringBundler sb, String name, Properties properties, int tabsCount) {
596
597 _writeElement(sb, name, _readProperty(properties, name), tabsCount);
598 }
599
600 private void _writeElement(
601 StringBundler sb, String name, String value, int tabsCount) {
602
603 for (int i = 0; i < tabsCount; i++) {
604 sb.append("\t");
605 }
606
607 sb.append("<");
608 sb.append(name);
609 sb.append("><![CDATA[");
610 sb.append(value);
611 sb.append("]]></");
612 sb.append(name);
613 sb.append(">\n");
614 }
615
616 private void _writeProperty(
617 StringBundler sb, Properties properties, String key,
618 String defaultValue) {
619
620 String value = GetterUtil.getString(
621 properties.getProperty(key), defaultValue);
622
623 if (sb.index() > 0) {
624 sb.append(StringPool.NEW_LINE);
625 }
626
627 sb.append(key);
628 sb.append(StringPool.EQUAL);
629 sb.append(value);
630 }
631
632 private static final String[] _TICKET_ID_PREFIXES = {"LPS", "SOS", "SYNC"};
633
634 private static FileImpl _fileUtil = FileImpl.getInstance();
635
636 private Set<String> _distinctAuthors = new TreeSet<String>();
637 private Set<String> _distinctLicenses = new TreeSet<String>();
638 private String _latestHASH;
639 private File _pluginsDir;
640
641 }