Coverage Report - org.codehaus.mojo.nbm.CreateClusterAppMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
CreateClusterAppMojo
0%
0/573
0%
0/296
8.522
CreateClusterAppMojo$1
0%
0/2
N/A
8.522
CreateClusterAppMojo$2
0%
0/2
N/A
8.522
CreateClusterAppMojo$3
0%
0/2
N/A
8.522
CreateClusterAppMojo$4
0%
0/2
N/A
8.522
CreateClusterAppMojo$BundleTuple
100%
4/4
N/A
8.522
CreateClusterAppMojo$ClusterTuple
0%
0/5
N/A
8.522
 
 1  
 /* ==========================================================================
 2  
  * Copyright Milos Kleint
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  *  Unless required by applicable law or agreed to in writing, software
 11  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 12  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  *  See the License for the specific language governing permissions and
 14  
  *  limitations under the License.
 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  
  * Create the NetBeans module clusters/application for the 'nbm-application' packaging
 74  
  * projects
 75  
  *
 76  
  * @author <a href="mailto:mkleint@codehaus.org">Milos Kleint</a>
 77  
  */
 78  
 @Mojo(name="cluster-app", 
 79  
         defaultPhase= LifecyclePhase.PACKAGE, 
 80  
         requiresProject=true, 
 81  
         threadSafe = true,
 82  
         requiresDependencyResolution= ResolutionScope.RUNTIME )
 83  0
 public class CreateClusterAppMojo
 84  
     extends AbstractNbmMojo
 85  
 {
 86  
 
 87  
     /**
 88  
      * output directory where the the NetBeans application will be created.
 89  
      */
 90  
     @Parameter(defaultValue="${project.build.directory}", required=true)
 91  
     private File outputDirectory;
 92  
 
 93  
     /**
 94  
      * The Maven Project.
 95  
      */
 96  
     @Parameter(required=true, readonly=true, property="project")
 97  
     private MavenProject project;
 98  
 
 99  
     /**
 100  
      * The branding token for the application based on NetBeans platform.
 101  
      */
 102  
     @Parameter(property="netbeans.branding.token", required=true)
 103  
     protected String brandingToken;
 104  
 
 105  
     /**
 106  
      * Optional path to custom etc/${brandingToken}.conf file. If not defined,
 107  
      * a default template will be used.
 108  
      */
 109  
     @Parameter( property="netbeans.conf.file")
 110  
     private File etcConfFile;
 111  
 
 112  
     /**
 113  
      * Optional path to custom etc/${brandingToken}.clusters file. If not defined,
 114  
      * a default one will be generated.
 115  
      */
 116  
     @Parameter(property="netbeans.clusters.file")
 117  
     private File etcClustersFile;
 118  
 
 119  
     /**
 120  
      * Directory which contains the executables that will be copied to
 121  
      * the final application's bin/ directory.
 122  
      * Please note that the name of the executables shall generally
 123  
      * match the brandingToken parameter. Otherwise the application can be wrongly branded.
 124  
      */
 125  
     @Parameter(property="netbeans.bin.directory")
 126  
     private File binDirectory;
 127  
 
 128  
     /**
 129  
      * If the depending NBM file doesn't contain any application cluster information,
 130  
      * use this value as default location for such module NBMs.
 131  
      * @since 3.2
 132  
      */
 133  
     @Parameter(defaultValue="extra")
 134  
     private String defaultCluster;
 135  
     
 136  
     /**
 137  
      * attempts to verify the integrity of module artifacts making sure that all dependencies are included
 138  
      * and that all required tokens are provided
 139  
      * @since 3.10
 140  
      */
 141  
     @Parameter(defaultValue = "true", property = "netbeans.verify.integrity")
 142  
     private boolean verifyIntegrity;
 143  
     
 144  0
     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" //MNBMODULE-234
 155  
     });
 156  
 
 157  
 
 158  
     // <editor-fold defaultstate="collapsed" desc="Component parameters">
 159  
     
 160  
     @Component
 161  
     private ArtifactFactory artifactFactory;
 162  
 
 163  
     @Component
 164  
     private ArtifactResolver artifactResolver;
 165  
 
 166  
     /**
 167  
      * Local maven repository.
 168  
      *
 169  
      */
 170  
     @Parameter(required=true, readonly=true, property="localRepository")
 171  
     protected ArtifactRepository localRepository;
 172  
 
 173  
 // end of component params custom code folding
 174  
 // </editor-fold>
 175  
 
 176  
     public void execute()
 177  
         throws MojoExecutionException, MojoFailureException
 178  
     {
 179  
 
 180  0
         File nbmBuildDirFile = new File( outputDirectory, brandingToken );
 181  0
         if ( !nbmBuildDirFile.exists() )
 182  
         {
 183  0
             nbmBuildDirFile.mkdirs();
 184  
         }
 185  
 
 186  0
         if ( "nbm-application".equals( project.getPackaging() ) )
 187  
         {
 188  0
             Project antProject = registerNbmAntTasks();
 189  
 
 190  0
             Set<String> wrappedBundleCNBs = new HashSet<String>(100);
 191  0
             Map<String, Set<String>> clusterDependencies = new HashMap<String, Set<String>>();
 192  0
             Map<String, Set<String>> clusterModules = new HashMap<String, Set<String>>();
 193  
             
 194  
             //verify integrity
 195  0
             Set<String> modulesCNBs = new HashSet<String>(200);
 196  0
             Set<String> dependencyCNBs = new HashSet<String>(200);
 197  0
             Map<String, Set<String>> dependencyCNBBacktraces = new HashMap<String, Set<String>>(50);
 198  0
             Set<String> requireTokens = new HashSet<String>(50);
 199  0
             Map<String, Set<String>> requireTokensBacktraces = new HashMap<String, Set<String>>(50);
 200  0
             Set<String> provideTokens = new HashSet<String>(50);
 201  0
             Set<String> osgiImports = new HashSet<String>(50);
 202  0
             Map<String, Set<String>> osgiImportsBacktraces = new HashMap<String, Set<String>>(50);
 203  0
             Set<String> osgiExports = new HashSet<String>(50);
 204  0
             Set<String> osgiExportsSubs = new HashSet<String>(50); //a way to deal with nb module declaring xxx.** (subpackages) declaration that is consumed by osgi imports
 205  
             
 206  0
             List<BundleTuple> bundles = new ArrayList<BundleTuple>();
 207  
 
 208  
             @SuppressWarnings( "unchecked" )
 209  0
             Set<Artifact> artifacts = project.getArtifacts();
 210  0
             for ( Artifact art : artifacts )
 211  
             {
 212  0
                 ArtifactResult res = turnJarToNbmFile( art, artifactFactory, artifactResolver, project, localRepository );
 213  0
                 if ( res.hasConvertedArtifact() )
 214  
                 {
 215  0
                     art = res.getConvertedArtifact();
 216  
                 }
 217  
 
 218  0
                 if ( art.getType().equals( "nbm-file" ) )
 219  
                 {
 220  
                     try
 221  
                     {
 222  0
                         JarFile jf = new JarFile( art.getFile() );
 223  
                         try
 224  
                         {
 225  0
                             String clusterName = findCluster( jf );                            
 226  0
                             ClusterTuple cluster = processCluster( clusterName, nbmBuildDirFile, art );
 227  
                             
 228  0
                                 getLog().debug( "Copying " + art.getId() + " to cluster " + clusterName );
 229  0
                                 Enumeration<JarEntry> enu = jf.entries();
 230  
 
 231  
                                 // we need to trigger this ant task to generate the update_tracking file.
 232  0
                                 MakeListOfNBM makeTask = (MakeListOfNBM) antProject.createTask( "genlist" );
 233  0
                                 antProject.setNewProperty( "module.name", art.getFile().getName() ); // TODO
 234  0
                                 antProject.setProperty( "cluster.dir", clusterName );
 235  0
                                 FileSet set = makeTask.createFileSet();
 236  0
                                 set.setDir( cluster.location );
 237  0
                                 makeTask.setOutputfiledir( cluster.location );
 238  0
                                 String[] executables = null;
 239  0
                                 File classpathRoot = null;
 240  0
                                 String classPath = null;
 241  0
                                 while ( enu.hasMoreElements() )
 242  
                                 {
 243  0
                                     JarEntry ent = enu.nextElement();
 244  0
                                     String name = ent.getName();
 245  
                                     //MNBMODULE-176
 246  0
                                     if (name.equals("Info/executables.list")) {
 247  0
                                         if (cluster.newer) {
 248  0
                                             InputStream is = jf.getInputStream( ent );
 249  0
                                             executables = StringUtils.split( IOUtil.toString( is, "UTF-8" ), "\n");
 250  0
                                         }
 251  
                                     }
 252  0
                                     else if ( name.startsWith( "netbeans/" ) )
 253  
                                     { // ignore everything else.
 254  0
                                         String path = clusterName + name.substring( "netbeans".length() );
 255  0
                                         boolean ispack200 = path.endsWith( ".jar.pack.gz" );
 256  0
                                         if ( ispack200 )
 257  
                                         {
 258  0
                                             path = path.replace( ".jar.pack.gz", ".jar" );
 259  
                                         }
 260  0
                                         File fl = new File( nbmBuildDirFile, path.replace( "/", File.separator ) );
 261  0
                                         String part = name.substring( "netbeans/".length() );
 262  0
                                         if ( ispack200 )
 263  
                                         {
 264  0
                                             part = part.replace( ".jar.pack.gz", ".jar" );
 265  
                                         }
 266  0
                                         if (cluster.newer) 
 267  
                                         {
 268  0
                                             if ( ent.isDirectory() )
 269  
                                             {
 270  0
                                                 fl.mkdirs();
 271  
                                             }
 272  0
                                             else if ( path.endsWith( ".external" ) ) // MNBMODULE-138
 273  
                                             {
 274  0
                                                 InputStream is = jf.getInputStream( ent );
 275  
                                                 try
 276  
                                                 {
 277  0
                                                     externalDownload( new File( fl.getParentFile(),
 278  
                                                                                 fl.getName().replaceFirst( "[.]external$",
 279  
                                                                                                            "" ) ), is );
 280  
                                                 }
 281  
                                                 finally
 282  
                                                 {
 283  0
                                                     is.close();
 284  0
                                                 }
 285  
                                                 //MNBMODULE-192
 286  0
                                                 set.appendIncludes( new String[] { name.substring( "netbeans/".length(), name.length() - ".external".length() ) } );
 287  0
                                             }
 288  
                                             else
 289  
                                             {
 290  0
                                                 set.appendIncludes( new String[] { part } );
 291  
 
 292  0
                                                 fl.getParentFile().mkdirs();
 293  0
                                                 fl.createNewFile();
 294  0
                                                 BufferedOutputStream outstream = null;
 295  
                                                 try
 296  
                                                 {
 297  0
                                                     outstream = new BufferedOutputStream( new FileOutputStream( fl ) );
 298  0
                                                     InputStream instream = jf.getInputStream( ent );
 299  0
                                                     if ( ispack200 )
 300  
                                                     {
 301  0
                                                         Pack200.Unpacker unp = Pack200.newUnpacker();
 302  0
                                                         JarOutputStream jos = new JarOutputStream( outstream );
 303  0
                                                         GZIPInputStream gzip = new GZIPInputStream( instream );
 304  
                                                         try
 305  
                                                         {
 306  0
                                                             unp.unpack( gzip, jos );
 307  
                                                         }
 308  
                                                         finally
 309  
                                                         {
 310  0
                                                             jos.close();
 311  0
                                                         }
 312  0
                                                     }
 313  
                                                     else
 314  
                                                     {
 315  0
                                                         IOUtil.copy( instream, outstream );
 316  
                                                     }
 317  
                                                 }
 318  
                                                 finally
 319  
                                                 {
 320  0
                                                     IOUtil.close( outstream );
 321  0
                                                 }
 322  
                                             }
 323  
                                         }
 324  
                                             
 325  
                                             //TODO examine netbeans/config/Modules to see if the module is autoload/eager
 326  
                                             // in verifyIntegrity these could be handled more gracefully than regular modules.
 327  
                                             //eager is simpler, does not need to have module dependencies satisfied.
 328  
                                             //autoload needs checking if any of the other modules declares a dependency on it. if not, also safe to ignore?
 329  
                                             
 330  
                                             
 331  
                                             // now figure which one of the jars is the module jar..
 332  0
                                             if ( part.matches("(modules|core|lib)/[^/]+[.]jar") )
 333  
                                             {
 334  0
                                                 ExamineManifest ex = new ExamineManifest( getLog() );
 335  0
                                                 ex.setJarFile( fl );
 336  0
                                                 ex.setPopulateDependencies( true );
 337  0
                                                 ex.checkFile();
 338  0
                                                 if ( ex.isNetBeansModule() )
 339  
                                                 {
 340  0
                                                     makeTask.setModule( part );
 341  0
                                                     addToMap(clusterDependencies, clusterName, ex.getDependencyTokens());
 342  0
                                                     addToMap(clusterModules, clusterName, Collections.singletonList( ex.getModule() ));
 343  0
                                                     if (ex.getClasspath().length() > 0) { //MNBMODULE-220
 344  0
                                                         classPath = ex.getClasspath();
 345  0
                                                         classpathRoot = fl.getParentFile();
 346  
                                                     }
 347  
                                                 }
 348  0
                                                 if (verifyIntegrity) {
 349  0
                                                     dependencyCNBs.addAll(ex.getDependencyTokens());
 350  0
                                                     modulesCNBs.add(ex.getModule());
 351  0
                                                     for (String d : ex.getDependencyTokens()) {
 352  0
                                                         addToMap(dependencyCNBBacktraces, d, Collections.singletonList( ex.getModule() ));
 353  0
                                                     }
 354  0
                                                     if (ex.isNetBeansModule()) {
 355  0
                                                         requireTokens.addAll(ex.getNetBeansRequiresTokens());
 356  0
                                                         for (String r : ex.getNetBeansRequiresTokens()) {
 357  0
                                                             addToMap( requireTokensBacktraces, r, Collections.singletonList( ex.getModule()));
 358  0
                                                         }
 359  0
                                                         provideTokens.addAll(ex.getNetBeansProvidesTokens());
 360  0
                                                         for (String pack : ex.getPackages()) {
 361  0
                                                             if (pack.endsWith( ".**")) {
 362  
                                                                 //what to do with subpackages?
 363  0
                                                                 pack = pack.substring( 0, pack.length() - ".**".length());
 364  0
                                                                 osgiExportsSubs.add( pack );
 365  0
                                                             } else if (pack.endsWith( ".*")) {
 366  0
                                                                 pack = pack.substring( 0, pack.length() - ".*".length());
 367  0
                                                                 osgiExports.add(pack);                                                            
 368  
                                                             }
 369  0
                                                         }
 370  
                                                         
 371  
                                                     }
 372  
                                                 }
 373  
                                             }
 374  
                                         }
 375  0
                                     }
 376  0
                                     if (classPath != null) { //MNBMODULE-220 collect wrappedbundleCNBs, later useful in assignClustersToBundles(), these get removed from list of bundles.
 377  0
                                         String[] paths = StringUtils.split( classPath, " ");
 378  0
                                         for (String path : paths) {
 379  0
                                             path = path.trim();
 380  0
                                             File classpathFile = new File(classpathRoot, path);
 381  0
                                             if (path.equals("${java.home}/lib/ext/jfxrt.jar")) { //MNBMODULE-228
 382  0
                                                 String jhm = System.getProperty("java.home");
 383  0
                                                 classpathFile = new File(new File(new File(new File(jhm), "lib"), "ext"), "jfxrt.jar");
 384  0
                                                 if (!classpathFile.exists()) {
 385  0
                                                     File jdk7 = new File(new File(new File(jhm), "lib"), "jfxrt.jar");
 386  0
                                                     if (jdk7.exists()) {
 387  0
                                                         classpathFile = jdk7;
 388  
                                                     }
 389  
                                                 }
 390  
                                             }
 391  0
                                             if (!classpathFile.isFile()) {
 392  0
                                                 getLog().warn( "Could not resolve Class-Path item in " + art.getId() + ", path is:" + path +  ", skipping");
 393  0
                                                 continue; //try to guard against future failures
 394  
                                             } 
 395  0
                                             ExamineManifest ex = new ExamineManifest( getLog() );
 396  0
                                             ex.setJarFile( classpathFile );
 397  
                                             //ex.setPopulateDependencies( true );
 398  0
                                             ex.checkFile();
 399  0
                                             if (ex.isOsgiBundle()) {
 400  0
                                                 wrappedBundleCNBs.add( ex.getModule() );
 401  
                                             }
 402  
                                         }
 403  
                                     }
 404  0
                             if ( cluster.newer )
 405  
                             {
 406  
                                 try
 407  
                                 {
 408  0
                                     makeTask.execute();
 409  
                                 }
 410  0
                                 catch ( BuildException e )
 411  
                                 {
 412  0
                                     getLog().error( "Cannot Generate update_tracking XML file from " + art.getFile() );
 413  0
                                     throw new MojoExecutionException( e.getMessage(), e );
 414  0
                                 }
 415  
 
 416  0
                                 if ( executables != null )
 417  
                                 {
 418  
                                     //MNBMODULE-176
 419  0
                                     for ( String exec : executables )
 420  
                                     {
 421  0
                                         exec = exec.replace( "/", File.separator );
 422  0
                                         File execFile = new File( cluster.location, exec );
 423  0
                                         if ( execFile.exists() )
 424  
                                         {
 425  0
                                             execFile.setExecutable( true, false );
 426  
                                         }
 427  
                                     }
 428  
                                 }
 429  
                             }
 430  
                             
 431  
                         }
 432  
                         finally
 433  
                         {
 434  0
                             jf.close();
 435  0
                         }
 436  
                     }
 437  0
                     catch ( IOException ex )
 438  
                     {
 439  0
                         getLog().error( art.getFile().getAbsolutePath(), ex );
 440  0
                     }
 441  
                 }
 442  0
                 if ( res.isOSGiBundle() )
 443  
                 {
 444  0
                     ExamineManifest ex = res.getExaminedManifest();
 445  0
                     bundles.add( new BundleTuple( art,  ex) );
 446  0
                     if (verifyIntegrity) {
 447  0
                         dependencyCNBs.addAll(ex.getDependencyTokens());
 448  0
                         for ( String d : ex.getDependencyTokens() )
 449  
                         {
 450  0
                             addToMap( dependencyCNBBacktraces, d, Collections.singletonList( ex.getModule() ) );
 451  0
                         }
 452  0
                         modulesCNBs.add(ex.getModule());
 453  0
                         osgiImports.addAll( ex.getOsgiImports());
 454  0
                         for ( String d : ex.getOsgiImports() )
 455  
                         {
 456  0
                             addToMap( osgiImportsBacktraces, d, Collections.singletonList( ex.getModule() ) );
 457  0
                         }
 458  
                         
 459  0
                         osgiExports.addAll( ex.getOsgiExports());
 460  
                     }
 461  
                 } 
 462  0
             }
 463  
             
 464  0
             if (verifyIntegrity) {
 465  0
                 if (getLog().isDebugEnabled()) {
 466  0
                     getLog().debug( "All found codenamebases:" + Arrays.toString( modulesCNBs.toArray()) );
 467  0
                     getLog().debug( "All found OSGI exports:" + Arrays.toString( osgiExports.toArray()) );
 468  0
                     getLog().debug( "All found provided tokens:" + Arrays.toString( provideTokens.toArray()) );
 469  
                 }
 470  0
                 dependencyCNBs.removeAll( modulesCNBs );
 471  0
                 if (modulesCNBs.contains( "org.netbeans.modules.netbinox")) {
 472  0
                     dependencyCNBs.remove( "org.eclipse.osgi"); //this is special.
 473  
                 }
 474  0
                 osgiImports.removeAll( osgiExports );
 475  0
                 Iterator<String> it = osgiImports.iterator();
 476  0
                 while (it.hasNext()) {
 477  0
                     String s = it.next();
 478  0
                     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  0
                         it.remove();
 480  0
                         continue;
 481  
                     }
 482  0
                     for (String sub : osgiExportsSubs) {
 483  0
                         if (s.startsWith( sub )) {
 484  0
                             it.remove();
 485  0
                             break;
 486  
                         }
 487  0
                     }
 488  0
                 }
 489  0
                 requireTokens.removeAll( provideTokens );
 490  0
                 requireTokens.removeAll( defaultPlatformTokens );
 491  0
                 if (!dependencyCNBs.isEmpty() || !osgiImports.isEmpty() ||!requireTokens.isEmpty()) {
 492  0
                     if (!dependencyCNBs.isEmpty()) {
 493  0
                         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  0
                         for (String s : dependencyCNBs) {
 495  0
                             Set<String> back = dependencyCNBBacktraces.get( s );
 496  0
                             getLog().error("   " + s + (back != null ? "          ref: " + Arrays.toString( back.toArray()) : ""));
 497  0
                         }
 498  
                     }
 499  0
                     if (!osgiImports.isEmpty()) {
 500  0
                         getLog().error("Some OSGi imports are not satisfied by included bundles' exports. The application will fail starting up. The missing imports are:");
 501  0
                         for (String s : osgiImports) {
 502  0
                             Set<String> back = osgiImportsBacktraces.get( s );
 503  0
                             getLog().error("   " + s + (back != null ? "          ref: " + Arrays.toString( back.toArray()) : ""));
 504  0
                         }
 505  
                     }
 506  0
                      if (!requireTokens.isEmpty()) {
 507  0
                         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  0
                         for (String s : requireTokens) {
 509  0
                             Set<String> back = requireTokensBacktraces.get( s );
 510  0
                             getLog().error("   " + s + (back != null ? "          ref: " + Arrays.toString( back.toArray()) : ""));
 511  0
                         }
 512  
                     }
 513  0
                     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  0
                     getLog().info( "Integrity verification passed.");
 516  
                 }
 517  0
             } else {
 518  0
                 getLog().info( "Integrity verification skipped.");
 519  
             }
 520  
             
 521  
             //attempt to sort clusters based on the dependencies and cluster content.
 522  0
             Map<String, Set<String>> cluster2depClusters = computeClusterOrdering( clusterDependencies, clusterModules );
 523  0
             clusterModules.clear();
 524  
         
 525  
             //now assign the cluster to bundles based on dependencies..
 526  0
             assignClustersToBundles( bundles, wrappedBundleCNBs, clusterDependencies, cluster2depClusters, getLog() );
 527  
             
 528  
             
 529  0
             for (BundleTuple ent : bundles) {
 530  0
                 Artifact art = ent.artifact;
 531  0
                 final ExamineManifest ex = ent.manifest;
 532  
                 
 533  0
                 String clstr = ent.cluster;
 534  0
                 if (clstr == null) {
 535  0
                     clstr = defaultCluster;
 536  
                 }
 537  
                 
 538  0
                 ClusterTuple cluster = processCluster( clstr, nbmBuildDirFile, art );
 539  0
                 if ( cluster.newer )
 540  
                 {
 541  0
                     getLog().info( "Copying " + art.getId() + " to cluster " + clstr );
 542  0
                     File modules = new File( cluster.location, "modules" );
 543  0
                     modules.mkdirs();
 544  0
                     File config = new File( cluster.location, "config" );
 545  0
                     File confModules = new File( config, "Modules" );
 546  0
                     confModules.mkdirs();
 547  0
                     File updateTracking = new File( cluster.location, "update_tracking" );
 548  0
                     updateTracking.mkdirs();
 549  0
                     final String cnb = ex.getModule();
 550  0
                     final String cnbDashed = cnb.replace( ".", "-" );
 551  0
                     final File moduleArt = new File( modules, cnbDashed + ".jar" ); //do we need the file in some canotical name pattern?
 552  0
                     final String specVer = ex.getSpecVersion();
 553  
                     try
 554  
                     {
 555  0
                         FileUtils.copyFile( art.getFile(), moduleArt );
 556  0
                         final File moduleConf = new File( confModules, cnbDashed + ".xml" );
 557  0
                         FileUtils.copyStreamToFile( new InputStreamFacade() {
 558  
                             @Override
 559  
                             public InputStream getInputStream() throws IOException
 560  
                             {
 561  0
                                 return new StringInputStream( createBundleConfigFile( cnb, ex.isBundleAutoload() ), "UTF-8" );
 562  
                             }
 563  
                         }, moduleConf );
 564  0
                         FileUtils.copyStreamToFile( new InputStreamFacade() {
 565  
                             @Override
 566  
                             public InputStream getInputStream() throws IOException
 567  
                             {
 568  0
                                 return new StringInputStream( createBundleUpdateTracking( cnb, moduleArt, moduleConf, specVer ), "UTF-8" );
 569  
                             }
 570  
                         }, new File( updateTracking, cnbDashed + ".xml" ) );
 571  
                     }
 572  0
                     catch ( IOException exc )
 573  
                     {
 574  0
                         getLog().error( exc );
 575  0
                     }
 576  
                 }
 577  0
             }
 578  
 
 579  0
             getLog().info(
 580  
                 "Created NetBeans module cluster(s) at " + nbmBuildDirFile.getAbsoluteFile() );
 581  
 
 582  0
         }
 583  
         else
 584  
         {
 585  0
             throw new MojoExecutionException(
 586  
                 "This goal only makes sense on project with nbm-application packaging" );
 587  
         }
 588  
         //in 6.1 the rebuilt modules will be cached if the timestamp is not touched.
 589  0
         File[] files = nbmBuildDirFile.listFiles();
 590  0
         for ( int i = 0; i < files.length; i++ )
 591  
         {
 592  0
             if ( files[i].isDirectory() )
 593  
             {
 594  0
                 File stamp = new File( files[i], ".lastModified" );
 595  0
                 if ( !stamp.exists() )
 596  
                 {
 597  
                     try
 598  
                     {
 599  0
                         stamp.createNewFile();
 600  
                     }
 601  0
                     catch ( IOException ex )
 602  
                     {
 603  0
                         ex.printStackTrace();
 604  0
                     }
 605  
                 }
 606  0
                 stamp.setLastModified( new Date().getTime() );
 607  
             }
 608  
         }
 609  
         try
 610  
         {
 611  0
             createBinEtcDir( nbmBuildDirFile, brandingToken );
 612  
         }
 613  0
         catch ( IOException ex )
 614  
         {
 615  0
             throw new MojoExecutionException(
 616  
                 "Cannot process etc folder content creation.", ex );
 617  0
         }
 618  0
     }
 619  0
     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  0
         ZipEntry entry = jf.getEntry( "Info/info.xml" );
 626  0
         InputStream ins = jf.getInputStream( entry );
 627  0
         String str = IOUtil.toString( ins, "UTF8" );
 628  0
         Matcher m = patt.matcher( str );
 629  0
         if ( !m.matches() )
 630  
         {
 631  0
             getLog().info( "Cannot find cluster for " + jf.getName() + " Falling back to default value - '"
 632  
                                + defaultCluster + "'." );
 633  0
             return defaultCluster;
 634  
         }
 635  
         else
 636  
         {
 637  0
             return m.group( 1 );
 638  
         }
 639  
     }
 640  
 
 641  
     /**
 642  
      * 
 643  
      * @param buildDir Directory where the platform bundle is built
 644  
      * @param brandingToken
 645  
      * 
 646  
      * @throws java.io.IOException
 647  
      */
 648  
     private void createBinEtcDir( File buildDir, String brandingToken )
 649  
         throws IOException, MojoExecutionException
 650  
     {
 651  0
         File etcDir = new File( buildDir + File.separator + "etc" );
 652  0
         etcDir.mkdir();
 653  
 
 654  
         // create app.clusters which contains a list of clusters to include in the application
 655  
 
 656  0
         File clusterConf = new File( etcDir + File.separator + brandingToken + ".clusters" );
 657  
         String clustersString;
 658  0
         if ( etcClustersFile != null )
 659  
         {
 660  0
             clustersString = FileUtils.fileRead( etcClustersFile, "UTF-8" );
 661  
         }
 662  
         else
 663  
         {
 664  0
             clusterConf.createNewFile();
 665  0
             StringBuffer buffer = new StringBuffer();
 666  0
             File[] clusters = buildDir.listFiles( new FileFilter()
 667  0
             {
 668  
 
 669  
                 @Override
 670  
                 public boolean accept( File pathname )
 671  
                 {
 672  0
                     return new File( pathname, ".lastModified" ).exists();
 673  
                 }
 674  
             } );
 675  0
             for ( File cluster : clusters )
 676  
             {
 677  0
                 buffer.append( cluster.getName() );
 678  0
                 buffer.append( "\n" );
 679  
             }
 680  0
             clustersString = buffer.toString();
 681  
         }
 682  
 
 683  0
         FileUtils.fileWrite( clusterConf.getAbsolutePath(), clustersString );
 684  
 
 685  0
         File confFile = etcConfFile;
 686  
         String str;
 687  0
         if ( confFile == null )
 688  
         {
 689  0
             File harnessDir = new File( buildDir, "harness" );
 690  
             // app.conf contains default options and other settings
 691  0
             confFile = new File(
 692  
                     harnessDir.getAbsolutePath() + File.separator + "etc" + File.separator + "app.conf" );
 693  0
             if ( confFile.exists() )
 694  
             {
 695  0
                 str = FileUtils.fileRead( confFile, "UTF-8" );
 696  
             }
 697  
             else 
 698  
             {
 699  0
                 getLog().debug( "Using fallback app.conf shipping with the nbm-maven-plugin." );
 700  0
                 InputStream instream = null;
 701  
                 try
 702  
                 {
 703  0
                     instream = getClass().getClassLoader().getResourceAsStream( "harness/etc/app.conf" );
 704  0
                     str = IOUtil.toString( instream, "UTF-8" );
 705  
                 }
 706  
                 finally
 707  
                 {
 708  0
                     IOUtil.close( instream );
 709  0
                 }
 710  
             }
 711  0
         }
 712  
         else
 713  
         {
 714  0
             str = FileUtils.fileRead( confFile, "UTF-8" );
 715  
         }
 716  0
         File confDestFile = new File(
 717  
             etcDir.getAbsolutePath() + File.separator + brandingToken + ".conf" );
 718  
 
 719  0
         str = str.replace( "${branding.token}", brandingToken );
 720  0
         FileUtils.fileWrite( confDestFile.getAbsolutePath(), "UTF-8", str );
 721  
 
 722  0
         File destBinDir = new File( buildDir + File.separator + "bin" );
 723  0
         destBinDir.mkdir();
 724  
 
 725  
         File binDir;
 726  0
         File destExeW = new File( destBinDir, brandingToken + "_w.exe" );
 727  0
         File destExe = new File( destBinDir, brandingToken + ".exe" );
 728  0
         File destExe64 = new File( destBinDir, brandingToken + "64.exe" );
 729  0
         File destSh = new File( destBinDir, brandingToken );
 730  
 
 731  0
         if ( binDirectory != null )
 732  
         {
 733  
             //we have custom launchers.
 734  0
             binDir = binDirectory;
 735  0
             File[] fls = binDir.listFiles();
 736  0
             if ( fls == null )
 737  
             {
 738  0
                 throw new MojoExecutionException( "Parameter 'binDirectory' has to point to an existing folder." );
 739  
             }
 740  0
             for ( File fl : fls )
 741  
             {
 742  0
                 String name = fl.getName();
 743  0
                 File dest = null;
 744  0
                 if ( name.endsWith( "_w.exe" ) ) 
 745  
                 {
 746  0
                     dest = destExeW;
 747  
                 }
 748  0
                 else if ( name.endsWith( "64.exe" ) )
 749  
                 {
 750  0
                     dest = destExe64;
 751  
                 }
 752  0
                 else if ( name.endsWith( ".exe" ) )
 753  
                 {
 754  0
                     dest = destExe;
 755  
                 }
 756  0
                 else if ( !name.contains( "." ) || name.endsWith( ".sh" ) )
 757  
                 {
 758  0
                     dest = destSh;
 759  
                 }
 760  0
                 if ( dest != null  && fl.exists() ) //in 6.7 the _w.exe file is no more.
 761  
                 {
 762  0
                     FileUtils.copyFile( fl, dest );
 763  
                 }
 764  
                 else
 765  
                 {
 766  
                     //warn about file not being copied
 767  
                 }
 768  
             }
 769  0
         }
 770  
         else
 771  
         {
 772  0
             File harnessDir = new File( buildDir, "harness" );
 773  
             //we have org-netbeans-modules-apisupport-harness in target area, just use it's own launchers.
 774  0
             binDir = new File(
 775  
                     harnessDir.getAbsolutePath() + File.separator + "launchers" );
 776  0
             if ( binDir.exists() )
 777  
             {
 778  0
                 File exe = new File( binDir, "app.exe" );
 779  0
                 FileUtils.copyFile( exe, destExe );
 780  0
                 File exe64 = new File( binDir, "app64.exe" );
 781  0
                 if ( exe64.isFile() )
 782  
                 {
 783  0
                     FileUtils.copyFile( exe64, destExe64 );
 784  
                 }
 785  0
                 File exew = new File( binDir, "app_w.exe" );
 786  0
                 if ( exew.exists() ) //in 6.7 the _w.exe file is no more.
 787  
                 {
 788  0
                     FileUtils.copyFile( exew, destExeW );
 789  
                 }
 790  0
                 File sh = new File( binDir, "app.sh" );
 791  0
                 FileUtils.copyFile( sh, destSh );
 792  0
             }
 793  
             else
 794  
             {
 795  0
                 File nbm = getHarnessNbm();
 796  0
                 ZipFile zip = new ZipFile( nbm );
 797  
                 try {
 798  0
                     getLog().debug( "Using fallback executables from downloaded org-netbeans-modules-apisupport-harness nbm file." );
 799  0
                     writeFromZip(zip, "netbeans/launchers/app.sh",  destSh, true );
 800  0
                     writeFromZip(zip, "netbeans/launchers/app.exe",  destExe, true );
 801  0
                     writeFromZip(zip, "netbeans/launchers/app64.exe",  destExe64, false );
 802  0
                     writeFromZip(zip, "netbeans/launchers/app_w.exe",  destExeW, false );
 803  
                 } finally {
 804  0
                     zip.close();
 805  0
                 }
 806  
             }
 807  
         }
 808  
 
 809  0
         Project antProject = antProject();
 810  
 
 811  0
         Chmod chmod = (Chmod) antProject.createTask( "chmod" );
 812  0
         FileSet fs = new FileSet();
 813  0
         fs.setDir( destBinDir );
 814  0
         fs.setIncludes( "*" );
 815  0
         chmod.addFileset( fs );
 816  0
         chmod.setPerm( "755" );
 817  0
         chmod.execute();
 818  0
     }
 819  
 
 820  
     private void writeFile( String path, File destSh )
 821  
         throws IOException
 822  
     {
 823  0
         InputStream instream = null;
 824  0
         OutputStream output = null;
 825  
         try
 826  
         {
 827  0
             instream = getClass().getClassLoader().getResourceAsStream( path );
 828  0
             if ( instream == null )
 829  
             {
 830  0
                 throw new FileNotFoundException( path );
 831  
             }
 832  0
             destSh.createNewFile();
 833  0
             output = new BufferedOutputStream( new FileOutputStream( destSh ) );
 834  0
             IOUtil.copy( instream, output );
 835  
         }
 836  
         finally
 837  
         {
 838  0
             IOUtil.close( instream );
 839  0
             IOUtil.close( output );
 840  0
         }
 841  0
     }
 842  
 
 843  
     private ClusterTuple processCluster( String cluster, File nbmBuildDirFile, Artifact art )
 844  
     {
 845  0
         File clusterFile = new File( nbmBuildDirFile, cluster );
 846  0
         boolean newer = false;
 847  0
         if ( !clusterFile.exists() )
 848  
         {
 849  0
             clusterFile.mkdir();
 850  0
             newer = true;
 851  
         }
 852  
         else
 853  
         {
 854  0
             File stamp = new File( clusterFile, ".lastModified" );
 855  0
             if ( stamp.lastModified() < art.getFile().lastModified() )
 856  
             {
 857  0
                 newer = true;
 858  
             }
 859  
         }
 860  0
         return new ClusterTuple( clusterFile, newer );
 861  
     }
 862  
 
 863  
     private void externalDownload( File f, InputStream is )
 864  
         throws IOException
 865  
     {
 866  
         // Cf. org.netbeans.nbbuild.AutoUpdate
 867  0
         BufferedReader r = new BufferedReader( new InputStreamReader( is, "UTF-8" ) );
 868  0
         long crc = -1;
 869  0
         long size = -1;
 870  0
         boolean found = false;
 871  
         String line;
 872  0
         while ( ( line = r.readLine() ) != null )
 873  
         {
 874  0
             if ( line.startsWith( "CRC:" ) )
 875  
             {
 876  0
                 crc = Long.parseLong( line.substring( 4 ).trim() );
 877  
             }
 878  0
             else if ( line.startsWith( "URL:m2:/" ) )
 879  
             {
 880  0
                 if ( ! found )
 881  
                 {
 882  0
                     String[] coords = line.substring( 8 ).trim().split( ":" );
 883  
                     Artifact artifact;
 884  0
                     if ( coords.length == 4 )
 885  
                     {
 886  0
                         artifact = artifactFactory.createArtifact( coords[0], coords[1], coords[2], null, coords[3] );
 887  
                     }
 888  
                     else
 889  
                     {
 890  0
                         artifact = artifactFactory.createArtifactWithClassifier( coords[0], coords[1], coords[2], coords[3], coords[4] );
 891  
                     }
 892  
                     try
 893  
                     {
 894  0
                         artifactResolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
 895  0
                         FileUtils.copyFile( artifact.getFile(), f );
 896  0
                         found = true;
 897  
                     }
 898  0
                     catch ( AbstractArtifactResolutionException x )
 899  
                     {
 900  0
                         getLog().warn( "Cannot find " + line.substring( 8 ), x );
 901  0
                     }
 902  0
                 }
 903  
             }
 904  0
             else if ( line.startsWith( "URL:" ) )
 905  
             {
 906  0
                 if ( ! found )
 907  
                 {
 908  0
                     String url = line.substring( 4 ).trim();
 909  
                     try
 910  
                     {
 911  
                         // XXX use Wagon API instead
 912  0
                         FileUtils.copyURLToFile( new URL( url ), f );
 913  0
                         found = true;
 914  
                     }
 915  0
                     catch ( IOException x )
 916  
                     {
 917  0
                         getLog().warn( "Cannot download " + url, x );
 918  0
                     }
 919  0
                 }
 920  
             }
 921  0
             else if ( line.startsWith( "SIZE:" ) )
 922  
             {
 923  0
                 size = Long.parseLong( line.substring( 5 ).trim() );
 924  
             }
 925  
             else
 926  
             {
 927  0
                 getLog().warn( "Unrecognized line: " + line );
 928  
             }
 929  
         }
 930  0
         if ( ! found )
 931  
         {
 932  0
             throw new IOException( "Could not download " + f );
 933  
         }
 934  0
         if ( crc != -1 && crc != crcForFile( f ).getValue() )
 935  
         {
 936  0
             throw new IOException( "CRC-32 of " + f + " does not match declared " + crc );
 937  
         }
 938  0
         if ( size != -1 && size != f.length() )
 939  
         {
 940  0
             throw new IOException( "Size of " + f + " does not match declared " + size );
 941  
         }
 942  0
     }
 943  
 
 944  
     private File getHarnessNbm() throws MojoExecutionException
 945  
     {
 946  
         @SuppressWarnings( "unchecked" )
 947  0
         Set<Artifact> artifacts = project.getArtifacts();
 948  0
         String version = null;
 949  0
         for (Artifact a : artifacts) {
 950  0
             if ("org.netbeans.modules".equals(a.getGroupId()) && "org-netbeans-bootstrap".equals(a.getArtifactId())) {
 951  0
                 version = a.getBaseVersion(); //base version in non-snapshot should equals version, in snapshots to X-SNAPSHOT, not timestamp
 952  0
                 break;
 953  
             }
 954  0
         }
 955  0
         if (version == null) {
 956  0
             throw new MojoExecutionException( "We could not find org-netbeans-bootstrap among the modules in the application. Launchers could not be found.");
 957  
         }
 958  0
         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  0
             artifactResolver.resolve( nbmArt, project.getRemoteArtifactRepositories(), localRepository );
 967  
         }
 968  
 
 969  0
         catch ( ArtifactResolutionException ex )
 970  
         {
 971  0
             throw new MojoExecutionException( "Failed to retrieve the nbm file from repository", ex );
 972  
         }
 973  0
         catch ( ArtifactNotFoundException ex )
 974  
         {
 975  0
             throw new MojoExecutionException( "Failed to retrieve the nbm file from repository", ex );
 976  0
         }
 977  0
         return nbmArt.getFile();
 978  
     }
 979  
 
 980  
     private void writeFromZip( final ZipFile zip, String zipPath, File destFile, boolean mandatory ) throws MojoExecutionException, IOException
 981  
     {
 982  0
         final ZipEntry path = zip.getEntry( zipPath );
 983  0
         if (path == null) {
 984  0
             if (mandatory) {
 985  0
                 throw new MojoExecutionException( zipPath + " not found in " + zip.getName());
 986  
             }
 987  0
             getLog().debug(zipPath + " is not present in " + zip.getName());
 988  0
             return;
 989  
         }
 990  0
         FileUtils.copyStreamToFile( new InputStreamFacade() {
 991  
             
 992  
             @Override
 993  
             public InputStream getInputStream() throws IOException
 994  
             {
 995  0
                 return zip.getInputStream( path );
 996  
             }
 997  
         }, destFile);
 998  0
     }
 999  
 
 1000  
     private static void addToMap( Map<String, Set<String>> map, String clusterName, List<String> newValues )
 1001  
     {
 1002  0
         Set<String> lst = map.get( clusterName );
 1003  0
         if ( lst == null )
 1004  
         {
 1005  0
             lst = new HashSet<String>();
 1006  0
             map.put( clusterName, lst );
 1007  
         }
 1008  0
         if ( newValues != null )
 1009  
         {
 1010  0
             lst.addAll( newValues );
 1011  
         }
 1012  0
     }
 1013  
     
 1014  
     private static List<String> findByDependencies( Map<String, Set<String>> clusterDependencies, String spec)
 1015  
     {
 1016  0
         List<String> toRet = new ArrayList<String>();
 1017  0
         for ( Map.Entry<String, Set<String>> entry : clusterDependencies.entrySet() )
 1018  
         {
 1019  0
             if ( entry.getValue().contains( spec ) )
 1020  
             {
 1021  0
                 toRet.add(entry.getKey());
 1022  
             }
 1023  0
         }
 1024  0
         return toRet;
 1025  
     }
 1026  
 
 1027  
     //the basic idea is that bundle's cluster can be determined by who depends on it.
 1028  
     //simplest case is when a module depends on it. If there are more, we need to pick one that is "lower in the stack, that's what cluster2depClusters is for.
 1029  
     //the rest needs to be determined in more sofisticated manner.
 1030  
     //start from bundles with known cluster and see what other bundles they depend on. stamp all these with the same cluster. do it recursively.
 1031  
     //At the end process the remaining bundles in reverse order. Check if *they* depend on a bundle with known cluster and so on..
 1032  
     //A few unsolved cases:
 1033  
     // - we never update the cluster information once a match was found, but there is a possibility that later in the processing the cluster could be "lowered".
 1034  
     // - 2 or more modules from unrelated clusters we cannot easily decide, most likely should be in common denominator cluster but our cluster2depClusters map is not transitive, only lists direct dependencies
 1035  
     static void assignClustersToBundles( List<BundleTuple> bundles, Set<String> wrappedBundleCNBs, Map<String, Set<String>> clusterDependencies, Map<String, Set<String>> cluster2depClusters, Log log)
 1036  
     {
 1037  0
         List<BundleTuple> toProcess = new ArrayList<BundleTuple>();
 1038  0
         List<BundleTuple> known = new ArrayList<BundleTuple>();
 1039  0
         for ( Iterator<BundleTuple> it = bundles.iterator(); it.hasNext(); )
 1040  
         {
 1041  0
             BundleTuple ent = it.next();
 1042  0
             Artifact art = ent.artifact;
 1043  0
             ExamineManifest ex = ent.manifest;
 1044  0
             String spec = ex.getModule();
 1045  0
             if ( wrappedBundleCNBs.contains( spec ) )
 1046  
             {
 1047  
                 // we already have this one as a wrapped module.
 1048  0
                 log.debug( "Not including bundle " + art.getDependencyConflictId()
 1049  
                                     + ". It is already included in a NetBeans module" );
 1050  0
                 it.remove();
 1051  0
                 continue;
 1052  
             }
 1053  0
             List<String> depclusters = findByDependencies(clusterDependencies, spec);
 1054  0
             if (depclusters.size() == 1) {
 1055  0
                 ent.cluster = depclusters.get( 0 );
 1056  0
                 known.add( ent );
 1057  0
             } else if (depclusters.isEmpty()) {
 1058  0
                 toProcess.add(ent);
 1059  
             } else {
 1060  
                 //more results.. from 2 dependent clusters pick the one that is lower in the stack.
 1061  0
                 for ( Iterator<String> it2 = depclusters.iterator(); it2.hasNext(); )
 1062  
                 {
 1063  0
                     String s = it2.next();
 1064  0
                     Set<String> depsCs = cluster2depClusters.get( s );
 1065  0
                     boolean removeS = false;
 1066  0
                     for (String sDep : depclusters) {
 1067  0
                         if (s.equals( sDep) ) {
 1068  0
                             continue;
 1069  
                         }
 1070  0
                         if (depsCs != null && depsCs.contains( sDep ) ) {
 1071  0
                             removeS = true;
 1072  
                         }
 1073  0
                     }
 1074  0
                     if (removeS) {
 1075  0
                         it2.remove();
 1076  
                     }
 1077  0
                 }
 1078  0
                 ent.cluster = depclusters.get( 0 ); //TODO still some free room there, what if they don't directly depend on each other but still are related
 1079  0
                 known.add (ent);
 1080  
             }
 1081  0
         }
 1082  0
         if (!toProcess.isEmpty())
 1083  
         {
 1084  0
             walkKnownBundleDependenciesDown(known, toProcess);
 1085  
         }
 1086  0
         if (!toProcess.isEmpty())
 1087  
         {
 1088  0
             walkKnownBundleDependenciesUp(known, toProcess);
 1089  
         }
 1090  0
     }
 1091  
 
 1092  
     private static void walkKnownBundleDependenciesDown( List<BundleTuple> known, List<BundleTuple> toProcess )
 1093  
     {
 1094  0
         boolean atLeastOneWasFound = false;
 1095  0
         for ( Iterator<BundleTuple> it = toProcess.iterator(); it.hasNext(); )
 1096  
         {
 1097  0
             BundleTuple bundleTuple = it.next();
 1098  0
             boolean found = false;
 1099  0
             for ( BundleTuple knownBT : known)
 1100  
             {
 1101  0
                 Sets.SetView<String> is = Sets.intersection(bundleTuple.manifest.getOsgiExports() , knownBT.manifest.getOsgiImports() );
 1102  0
                 if (!is.isEmpty()) {
 1103  0
                     found = true;
 1104  0
                     bundleTuple.cluster = knownBT.cluster;
 1105  0
                     break;
 1106  
                 }
 1107  
                 //dependencyTokens are requireBundle - matches the module property
 1108  0
                 is = Sets.intersection(Collections.singleton( bundleTuple.manifest.getModule()), new HashSet(knownBT.manifest.getDependencyTokens()) );
 1109  0
                 if (!is.isEmpty()) {
 1110  0
                     found = true;
 1111  0
                     bundleTuple.cluster = knownBT.cluster;
 1112  0
                     break;
 1113  
                 }
 1114  
                 
 1115  0
             }
 1116  0
             if (found) {
 1117  0
                 atLeastOneWasFound = true;
 1118  0
                 it.remove();
 1119  0
                 known.add(bundleTuple);
 1120  
             }
 1121  
             
 1122  0
         }
 1123  0
         if (!toProcess.isEmpty() && atLeastOneWasFound) {
 1124  0
             walkKnownBundleDependenciesDown( known, toProcess );
 1125  
         }
 1126  0
     }
 1127  
 
 1128  
     private static void walkKnownBundleDependenciesUp( List<BundleTuple> known, List<BundleTuple> toProcess )
 1129  
     {
 1130  0
         boolean atLeastOneWasFound = false;
 1131  0
         for ( Iterator<BundleTuple> it = toProcess.iterator(); it.hasNext(); )
 1132  
         {
 1133  0
             BundleTuple bundleTuple = it.next();
 1134  0
             boolean found = false;
 1135  0
             for ( BundleTuple knownBT : known)
 1136  
             {
 1137  0
                 Sets.SetView<String> is = Sets.intersection(bundleTuple.manifest.getOsgiImports() , knownBT.manifest.getOsgiExports() );
 1138  0
                 if (!is.isEmpty()) {
 1139  0
                     found = true;
 1140  0
                     bundleTuple.cluster = knownBT.cluster;
 1141  0
                     break;
 1142  
                 }
 1143  
                 //dependencyTokens are requireBundle - matches the module property
 1144  0
                 is = Sets.intersection(Collections.singleton( knownBT.manifest.getModule()), new HashSet(bundleTuple.manifest.getDependencyTokens()) );
 1145  0
                 if (!is.isEmpty()) {
 1146  0
                     found = true;
 1147  0
                     bundleTuple.cluster = knownBT.cluster;
 1148  0
                     break;
 1149  
                 }
 1150  
                 
 1151  0
             }
 1152  0
             if (found) {
 1153  0
                 atLeastOneWasFound = true;
 1154  0
                 it.remove();
 1155  0
                 known.add(bundleTuple);
 1156  
             }
 1157  
             
 1158  0
         }
 1159  0
         if (!toProcess.isEmpty() && atLeastOneWasFound) {
 1160  0
             walkKnownBundleDependenciesDown( known, toProcess );
 1161  
         }
 1162  0
         if (!toProcess.isEmpty() && atLeastOneWasFound) {
 1163  0
             walkKnownBundleDependenciesUp( known, toProcess );
 1164  
         }
 1165  0
     }
 1166  
 
 1167  
     //static and default for tests..
 1168  
     static Map<String, Set<String>> computeClusterOrdering( Map<String, Set<String>> clusterDependencies, Map<String, Set<String>> clusterModules )
 1169  
     {
 1170  0
         Map<String, Set<String>> cluster2depClusters = new HashMap<String, Set<String>>();
 1171  0
         for ( Map.Entry<String, Set<String>> entry : clusterDependencies.entrySet() )
 1172  
         {
 1173  0
             String cluster = entry.getKey();
 1174  0
             Set<String> deps = entry.getValue();
 1175  0
             for (Map.Entry<String, Set<String>> subEnt : clusterModules.entrySet()) {
 1176  0
                 if (subEnt.getKey().equals( cluster) ) {
 1177  0
                     continue;
 1178  
                 }
 1179  0
                 Sets.SetView<String> is = Sets.intersection(subEnt.getValue(), deps );
 1180  0
                 if (!is.isEmpty()) {
 1181  0
                     addToMap( cluster2depClusters, cluster, Collections.singletonList( subEnt.getKey() ) );
 1182  
                 }
 1183  0
             }
 1184  0
         }
 1185  0
         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  4
         {
 1195  4
             this.artifact = artifact;
 1196  4
             this.manifest = manifest;
 1197  4
         }
 1198  
         
 1199  
     }
 1200  
 
 1201  0
     private static class ClusterTuple
 1202  
     {
 1203  
         final File location;
 1204  
         final boolean newer;
 1205  
 
 1206  
         private ClusterTuple( File clusterFile, boolean newer )
 1207  0
         {
 1208  0
             location = clusterFile;
 1209  0
             this.newer = newer;
 1210  0
         }
 1211  
     }
 1212  
 
 1213  
     static String createBundleConfigFile( String cnb, boolean autoload)
 1214  
     {
 1215  0
         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  0
         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  0
         CRC32 crc = new CRC32();
 1246  0
         InputStream inFileStream = new FileInputStream( inFile );
 1247  
         try {
 1248  0
             byte[] array = new byte[(int) inFile.length()];
 1249  0
             int len = inFileStream.read( array );
 1250  0
             if ( len != array.length )
 1251  
             {
 1252  0
                 throw new IOException( "Cannot fully read " + inFile );
 1253  
             }
 1254  0
             crc.update( array );
 1255  
         }
 1256  
         finally
 1257  
         {
 1258  0
             inFileStream.close();
 1259  0
         }
 1260  
 
 1261  0
         return crc;
 1262  
     }
 1263  
 
 1264  
 }