1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.codehaus.mojo.nbm;
18
19 import com.google.common.collect.Sets;
20 import java.io.*;
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.jar.JarEntry;
35 import java.util.jar.JarFile;
36 import java.util.jar.JarOutputStream;
37 import java.util.jar.Pack200;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40 import java.util.zip.CRC32;
41 import java.util.zip.GZIPInputStream;
42 import java.util.zip.ZipEntry;
43 import java.util.zip.ZipFile;
44 import org.apache.maven.artifact.Artifact;
45 import org.apache.maven.artifact.factory.ArtifactFactory;
46 import org.apache.maven.artifact.repository.ArtifactRepository;
47 import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException;
48 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
49 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
50 import org.apache.maven.artifact.resolver.ArtifactResolver;
51 import org.apache.maven.plugin.MojoExecutionException;
52 import org.apache.maven.plugin.MojoFailureException;
53 import org.apache.maven.plugin.logging.Log;
54 import org.apache.maven.plugins.annotations.Component;
55 import org.apache.maven.plugins.annotations.LifecyclePhase;
56 import org.apache.maven.plugins.annotations.Mojo;
57 import org.apache.maven.plugins.annotations.Parameter;
58 import org.apache.maven.plugins.annotations.ResolutionScope;
59 import org.apache.maven.project.MavenProject;
60 import org.apache.tools.ant.BuildException;
61 import org.apache.tools.ant.Project;
62 import org.apache.tools.ant.filters.StringInputStream;
63 import org.apache.tools.ant.taskdefs.Chmod;
64 import org.apache.tools.ant.types.FileSet;
65 import org.codehaus.mojo.nbm.utils.ExamineManifest;
66 import org.codehaus.plexus.util.FileUtils;
67 import org.codehaus.plexus.util.IOUtil;
68 import org.codehaus.plexus.util.StringUtils;
69 import org.codehaus.plexus.util.io.InputStreamFacade;
70 import org.netbeans.nbbuild.MakeListOfNBM;
71
72
73
74
75
76
77
78 @Mojo(name="cluster-app",
79 defaultPhase= LifecyclePhase.PACKAGE,
80 requiresProject=true,
81 threadSafe = true,
82 requiresDependencyResolution= ResolutionScope.RUNTIME )
83 public class CreateClusterAppMojo
84 extends AbstractNbmMojo
85 {
86
87
88
89
90 @Parameter(defaultValue="${project.build.directory}", required=true)
91 private File outputDirectory;
92
93
94
95
96 @Parameter(required=true, readonly=true, property="project")
97 private MavenProject project;
98
99
100
101
102 @Parameter(property="netbeans.branding.token", required=true)
103 protected String brandingToken;
104
105
106
107
108
109 @Parameter( property="netbeans.conf.file")
110 private File etcConfFile;
111
112
113
114
115
116 @Parameter(property="netbeans.clusters.file")
117 private File etcClustersFile;
118
119
120
121
122
123
124
125 @Parameter(property="netbeans.bin.directory")
126 private File binDirectory;
127
128
129
130
131
132
133 @Parameter(defaultValue="extra")
134 private String defaultCluster;
135
136
137
138
139
140
141 @Parameter(defaultValue = "true", property = "netbeans.verify.integrity")
142 private boolean verifyIntegrity;
143
144 private final Collection<String> defaultPlatformTokens = Arrays.asList( new String[] {
145 "org.openide.modules.os.Windows",
146 "org.openide.modules.os.Unix",
147 "org.openide.modules.os.MacOSX",
148 "org.openide.modules.os.OS2",
149 "org.openide.modules.os.PlainUnix",
150 "org.openide.modules.os.Linux",
151 "org.openide.modules.os.Solaris",
152 "org.openide.modules.ModuleFormat1",
153 "org.openide.modules.ModuleFormat2",
154 "org.openide.modules.jre.JavaFX"
155 });
156
157
158
159
160 @Component
161 private ArtifactFactory artifactFactory;
162
163 @Component
164 private ArtifactResolver artifactResolver;
165
166
167
168
169
170 @Parameter(required=true, readonly=true, property="localRepository")
171 protected ArtifactRepository localRepository;
172
173
174
175
176 public void execute()
177 throws MojoExecutionException, MojoFailureException
178 {
179
180 File nbmBuildDirFile = new File( outputDirectory, brandingToken );
181 if ( !nbmBuildDirFile.exists() )
182 {
183 nbmBuildDirFile.mkdirs();
184 }
185
186 if ( "nbm-application".equals( project.getPackaging() ) )
187 {
188 Project antProject = registerNbmAntTasks();
189
190 Set<String> wrappedBundleCNBs = new HashSet<String>(100);
191 Map<String, Set<String>> clusterDependencies = new HashMap<String, Set<String>>();
192 Map<String, Set<String>> clusterModules = new HashMap<String, Set<String>>();
193
194
195 Set<String> modulesCNBs = new HashSet<String>(200);
196 Set<String> dependencyCNBs = new HashSet<String>(200);
197 Map<String, Set<String>> dependencyCNBBacktraces = new HashMap<String, Set<String>>(50);
198 Set<String> requireTokens = new HashSet<String>(50);
199 Map<String, Set<String>> requireTokensBacktraces = new HashMap<String, Set<String>>(50);
200 Set<String> provideTokens = new HashSet<String>(50);
201 Set<String> osgiImports = new HashSet<String>(50);
202 Map<String, Set<String>> osgiImportsBacktraces = new HashMap<String, Set<String>>(50);
203 Set<String> osgiExports = new HashSet<String>(50);
204 Set<String> osgiExportsSubs = new HashSet<String>(50);
205
206 List<BundleTuple> bundles = new ArrayList<BundleTuple>();
207
208 @SuppressWarnings( "unchecked" )
209 Set<Artifact> artifacts = project.getArtifacts();
210 for ( Artifact art : artifacts )
211 {
212 ArtifactResult res = turnJarToNbmFile( art, artifactFactory, artifactResolver, project, localRepository );
213 if ( res.hasConvertedArtifact() )
214 {
215 art = res.getConvertedArtifact();
216 }
217
218 if ( art.getType().equals( "nbm-file" ) )
219 {
220 try
221 {
222 JarFile jf = new JarFile( art.getFile() );
223 try
224 {
225 String clusterName = findCluster( jf );
226 ClusterTuple cluster = processCluster( clusterName, nbmBuildDirFile, art );
227
228 getLog().debug( "Copying " + art.getId() + " to cluster " + clusterName );
229 Enumeration<JarEntry> enu = jf.entries();
230
231
232 MakeListOfNBM makeTask = (MakeListOfNBM) antProject.createTask( "genlist" );
233 antProject.setNewProperty( "module.name", art.getFile().getName() );
234 antProject.setProperty( "cluster.dir", clusterName );
235 FileSet set = makeTask.createFileSet();
236 set.setDir( cluster.location );
237 makeTask.setOutputfiledir( cluster.location );
238 String[] executables = null;
239 File classpathRoot = null;
240 String classPath = null;
241 while ( enu.hasMoreElements() )
242 {
243 JarEntry ent = enu.nextElement();
244 String name = ent.getName();
245
246 if (name.equals("Info/executables.list")) {
247 if (cluster.newer) {
248 InputStream is = jf.getInputStream( ent );
249 executables = StringUtils.split( IOUtil.toString( is, "UTF-8" ), "\n");
250 }
251 }
252 else if ( name.startsWith( "netbeans/" ) )
253 {
254 String path = clusterName + name.substring( "netbeans".length() );
255 boolean ispack200 = path.endsWith( ".jar.pack.gz" );
256 if ( ispack200 )
257 {
258 path = path.replace( ".jar.pack.gz", ".jar" );
259 }
260 File fl = new File( nbmBuildDirFile, path.replace( "/", File.separator ) );
261 String part = name.substring( "netbeans/".length() );
262 if ( ispack200 )
263 {
264 part = part.replace( ".jar.pack.gz", ".jar" );
265 }
266 if (cluster.newer)
267 {
268 if ( ent.isDirectory() )
269 {
270 fl.mkdirs();
271 }
272 else if ( path.endsWith( ".external" ) )
273 {
274 InputStream is = jf.getInputStream( ent );
275 try
276 {
277 externalDownload( new File( fl.getParentFile(),
278 fl.getName().replaceFirst( "[.]external$",
279 "" ) ), is );
280 }
281 finally
282 {
283 is.close();
284 }
285
286 set.appendIncludes( new String[] { name.substring( "netbeans/".length(), name.length() - ".external".length() ) } );
287 }
288 else
289 {
290 set.appendIncludes( new String[] { part } );
291
292 fl.getParentFile().mkdirs();
293 fl.createNewFile();
294 BufferedOutputStream outstream = null;
295 try
296 {
297 outstream = new BufferedOutputStream( new FileOutputStream( fl ) );
298 InputStream instream = jf.getInputStream( ent );
299 if ( ispack200 )
300 {
301 Pack200.Unpacker unp = Pack200.newUnpacker();
302 JarOutputStream jos = new JarOutputStream( outstream );
303 GZIPInputStream gzip = new GZIPInputStream( instream );
304 try
305 {
306 unp.unpack( gzip, jos );
307 }
308 finally
309 {
310 jos.close();
311 }
312 }
313 else
314 {
315 IOUtil.copy( instream, outstream );
316 }
317 }
318 finally
319 {
320 IOUtil.close( outstream );
321 }
322 }
323 }
324
325
326
327
328
329
330
331
332 if ( part.matches("(modules|core|lib)/[^/]+[.]jar") )
333 {
334 ExamineManifest ex = new ExamineManifest( getLog() );
335 ex.setJarFile( fl );
336 ex.setPopulateDependencies( true );
337 ex.checkFile();
338 if ( ex.isNetBeansModule() )
339 {
340 makeTask.setModule( part );
341 addToMap(clusterDependencies, clusterName, ex.getDependencyTokens());
342 addToMap(clusterModules, clusterName, Collections.singletonList( ex.getModule() ));
343 if (ex.getClasspath().length() > 0) {
344 classPath = ex.getClasspath();
345 classpathRoot = fl.getParentFile();
346 }
347 }
348 if (verifyIntegrity) {
349 dependencyCNBs.addAll(ex.getDependencyTokens());
350 modulesCNBs.add(ex.getModule());
351 for (String d : ex.getDependencyTokens()) {
352 addToMap(dependencyCNBBacktraces, d, Collections.singletonList( ex.getModule() ));
353 }
354 if (ex.isNetBeansModule()) {
355 requireTokens.addAll(ex.getNetBeansRequiresTokens());
356 for (String r : ex.getNetBeansRequiresTokens()) {
357 addToMap( requireTokensBacktraces, r, Collections.singletonList( ex.getModule()));
358 }
359 provideTokens.addAll(ex.getNetBeansProvidesTokens());
360 for (String pack : ex.getPackages()) {
361 if (pack.endsWith( ".**")) {
362
363 pack = pack.substring( 0, pack.length() - ".**".length());
364 osgiExportsSubs.add( pack );
365 } else if (pack.endsWith( ".*")) {
366 pack = pack.substring( 0, pack.length() - ".*".length());
367 osgiExports.add(pack);
368 }
369 }
370
371 }
372 }
373 }
374 }
375 }
376 if (classPath != null) {
377 String[] paths = StringUtils.split( classPath, " ");
378 for (String path : paths) {
379 path = path.trim();
380 File classpathFile = new File(classpathRoot, path);
381 if (path.equals("${java.home}/lib/ext/jfxrt.jar")) {
382 String jhm = System.getProperty("java.home");
383 classpathFile = new File(new File(new File(new File(jhm), "lib"), "ext"), "jfxrt.jar");
384 if (!classpathFile.exists()) {
385 File jdk7 = new File(new File(new File(jhm), "lib"), "jfxrt.jar");
386 if (jdk7.exists()) {
387 classpathFile = jdk7;
388 }
389 }
390 }
391 if (!classpathFile.isFile()) {
392 getLog().warn( "Could not resolve Class-Path item in " + art.getId() + ", path is:" + path + ", skipping");
393 continue;
394 }
395 ExamineManifest ex = new ExamineManifest( getLog() );
396 ex.setJarFile( classpathFile );
397
398 ex.checkFile();
399 if (ex.isOsgiBundle()) {
400 wrappedBundleCNBs.add( ex.getModule() );
401 }
402 }
403 }
404 if ( cluster.newer )
405 {
406 try
407 {
408 makeTask.execute();
409 }
410 catch ( BuildException e )
411 {
412 getLog().error( "Cannot Generate update_tracking XML file from " + art.getFile() );
413 throw new MojoExecutionException( e.getMessage(), e );
414 }
415
416 if ( executables != null )
417 {
418
419 for ( String exec : executables )
420 {
421 exec = exec.replace( "/", File.separator );
422 File execFile = new File( cluster.location, exec );
423 if ( execFile.exists() )
424 {
425 execFile.setExecutable( true, false );
426 }
427 }
428 }
429 }
430
431 }
432 finally
433 {
434 jf.close();
435 }
436 }
437 catch ( IOException ex )
438 {
439 getLog().error( art.getFile().getAbsolutePath(), ex );
440 }
441 }
442 if ( res.isOSGiBundle() )
443 {
444 ExamineManifest ex = res.getExaminedManifest();
445 bundles.add( new BundleTuple( art, ex) );
446 if (verifyIntegrity) {
447 dependencyCNBs.addAll(ex.getDependencyTokens());
448 for ( String d : ex.getDependencyTokens() )
449 {
450 addToMap( dependencyCNBBacktraces, d, Collections.singletonList( ex.getModule() ) );
451 }
452 modulesCNBs.add(ex.getModule());
453 osgiImports.addAll( ex.getOsgiImports());
454 for ( String d : ex.getOsgiImports() )
455 {
456 addToMap( osgiImportsBacktraces, d, Collections.singletonList( ex.getModule() ) );
457 }
458
459 osgiExports.addAll( ex.getOsgiExports());
460 }
461 }
462 }
463
464 if (verifyIntegrity) {
465 if (getLog().isDebugEnabled()) {
466 getLog().debug( "All found codenamebases:" + Arrays.toString( modulesCNBs.toArray()) );
467 getLog().debug( "All found OSGI exports:" + Arrays.toString( osgiExports.toArray()) );
468 getLog().debug( "All found provided tokens:" + Arrays.toString( provideTokens.toArray()) );
469 }
470 dependencyCNBs.removeAll( modulesCNBs );
471 if (modulesCNBs.contains( "org.netbeans.modules.netbinox")) {
472 dependencyCNBs.remove( "org.eclipse.osgi");
473 }
474 osgiImports.removeAll( osgiExports );
475 Iterator<String> it = osgiImports.iterator();
476 while (it.hasNext()) {
477 String s = it.next();
478 if (s.startsWith( "java.") || s.startsWith( "javax.") || s.startsWith( "sun.") || s.startsWith( "org.xml.sax") || s.startsWith( "org.w3c.dom") || s.startsWith( "org.ietf.jgss")) {
479 it.remove();
480 continue;
481 }
482 for (String sub : osgiExportsSubs) {
483 if (s.startsWith( sub )) {
484 it.remove();
485 break;
486 }
487 }
488 }
489 requireTokens.removeAll( provideTokens );
490 requireTokens.removeAll( defaultPlatformTokens );
491 if (!dependencyCNBs.isEmpty() || !osgiImports.isEmpty() ||!requireTokens.isEmpty()) {
492 if (!dependencyCNBs.isEmpty()) {
493 getLog().error( "Some included modules/bundles depend on these codenamebases but they are not included. The application will fail starting up. The missing codenamebases are:" );
494 for (String s : dependencyCNBs) {
495 Set<String> back = dependencyCNBBacktraces.get( s );
496 getLog().error(" " + s + (back != null ? " ref: " + Arrays.toString( back.toArray()) : ""));
497 }
498 }
499 if (!osgiImports.isEmpty()) {
500 getLog().error("Some OSGi imports are not satisfied by included bundles' exports. The application will fail starting up. The missing imports are:");
501 for (String s : osgiImports) {
502 Set<String> back = osgiImportsBacktraces.get( s );
503 getLog().error(" " + s + (back != null ? " ref: " + Arrays.toString( back.toArray()) : ""));
504 }
505 }
506 if (!requireTokens.isEmpty()) {
507 getLog().error("Some tokens required by included modules are not provided by included modules. The application will fail starting up. The missing tokens are:");
508 for (String s : requireTokens) {
509 Set<String> back = requireTokensBacktraces.get( s );
510 getLog().error(" " + s + (back != null ? " ref: " + Arrays.toString( back.toArray()) : ""));
511 }
512 }
513 throw new MojoFailureException("See above for consistency validation check failures. Either fix those by adding the relevant dependencies to the application or disable the check by setting the verifyIntegrity parameter to false or by running with -Dnetbeans.verify.integrity=false cmd line parameter.");
514 } else {
515 getLog().info( "Integrity verification passed.");
516 }
517 } else {
518 getLog().info( "Integrity verification skipped.");
519 }
520
521
522 Map<String, Set<String>> cluster2depClusters = computeClusterOrdering( clusterDependencies, clusterModules );
523 clusterModules.clear();
524
525
526 assignClustersToBundles( bundles, wrappedBundleCNBs, clusterDependencies, cluster2depClusters, getLog() );
527
528
529 for (BundleTuple ent : bundles) {
530 Artifact art = ent.artifact;
531 final ExamineManifest ex = ent.manifest;
532
533 String clstr = ent.cluster;
534 if (clstr == null) {
535 clstr = defaultCluster;
536 }
537
538 ClusterTuple cluster = processCluster( clstr, nbmBuildDirFile, art );
539 if ( cluster.newer )
540 {
541 getLog().info( "Copying " + art.getId() + " to cluster " + clstr );
542 File modules = new File( cluster.location, "modules" );
543 modules.mkdirs();
544 File config = new File( cluster.location, "config" );
545 File confModules = new File( config, "Modules" );
546 confModules.mkdirs();
547 File updateTracking = new File( cluster.location, "update_tracking" );
548 updateTracking.mkdirs();
549 final String cnb = ex.getModule();
550 final String cnbDashed = cnb.replace( ".", "-" );
551 final File moduleArt = new File( modules, cnbDashed + ".jar" );
552 final String specVer = ex.getSpecVersion();
553 try
554 {
555 FileUtils.copyFile( art.getFile(), moduleArt );
556 final File moduleConf = new File( confModules, cnbDashed + ".xml" );
557 FileUtils.copyStreamToFile( new InputStreamFacade() {
558 @Override
559 public InputStream getInputStream() throws IOException
560 {
561 return new StringInputStream( createBundleConfigFile( cnb, ex.isBundleAutoload() ), "UTF-8" );
562 }
563 }, moduleConf );
564 FileUtils.copyStreamToFile( new InputStreamFacade() {
565 @Override
566 public InputStream getInputStream() throws IOException
567 {
568 return new StringInputStream( createBundleUpdateTracking( cnb, moduleArt, moduleConf, specVer ), "UTF-8" );
569 }
570 }, new File( updateTracking, cnbDashed + ".xml" ) );
571 }
572 catch ( IOException exc )
573 {
574 getLog().error( exc );
575 }
576 }
577 }
578
579 getLog().info(
580 "Created NetBeans module cluster(s) at " + nbmBuildDirFile.getAbsoluteFile() );
581
582 }
583 else
584 {
585 throw new MojoExecutionException(
586 "This goal only makes sense on project with nbm-application packaging" );
587 }
588
589 File[] files = nbmBuildDirFile.listFiles();
590 for ( int i = 0; i < files.length; i++ )
591 {
592 if ( files[i].isDirectory() )
593 {
594 File stamp = new File( files[i], ".lastModified" );
595 if ( !stamp.exists() )
596 {
597 try
598 {
599 stamp.createNewFile();
600 }
601 catch ( IOException ex )
602 {
603 ex.printStackTrace();
604 }
605 }
606 stamp.setLastModified( new Date().getTime() );
607 }
608 }
609 try
610 {
611 createBinEtcDir( nbmBuildDirFile, brandingToken );
612 }
613 catch ( IOException ex )
614 {
615 throw new MojoExecutionException(
616 "Cannot process etc folder content creation.", ex );
617 }
618 }
619 private final static Pattern patt = Pattern.compile(
620 ".*targetcluster=\"([a-zA-Z0-9_\\.\\-]+)\".*", Pattern.DOTALL );
621
622 private String findCluster( JarFile jf )
623 throws MojoFailureException, IOException
624 {
625 ZipEntry entry = jf.getEntry( "Info/info.xml" );
626 InputStream ins = jf.getInputStream( entry );
627 String str = IOUtil.toString( ins, "UTF8" );
628 Matcher m = patt.matcher( str );
629 if ( !m.matches() )
630 {
631 getLog().info( "Cannot find cluster for " + jf.getName() + " Falling back to default value - '"
632 + defaultCluster + "'." );
633 return defaultCluster;
634 }
635 else
636 {
637 return m.group( 1 );
638 }
639 }
640
641
642
643
644
645
646
647
648 private void createBinEtcDir( File buildDir, String brandingToken )
649 throws IOException, MojoExecutionException
650 {
651 File etcDir = new File( buildDir + File.separator + "etc" );
652 etcDir.mkdir();
653
654
655
656 File clusterConf = new File( etcDir + File.separator + brandingToken + ".clusters" );
657 String clustersString;
658 if ( etcClustersFile != null )
659 {
660 clustersString = FileUtils.fileRead( etcClustersFile, "UTF-8" );
661 }
662 else
663 {
664 clusterConf.createNewFile();
665 StringBuffer buffer = new StringBuffer();
666 File[] clusters = buildDir.listFiles( new FileFilter()
667 {
668
669 @Override
670 public boolean accept( File pathname )
671 {
672 return new File( pathname, ".lastModified" ).exists();
673 }
674 } );
675 for ( File cluster : clusters )
676 {
677 buffer.append( cluster.getName() );
678 buffer.append( "\n" );
679 }
680 clustersString = buffer.toString();
681 }
682
683 FileUtils.fileWrite( clusterConf.getAbsolutePath(), clustersString );
684
685 File confFile = etcConfFile;
686 String str;
687 if ( confFile == null )
688 {
689 File harnessDir = new File( buildDir, "harness" );
690
691 confFile = new File(
692 harnessDir.getAbsolutePath() + File.separator + "etc" + File.separator + "app.conf" );
693 if ( confFile.exists() )
694 {
695 str = FileUtils.fileRead( confFile, "UTF-8" );
696 }
697 else
698 {
699 getLog().debug( "Using fallback app.conf shipping with the nbm-maven-plugin." );
700 InputStream instream = null;
701 try
702 {
703 instream = getClass().getClassLoader().getResourceAsStream( "harness/etc/app.conf" );
704 str = IOUtil.toString( instream, "UTF-8" );
705 }
706 finally
707 {
708 IOUtil.close( instream );
709 }
710 }
711 }
712 else
713 {
714 str = FileUtils.fileRead( confFile, "UTF-8" );
715 }
716 File confDestFile = new File(
717 etcDir.getAbsolutePath() + File.separator + brandingToken + ".conf" );
718
719 str = str.replace( "${branding.token}", brandingToken );
720 FileUtils.fileWrite( confDestFile.getAbsolutePath(), "UTF-8", str );
721
722 File destBinDir = new File( buildDir + File.separator + "bin" );
723 destBinDir.mkdir();
724
725 File binDir;
726 File destExeW = new File( destBinDir, brandingToken + "_w.exe" );
727 File destExe = new File( destBinDir, brandingToken + ".exe" );
728 File destExe64 = new File( destBinDir, brandingToken + "64.exe" );
729 File destSh = new File( destBinDir, brandingToken );
730
731 if ( binDirectory != null )
732 {
733
734 binDir = binDirectory;
735 File[] fls = binDir.listFiles();
736 if ( fls == null )
737 {
738 throw new MojoExecutionException( "Parameter 'binDirectory' has to point to an existing folder." );
739 }
740 for ( File fl : fls )
741 {
742 String name = fl.getName();
743 File dest = null;
744 if ( name.endsWith( "_w.exe" ) )
745 {
746 dest = destExeW;
747 }
748 else if ( name.endsWith( "64.exe" ) )
749 {
750 dest = destExe64;
751 }
752 else if ( name.endsWith( ".exe" ) )
753 {
754 dest = destExe;
755 }
756 else if ( !name.contains( "." ) || name.endsWith( ".sh" ) )
757 {
758 dest = destSh;
759 }
760 if ( dest != null && fl.exists() )
761 {
762 FileUtils.copyFile( fl, dest );
763 }
764 else
765 {
766
767 }
768 }
769 }
770 else
771 {
772 File harnessDir = new File( buildDir, "harness" );
773
774 binDir = new File(
775 harnessDir.getAbsolutePath() + File.separator + "launchers" );
776 if ( binDir.exists() )
777 {
778 File exe = new File( binDir, "app.exe" );
779 FileUtils.copyFile( exe, destExe );
780 File exe64 = new File( binDir, "app64.exe" );
781 if ( exe64.isFile() )
782 {
783 FileUtils.copyFile( exe64, destExe64 );
784 }
785 File exew = new File( binDir, "app_w.exe" );
786 if ( exew.exists() )
787 {
788 FileUtils.copyFile( exew, destExeW );
789 }
790 File sh = new File( binDir, "app.sh" );
791 FileUtils.copyFile( sh, destSh );
792 }
793 else
794 {
795 File nbm = getHarnessNbm();
796 ZipFile zip = new ZipFile( nbm );
797 try {
798 getLog().debug( "Using fallback executables from downloaded org-netbeans-modules-apisupport-harness nbm file." );
799 writeFromZip(zip, "netbeans/launchers/app.sh", destSh, true );
800 writeFromZip(zip, "netbeans/launchers/app.exe", destExe, true );
801 writeFromZip(zip, "netbeans/launchers/app64.exe", destExe64, false );
802 writeFromZip(zip, "netbeans/launchers/app_w.exe", destExeW, false );
803 } finally {
804 zip.close();
805 }
806 }
807 }
808
809 Project antProject = antProject();
810
811 Chmod chmod = (Chmod) antProject.createTask( "chmod" );
812 FileSet fs = new FileSet();
813 fs.setDir( destBinDir );
814 fs.setIncludes( "*" );
815 chmod.addFileset( fs );
816 chmod.setPerm( "755" );
817 chmod.execute();
818 }
819
820 private void writeFile( String path, File destSh )
821 throws IOException
822 {
823 InputStream instream = null;
824 OutputStream output = null;
825 try
826 {
827 instream = getClass().getClassLoader().getResourceAsStream( path );
828 if ( instream == null )
829 {
830 throw new FileNotFoundException( path );
831 }
832 destSh.createNewFile();
833 output = new BufferedOutputStream( new FileOutputStream( destSh ) );
834 IOUtil.copy( instream, output );
835 }
836 finally
837 {
838 IOUtil.close( instream );
839 IOUtil.close( output );
840 }
841 }
842
843 private ClusterTuple processCluster( String cluster, File nbmBuildDirFile, Artifact art )
844 {
845 File clusterFile = new File( nbmBuildDirFile, cluster );
846 boolean newer = false;
847 if ( !clusterFile.exists() )
848 {
849 clusterFile.mkdir();
850 newer = true;
851 }
852 else
853 {
854 File stamp = new File( clusterFile, ".lastModified" );
855 if ( stamp.lastModified() < art.getFile().lastModified() )
856 {
857 newer = true;
858 }
859 }
860 return new ClusterTuple( clusterFile, newer );
861 }
862
863 private void externalDownload( File f, InputStream is )
864 throws IOException
865 {
866
867 BufferedReader r = new BufferedReader( new InputStreamReader( is, "UTF-8" ) );
868 long crc = -1;
869 long size = -1;
870 boolean found = false;
871 String line;
872 while ( ( line = r.readLine() ) != null )
873 {
874 if ( line.startsWith( "CRC:" ) )
875 {
876 crc = Long.parseLong( line.substring( 4 ).trim() );
877 }
878 else if ( line.startsWith( "URL:m2:/" ) )
879 {
880 if ( ! found )
881 {
882 String[] coords = line.substring( 8 ).trim().split( ":" );
883 Artifact artifact;
884 if ( coords.length == 4 )
885 {
886 artifact = artifactFactory.createArtifact( coords[0], coords[1], coords[2], null, coords[3] );
887 }
888 else
889 {
890 artifact = artifactFactory.createArtifactWithClassifier( coords[0], coords[1], coords[2], coords[3], coords[4] );
891 }
892 try
893 {
894 artifactResolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
895 FileUtils.copyFile( artifact.getFile(), f );
896 found = true;
897 }
898 catch ( AbstractArtifactResolutionException x )
899 {
900 getLog().warn( "Cannot find " + line.substring( 8 ), x );
901 }
902 }
903 }
904 else if ( line.startsWith( "URL:" ) )
905 {
906 if ( ! found )
907 {
908 String url = line.substring( 4 ).trim();
909 try
910 {
911
912 FileUtils.copyURLToFile( new URL( url ), f );
913 found = true;
914 }
915 catch ( IOException x )
916 {
917 getLog().warn( "Cannot download " + url, x );
918 }
919 }
920 }
921 else if ( line.startsWith( "SIZE:" ) )
922 {
923 size = Long.parseLong( line.substring( 5 ).trim() );
924 }
925 else
926 {
927 getLog().warn( "Unrecognized line: " + line );
928 }
929 }
930 if ( ! found )
931 {
932 throw new IOException( "Could not download " + f );
933 }
934 if ( crc != -1 && crc != crcForFile( f ).getValue() )
935 {
936 throw new IOException( "CRC-32 of " + f + " does not match declared " + crc );
937 }
938 if ( size != -1 && size != f.length() )
939 {
940 throw new IOException( "Size of " + f + " does not match declared " + size );
941 }
942 }
943
944 private File getHarnessNbm() throws MojoExecutionException
945 {
946 @SuppressWarnings( "unchecked" )
947 Set<Artifact> artifacts = project.getArtifacts();
948 String version = null;
949 for (Artifact a : artifacts) {
950 if ("org.netbeans.modules".equals(a.getGroupId()) && "org-netbeans-bootstrap".equals(a.getArtifactId())) {
951 version = a.getBaseVersion();
952 break;
953 }
954 }
955 if (version == null) {
956 throw new MojoExecutionException( "We could not find org-netbeans-bootstrap among the modules in the application. Launchers could not be found.");
957 }
958 Artifact nbmArt = artifactFactory.createArtifact(
959 "org.netbeans.modules",
960 "org-netbeans-modules-apisupport-harness",
961 version,
962 "compile",
963 "nbm-file");
964 try
965 {
966 artifactResolver.resolve( nbmArt, project.getRemoteArtifactRepositories(), localRepository );
967 }
968
969 catch ( ArtifactResolutionException ex )
970 {
971 throw new MojoExecutionException( "Failed to retrieve the nbm file from repository", ex );
972 }
973 catch ( ArtifactNotFoundException ex )
974 {
975 throw new MojoExecutionException( "Failed to retrieve the nbm file from repository", ex );
976 }
977 return nbmArt.getFile();
978 }
979
980 private void writeFromZip( final ZipFile zip, String zipPath, File destFile, boolean mandatory ) throws MojoExecutionException, IOException
981 {
982 final ZipEntry path = zip.getEntry( zipPath );
983 if (path == null) {
984 if (mandatory) {
985 throw new MojoExecutionException( zipPath + " not found in " + zip.getName());
986 }
987 getLog().debug(zipPath + " is not present in " + zip.getName());
988 return;
989 }
990 FileUtils.copyStreamToFile( new InputStreamFacade() {
991
992 @Override
993 public InputStream getInputStream() throws IOException
994 {
995 return zip.getInputStream( path );
996 }
997 }, destFile);
998 }
999
1000 private static void addToMap( Map<String, Set<String>> map, String clusterName, List<String> newValues )
1001 {
1002 Set<String> lst = map.get( clusterName );
1003 if ( lst == null )
1004 {
1005 lst = new HashSet<String>();
1006 map.put( clusterName, lst );
1007 }
1008 if ( newValues != null )
1009 {
1010 lst.addAll( newValues );
1011 }
1012 }
1013
1014 private static List<String> findByDependencies( Map<String, Set<String>> clusterDependencies, String spec)
1015 {
1016 List<String> toRet = new ArrayList<String>();
1017 for ( Map.Entry<String, Set<String>> entry : clusterDependencies.entrySet() )
1018 {
1019 if ( entry.getValue().contains( spec ) )
1020 {
1021 toRet.add(entry.getKey());
1022 }
1023 }
1024 return toRet;
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 static void assignClustersToBundles( List<BundleTuple> bundles, Set<String> wrappedBundleCNBs, Map<String, Set<String>> clusterDependencies, Map<String, Set<String>> cluster2depClusters, Log log)
1036 {
1037 List<BundleTuple> toProcess = new ArrayList<BundleTuple>();
1038 List<BundleTuple> known = new ArrayList<BundleTuple>();
1039 for ( Iterator<BundleTuple> it = bundles.iterator(); it.hasNext(); )
1040 {
1041 BundleTuple ent = it.next();
1042 Artifact art = ent.artifact;
1043 ExamineManifest ex = ent.manifest;
1044 String spec = ex.getModule();
1045 if ( wrappedBundleCNBs.contains( spec ) )
1046 {
1047
1048 log.debug( "Not including bundle " + art.getDependencyConflictId()
1049 + ". It is already included in a NetBeans module" );
1050 it.remove();
1051 continue;
1052 }
1053 List<String> depclusters = findByDependencies(clusterDependencies, spec);
1054 if (depclusters.size() == 1) {
1055 ent.cluster = depclusters.get( 0 );
1056 known.add( ent );
1057 } else if (depclusters.isEmpty()) {
1058 toProcess.add(ent);
1059 } else {
1060
1061 for ( Iterator<String> it2 = depclusters.iterator(); it2.hasNext(); )
1062 {
1063 String s = it2.next();
1064 Set<String> depsCs = cluster2depClusters.get( s );
1065 boolean removeS = false;
1066 for (String sDep : depclusters) {
1067 if (s.equals( sDep) ) {
1068 continue;
1069 }
1070 if (depsCs != null && depsCs.contains( sDep ) ) {
1071 removeS = true;
1072 }
1073 }
1074 if (removeS) {
1075 it2.remove();
1076 }
1077 }
1078 ent.cluster = depclusters.get( 0 );
1079 known.add (ent);
1080 }
1081 }
1082 if (!toProcess.isEmpty())
1083 {
1084 walkKnownBundleDependenciesDown(known, toProcess);
1085 }
1086 if (!toProcess.isEmpty())
1087 {
1088 walkKnownBundleDependenciesUp(known, toProcess);
1089 }
1090 }
1091
1092 private static void walkKnownBundleDependenciesDown( List<BundleTuple> known, List<BundleTuple> toProcess )
1093 {
1094 boolean atLeastOneWasFound = false;
1095 for ( Iterator<BundleTuple> it = toProcess.iterator(); it.hasNext(); )
1096 {
1097 BundleTuple bundleTuple = it.next();
1098 boolean found = false;
1099 for ( BundleTuple knownBT : known)
1100 {
1101 Sets.SetView<String> is = Sets.intersection(bundleTuple.manifest.getOsgiExports() , knownBT.manifest.getOsgiImports() );
1102 if (!is.isEmpty()) {
1103 found = true;
1104 bundleTuple.cluster = knownBT.cluster;
1105 break;
1106 }
1107
1108 is = Sets.intersection(Collections.singleton( bundleTuple.manifest.getModule()), new HashSet(knownBT.manifest.getDependencyTokens()) );
1109 if (!is.isEmpty()) {
1110 found = true;
1111 bundleTuple.cluster = knownBT.cluster;
1112 break;
1113 }
1114
1115 }
1116 if (found) {
1117 atLeastOneWasFound = true;
1118 it.remove();
1119 known.add(bundleTuple);
1120 }
1121
1122 }
1123 if (!toProcess.isEmpty() && atLeastOneWasFound) {
1124 walkKnownBundleDependenciesDown( known, toProcess );
1125 }
1126 }
1127
1128 private static void walkKnownBundleDependenciesUp( List<BundleTuple> known, List<BundleTuple> toProcess )
1129 {
1130 boolean atLeastOneWasFound = false;
1131 for ( Iterator<BundleTuple> it = toProcess.iterator(); it.hasNext(); )
1132 {
1133 BundleTuple bundleTuple = it.next();
1134 boolean found = false;
1135 for ( BundleTuple knownBT : known)
1136 {
1137 Sets.SetView<String> is = Sets.intersection(bundleTuple.manifest.getOsgiImports() , knownBT.manifest.getOsgiExports() );
1138 if (!is.isEmpty()) {
1139 found = true;
1140 bundleTuple.cluster = knownBT.cluster;
1141 break;
1142 }
1143
1144 is = Sets.intersection(Collections.singleton( knownBT.manifest.getModule()), new HashSet(bundleTuple.manifest.getDependencyTokens()) );
1145 if (!is.isEmpty()) {
1146 found = true;
1147 bundleTuple.cluster = knownBT.cluster;
1148 break;
1149 }
1150
1151 }
1152 if (found) {
1153 atLeastOneWasFound = true;
1154 it.remove();
1155 known.add(bundleTuple);
1156 }
1157
1158 }
1159 if (!toProcess.isEmpty() && atLeastOneWasFound) {
1160 walkKnownBundleDependenciesDown( known, toProcess );
1161 }
1162 if (!toProcess.isEmpty() && atLeastOneWasFound) {
1163 walkKnownBundleDependenciesUp( known, toProcess );
1164 }
1165 }
1166
1167
1168 static Map<String, Set<String>> computeClusterOrdering( Map<String, Set<String>> clusterDependencies, Map<String, Set<String>> clusterModules )
1169 {
1170 Map<String, Set<String>> cluster2depClusters = new HashMap<String, Set<String>>();
1171 for ( Map.Entry<String, Set<String>> entry : clusterDependencies.entrySet() )
1172 {
1173 String cluster = entry.getKey();
1174 Set<String> deps = entry.getValue();
1175 for (Map.Entry<String, Set<String>> subEnt : clusterModules.entrySet()) {
1176 if (subEnt.getKey().equals( cluster) ) {
1177 continue;
1178 }
1179 Sets.SetView<String> is = Sets.intersection(subEnt.getValue(), deps );
1180 if (!is.isEmpty()) {
1181 addToMap( cluster2depClusters, cluster, Collections.singletonList( subEnt.getKey() ) );
1182 }
1183 }
1184 }
1185 return cluster2depClusters;
1186 }
1187
1188 static class BundleTuple {
1189 final Artifact artifact;
1190 final ExamineManifest manifest;
1191 String cluster;
1192
1193 BundleTuple( Artifact artifact, ExamineManifest manifest )
1194 {
1195 this.artifact = artifact;
1196 this.manifest = manifest;
1197 }
1198
1199 }
1200
1201 private static class ClusterTuple
1202 {
1203 final File location;
1204 final boolean newer;
1205
1206 private ClusterTuple( File clusterFile, boolean newer )
1207 {
1208 location = clusterFile;
1209 this.newer = newer;
1210 }
1211 }
1212
1213 static String createBundleConfigFile( String cnb, boolean autoload)
1214 {
1215 return
1216 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
1217 "<!DOCTYPE module PUBLIC \"-//NetBeans//DTD Module Status 1.0//EN\"\n" +
1218 " \"http://www.netbeans.org/dtds/module-status-1_0.dtd\">\n" +
1219 "<module name=\"" + cnb +"\">\n" +
1220 " <param name=\"autoload\">" + autoload + "</param>\n" +
1221 " <param name=\"eager\">false</param>\n" + (autoload ? "" : " <param name=\"enabled\">true</param>\n") +
1222 " <param name=\"jar\">modules/" + cnb.replace( ".", "-") + ".jar</param>\n" +
1223 " <param name=\"reloadable\">false</param>\n" +
1224 "</module>\n";
1225 }
1226
1227 static String createBundleUpdateTracking( String cnb, File moduleArt, File moduleConf, String specVersion )
1228 throws FileNotFoundException, IOException
1229 {
1230
1231 return
1232 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
1233 "<module codename=\"" + cnb + "\">\n" +
1234 " <module_version install_time=\"" + System.currentTimeMillis() + "\" last=\"true\" origin=\"installer\" specification_version=\"" + specVersion + "\">\n" +
1235 " <file crc=\"" + crcForFile( moduleConf ).getValue() + "\" name=\"config/Modules/" + cnb.replace( ".", "-" ) + ".xml\"/>\n" +
1236 " <file crc=\"" + crcForFile( moduleArt ).getValue() + "\" name=\"modules/" + cnb.replace( ".", "-" ) + ".jar\"/>\n" +
1237 " </module_version>\n" +
1238 "</module>";
1239
1240 }
1241
1242 static CRC32 crcForFile( File inFile )
1243 throws FileNotFoundException, IOException
1244 {
1245 CRC32 crc = new CRC32();
1246 InputStream inFileStream = new FileInputStream( inFile );
1247 try {
1248 byte[] array = new byte[(int) inFile.length()];
1249 int len = inFileStream.read( array );
1250 if ( len != array.length )
1251 {
1252 throw new IOException( "Cannot fully read " + inFile );
1253 }
1254 crc.update( array );
1255 }
1256 finally
1257 {
1258 inFileStream.close();
1259 }
1260
1261 return crc;
1262 }
1263
1264 }