View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to you under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
9    * or agreed to in writing, software distributed under the License is
10   * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11   * KIND, either express or implied. See the License for the specific language
12   * governing permissions and limitations under the License.
13   */
14  package org.apache.maven.plugin.eclipse.reader;
15  
16  import java.io.DataInputStream;
17  import java.io.File;
18  import java.io.FileInputStream;
19  import java.io.FileNotFoundException;
20  import java.io.FileReader;
21  import java.io.IOException;
22  import java.io.StringReader;
23  import java.net.URI;
24  import java.net.URISyntaxException;
25  import java.text.MessageFormat;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.Set;
32  import java.util.jar.JarFile;
33  
34  import org.apache.maven.plugin.eclipse.Messages;
35  import org.apache.maven.plugin.eclipse.WorkspaceConfiguration;
36  import org.apache.maven.plugin.ide.IdeDependency;
37  import org.apache.maven.plugin.ide.IdeUtils;
38  import org.apache.maven.plugin.logging.Log;
39  import org.apache.maven.project.MavenProject;
40  import org.codehaus.plexus.util.IOUtil;
41  import org.codehaus.plexus.util.xml.Xpp3Dom;
42  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
43  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
44  import org.eclipse.core.internal.localstore.SafeChunkyInputStream;
45  
46  /**
47   * Scan the eclipse workspace and create a array with {@link IdeDependency} for all found artefacts.
48   * 
49   * @author Richard van Nieuwenhoven
50   * @version $Id: ReadWorkspaceLocations.java 728546 2008-12-21 22:56:51Z bentmann $
51   */
52  public class ReadWorkspaceLocations
53  {
54  
55      public static final String BINARY_LOCATION_FILE = ".location";
56  
57      public static final String METADATA_PLUGINS_ORG_ECLIPSE_CORE_RESOURCES_PROJECTS =
58          ".metadata/.plugins/org.eclipse.core.resources/.projects";
59  
60      private static final String[] PARENT_VERSION = new String[] { "parent", "version" };
61  
62      private static final String[] PARENT_GROUP_ID = new String[] { "parent", "groupId" };
63  
64      private static final String[] PACKAGING = new String[] { "packaging" };
65  
66      private static final String[] VERSION = new String[] { "version" };
67  
68      private static final String[] GROUP_ID = new String[] { "groupId" };
69  
70      private static final String[] ARTEFACT_ID = new String[] { "artifactId" };
71  
72      private static final String METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_LAUNCHING_PREFS =
73          ".metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs";
74  
75      private static final String METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_PREFS_VM_KEY =
76          "org.eclipse.jdt.launching.PREF_VM_XML";
77  
78      private static final String METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_SERVER_PREFS =
79          ".metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.server.core.prefs";
80  
81      private static final String METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_PREFS_RUNTIMES_KEY = "runtimes";
82  
83      private static final String CLASSPATHENTRY_DEFAULT = "org.eclipse.jdt.launching.JRE_CONTAINER";
84  
85      private static final String CLASSPATHENTRY_STANDARD =
86          CLASSPATHENTRY_DEFAULT + "/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/";
87  
88      private static final String CLASSPATHENTRY_FORMAT = ReadWorkspaceLocations.CLASSPATHENTRY_DEFAULT + "/{0}/{1}";
89  
90      public void init( Log log, WorkspaceConfiguration workspaceConfiguration, MavenProject project,
91                        String wtpDefaultServer )
92      {
93          detectDefaultJREContainer( workspaceConfiguration, project, log );
94          readWorkspace( workspaceConfiguration, log );
95          detectWTPDefaultServer( workspaceConfiguration, wtpDefaultServer, log );
96      }
97  
98      /**
99       * Detect WTP Default Server. Do nothing if tehre are no defined servers in the settings.
100      * 
101      * @param workspaceConfiguration
102      * @param wtpDefaultServer
103      * @param log
104      */
105     private void detectWTPDefaultServer( WorkspaceConfiguration workspaceConfiguration, String wtpDefaultServer, Log log )
106     {
107         HashMap servers = readDefinedServers( workspaceConfiguration, log );
108         if ( servers == null || servers.isEmpty() )
109         {
110             return;
111         }
112         if ( wtpDefaultServer != null )
113         {
114             Set ids = servers.keySet();
115             // first we try the exact match
116             Iterator idIterator = ids.iterator();
117             while ( workspaceConfiguration.getDefaultDeployServerId() == null && idIterator.hasNext() )
118             {
119                 String id = (String) idIterator.next();
120                 String name = (String) servers.get( id );
121                 if ( wtpDefaultServer.equals( id ) || wtpDefaultServer.equals( name ) )
122                 {
123                     workspaceConfiguration.setDefaultDeployServerId( id );
124                     workspaceConfiguration.setDefaultDeployServerName( name );
125                 }
126             }
127             if ( workspaceConfiguration.getDefaultDeployServerId() == null )
128             {
129                 log.info( "no exact wtp server match." );
130                 // now we will try the substring match
131                 idIterator = ids.iterator();
132                 while ( workspaceConfiguration.getDefaultDeployServerId() == null && idIterator.hasNext() )
133                 {
134                     String id = (String) idIterator.next();
135                     String name = (String) servers.get( id );
136                     if ( id.indexOf( wtpDefaultServer ) >= 0 || name.indexOf( wtpDefaultServer ) >= 0 )
137                     {
138                         workspaceConfiguration.setDefaultDeployServerId( id );
139                         workspaceConfiguration.setDefaultDeployServerName( name );
140                     }
141                 }
142             }
143         }
144         if ( workspaceConfiguration.getDefaultDeployServerId() == null && servers.size() > 0 )
145         {
146             // now take the default server
147             log.info( "no substring wtp server match." );
148             workspaceConfiguration.setDefaultDeployServerId( (String) servers.get( "" ) );
149             workspaceConfiguration.setDefaultDeployServerName( (String) servers.get( workspaceConfiguration.getDefaultDeployServerId() ) );
150         }
151         log.info( "Using as WTP server : " + workspaceConfiguration.getDefaultDeployServerName() );
152     }
153 
154     /**
155      * Take the compiler executable and try to find a JRE that contains that compiler.
156      * 
157      * @param rawExecutable the executable with the complete path.
158      * @param jreMap the map with defined JRE's.
159      * @param logger the logger to log the error's
160      * @return the found container or null if non found.
161      */
162     private String getContainerFromExecutable( String rawExecutable, Map jreMap, Log logger )
163     {
164         String foundContainer = null;
165         if ( rawExecutable != null )
166         {
167             String executable;
168             try
169             {
170                 executable = new File( rawExecutable ).getCanonicalPath();
171                 logger.debug( "detected executable: " + executable );
172             }
173             catch ( Exception e )
174             {
175                 return null;
176             }
177             File executableFile = new File( executable );
178             while ( executableFile != null )
179             {
180                 foundContainer = (String) jreMap.get( executableFile.getPath() );
181                 if ( foundContainer != null )
182                 {
183                     logger.debug( "detected classpathContainer from executable: " + foundContainer );
184                     return foundContainer;
185 
186                 }
187                 executableFile = executableFile.getParentFile();
188             }
189         }
190         return null;
191     }
192 
193     /**
194      * Search the default JREContainer from eclipse for the current MavenProject
195      * 
196      * @param workspaceLocation the location of the workspace.
197      * @param project the maven project the get the configuration
198      * @param logger the logger for errors
199      */
200     private void detectDefaultJREContainer( WorkspaceConfiguration workspaceConfiguration, MavenProject project,
201                                             Log logger )
202     {
203         String defaultJREContainer = ReadWorkspaceLocations.CLASSPATHENTRY_DEFAULT;
204         if ( workspaceConfiguration.getWorkspaceDirectory() != null )
205         {
206             Map jreMap = readAvailableJREs( workspaceConfiguration.getWorkspaceDirectory(), logger );
207             if ( jreMap != null )
208             {
209                 String foundContainer =
210                     getContainerFromExecutable( System.getProperty( "maven.compiler.executable" ), jreMap, logger );
211                 if ( foundContainer == null )
212                 {
213                     foundContainer =
214                         getContainerFromExecutable( IdeUtils.getCompilerPluginSetting( project, "executable" ), jreMap,
215                                                     logger );
216                 }
217                 if ( foundContainer == null )
218                 {
219                     String sourceVersion = IdeUtils.getCompilerSourceVersion( project );
220                     foundContainer = (String) jreMap.get( sourceVersion );
221                     if ( foundContainer != null )
222                     {
223                         logger.debug( "detected classpathContainer from sourceVersion(" + sourceVersion + "): "
224                             + foundContainer );
225                     }
226                 }
227                 if ( foundContainer == null )
228                 {
229                     foundContainer = getContainerFromExecutable( System.getProperty( "java.home" ), jreMap, logger );
230                 }
231                 if ( foundContainer != null )
232                 {
233                     defaultJREContainer = foundContainer;
234                 }
235 
236             }
237         }
238         workspaceConfiguration.setDefaultClasspathContainer( defaultJREContainer );
239     }
240 
241     /**
242      * Get the project location for a project in the eclipse metadata.
243      * 
244      * @param workspaceLocation the location of the workspace
245      * @param project the project subdirectory in the metadata
246      * @return the full path to the project.
247      * @throws IOException failures to read location file
248      * @throws URISyntaxException failures to read location file
249      */
250     /* package */File getProjectLocation( File workspaceLocation, File project )
251         throws IOException, URISyntaxException
252     {
253         File location = new File( project, ReadWorkspaceLocations.BINARY_LOCATION_FILE );
254         if ( location.exists() )
255         {
256             SafeChunkyInputStream fileInputStream = null;
257             try
258             {
259                 fileInputStream = new SafeChunkyInputStream( location );
260                 DataInputStream dataInputStream = new DataInputStream( fileInputStream );
261                 String file = dataInputStream.readUTF().trim();
262 
263                 if ( file.length() > 0 )
264                 {
265                     if ( !file.startsWith( "URI//" ) )
266                     {
267                         throw new IOException( location.getAbsolutePath() + " contains unexpected data: " + file );
268                     }
269                     file = file.substring( "URI//".length() );
270                     return new File( new URI( file ) );
271                 }
272             }
273             finally
274             {
275                 IOUtil.close( fileInputStream );
276             }
277         }
278         File projectBase = new File( workspaceLocation, project.getName() );
279         if ( projectBase.isDirectory() )
280         {
281             return projectBase;
282         }
283 
284         return null;
285     }
286 
287     /**
288      * get a value from a dom element.
289      * 
290      * @param element the element to get a value from
291      * @param elementNames the sub elements to get
292      * @param defaultValue teh default value if the value was null or empty
293      * @return the value of the dome element.
294      */
295     private String getValue( Xpp3Dom element, String[] elementNames, String defaultValue )
296     {
297         String value = null;
298         Xpp3Dom dom = element;
299         for ( int index = 0; dom != null && index < elementNames.length; index++ )
300         {
301             dom = dom.getChild( elementNames[index] );
302         }
303         if ( dom != null )
304         {
305             value = dom.getValue();
306         }
307         if ( value == null || value.trim().length() == 0 )
308         {
309             return defaultValue;
310         }
311         else
312         {
313             return value;
314         }
315     }
316 
317     /**
318      * Read the artefact information from the pom in the project location and the eclipse project name from the .project
319      * file.
320      * 
321      * @param projectLocation the location of the project
322      * @param logger the logger to report errors and debug info.
323      * @return an {@link IdeDependency} or null.
324      * @throws FileNotFoundException
325      * @throws XmlPullParserException
326      * @throws IOException
327      */
328     private IdeDependency readArtefact( File projectLocation, Log logger )
329         throws FileNotFoundException, XmlPullParserException, IOException
330     {
331         File projectFile = new File( projectLocation, ".project" );
332         String eclipseProjectName = projectLocation.getName();
333         if ( projectFile.exists() )
334         {
335             Xpp3Dom project = Xpp3DomBuilder.build( new FileReader( projectFile ) );
336             eclipseProjectName = getValue( project, new String[] { "name" }, eclipseProjectName );
337         }
338         File pomFile = new File( projectLocation, "pom.xml" );
339         if ( pomFile.exists() )
340         {
341             Xpp3Dom pom = Xpp3DomBuilder.build( new FileReader( pomFile ) );
342 
343             String artifact = getValue( pom, ReadWorkspaceLocations.ARTEFACT_ID, null );
344             String group =
345                 getValue( pom, ReadWorkspaceLocations.GROUP_ID, getValue( pom, ReadWorkspaceLocations.PARENT_GROUP_ID,
346                                                                           null ) );
347             String version =
348                 getValue( pom, ReadWorkspaceLocations.VERSION, getValue( pom, ReadWorkspaceLocations.PARENT_VERSION,
349                                                                          null ) );
350             String packaging = getValue( pom, ReadWorkspaceLocations.PACKAGING, "jar" );
351 
352             logger.debug( "found workspace artefact " + group + ":" + artifact + ":" + version + " " + packaging + " ("
353                 + eclipseProjectName + ")" + " -> " + projectLocation.getAbsolutePath() );
354             return new IdeDependency( group, artifact, version, packaging, true, false, false, false, false, null,
355                                       packaging, false, null, 0, eclipseProjectName );
356         }
357         else
358         {
359             logger.debug( "ignored workspace project NO pom available " + projectLocation.getAbsolutePath() );
360             return null;
361         }
362     }
363 
364     /* package */ HashMap readDefinedServers( WorkspaceConfiguration workspaceConfiguration, Log logger )
365     {
366         HashMap detectedRuntimes = new HashMap();
367         if ( workspaceConfiguration.getWorkspaceDirectory() != null )
368         {
369             Xpp3Dom runtimesElement = null;
370             try
371             {
372                 File prefs =
373                     new File( workspaceConfiguration.getWorkspaceDirectory(),
374                               ReadWorkspaceLocations.METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_SERVER_PREFS );
375                 if ( prefs.exists() )
376                 {
377                     Properties properties = new Properties();
378                     properties.load( new FileInputStream( prefs ) );
379                     String runtimes = properties.getProperty( ReadWorkspaceLocations.METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_PREFS_RUNTIMES_KEY );
380                     if ( runtimes != null )
381                     {
382                         runtimesElement = Xpp3DomBuilder.build( new StringReader( runtimes ) );
383                     }
384                 }
385             }
386             catch ( Exception e )
387             {
388                 logger.error( "Could not read workspace wtp server runtimes preferences : " + e.getMessage() );
389             }
390 
391             if ( runtimesElement != null )
392             {
393                 Xpp3Dom[] runtimeArray = runtimesElement.getChildren( "runtime" );
394                 for ( int index = 0; runtimeArray != null && index < runtimeArray.length; index++ )
395                 {
396                     String id = runtimeArray[index].getAttribute( "id" );
397                     String name = runtimeArray[index].getAttribute( "name" );
398                     if ( detectedRuntimes.isEmpty() )
399                     {
400                         logger.debug( "Using WTP runtime with id: \"" + id + "\" as default runtime" );
401                         detectedRuntimes.put( "", id );
402                     }
403                     detectedRuntimes.put( id, name );
404                     logger.debug( "Detected WTP runtime with id: \"" + id + "\" and name: \"" + name + "\"" );
405                 }
406             }
407         }
408         return detectedRuntimes;
409     }
410 
411     /**
412      * Read the JRE definition configured in the workspace. They will be put in a HashMap with as key there path and as
413      * value the JRE constant. a second key is included with the JRE version as a key.
414      * 
415      * @param workspaceLocation the workspace location
416      * @param logger the logger to error messages
417      * @return the map with found jre's
418      */
419     private HashMap readAvailableJREs( File workspaceLocation, Log logger )
420     {
421         Xpp3Dom vms;
422         try
423         {
424             File prefs =
425                 new File( workspaceLocation,
426                           ReadWorkspaceLocations.METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_LAUNCHING_PREFS );
427             if ( !prefs.exists() )
428             {
429                 return null;
430             }
431             Properties properties = new Properties();
432             properties.load( new FileInputStream( prefs ) );
433             vms =
434                 Xpp3DomBuilder.build( new StringReader(
435                                                         properties.getProperty( ReadWorkspaceLocations.METADATA_PLUGINS_ORG_ECLIPSE_CORE_RUNTIME_PREFS_VM_KEY ) ) );
436         }
437         catch ( Exception e )
438         {
439             logger.error( "Could not read workspace JRE preferences", e );
440             return null;
441         }
442 
443         HashMap jreMap = new HashMap();
444         jreMap.put( "1.2", CLASSPATHENTRY_STANDARD + "J2SE-1.2" );
445         jreMap.put( "1.3", CLASSPATHENTRY_STANDARD + "J2SE-1.3" );
446         jreMap.put( "1.4", CLASSPATHENTRY_STANDARD + "J2SE-1.4" );
447         jreMap.put( "1.5", CLASSPATHENTRY_STANDARD + "J2SE-1.5" );
448         jreMap.put( "5", jreMap.get( "1.5" ) );
449         jreMap.put( "1.6", CLASSPATHENTRY_STANDARD + "JavaSE-1.6" );
450         jreMap.put( "6", jreMap.get( "1.6" ) );
451         String defaultJRE = vms.getAttribute( "defaultVM" ).trim();
452         Xpp3Dom[] vmTypes = vms.getChildren( "vmType" );
453         for ( int vmTypeIndex = 0; vmTypeIndex < vmTypes.length; vmTypeIndex++ )
454         {
455             String typeId = vmTypes[vmTypeIndex].getAttribute( "id" );
456             Xpp3Dom[] vm = vmTypes[vmTypeIndex].getChildren( "vm" );
457             for ( int vmIndex = 0; vmIndex < vm.length; vmIndex++ )
458             {
459                 try
460                 {
461                     String path = vm[vmIndex].getAttribute( "path" );
462                     String name = vm[vmIndex].getAttribute( "name" );
463                     String vmId = vm[vmIndex].getAttribute( "id" ).trim();
464                     String classpathEntry =
465                         MessageFormat.format( ReadWorkspaceLocations.CLASSPATHENTRY_FORMAT,
466                                               new Object[] { typeId, name } );
467                     String jrePath = new File( path ).getCanonicalPath();
468                     File rtJarFile = new File( new File( jrePath ), "jre/lib/rt.jar" );
469                     if ( !rtJarFile.exists() ) {
470                         logger.warn( Messages.getString( "EclipsePlugin.invalidvminworkspace", jrePath ) );
471                         continue;
472                     }
473                     JarFile rtJar = new JarFile( rtJarFile );
474                     String version = rtJar.getManifest().getMainAttributes().getValue( "Specification-Version" );
475                     if ( defaultJRE.endsWith( "," + vmId ) )
476                     {
477                         jreMap.put( jrePath, ReadWorkspaceLocations.CLASSPATHENTRY_DEFAULT );
478                         jreMap.put( version, ReadWorkspaceLocations.CLASSPATHENTRY_DEFAULT );
479                         logger.debug( "Default Classpath Container version: " + version + "  location: " + jrePath );
480                     }
481                     else if ( !jreMap.containsKey( jrePath ) )
482                     {
483                         if ( !jreMap.containsKey( version ) )
484                         {
485                             jreMap.put( version, classpathEntry );
486                         }
487                         jreMap.put( jrePath, classpathEntry );
488                         logger.debug( "Additional Classpath Container version: " + version + " " + classpathEntry
489                             + " location: " + jrePath );
490                     }
491                     else
492                     {
493                         logger.debug( "Ignored (duplicated) additional Classpath Container version: " + version + " "
494                             + classpathEntry + " location: " + jrePath );
495                     }
496                 }
497                 catch ( IOException e )
498                 {
499                     logger.warn( "Could not interpret entry: " + vm[vmIndex].toString() );
500                 }
501             }
502         }
503         return jreMap;
504     }
505 
506     /**
507      * Scan the eclipse workspace and create a array with {@link IdeDependency} for all found artifacts.
508      * 
509      * @param workspaceLocation the location of the eclipse workspace.
510      * @param logger the logger to report errors and debug info.
511      */
512     private void readWorkspace( WorkspaceConfiguration workspaceConfiguration, Log logger )
513     {
514         ArrayList dependencies = new ArrayList();
515         if ( workspaceConfiguration.getWorkspaceDirectory() != null )
516         {
517             File workspace =
518                 new File( workspaceConfiguration.getWorkspaceDirectory(),
519                           ReadWorkspaceLocations.METADATA_PLUGINS_ORG_ECLIPSE_CORE_RESOURCES_PROJECTS );
520 
521             File[] directories = workspace.listFiles();
522             for ( int index = 0; directories != null && index < directories.length; index++ )
523             {
524                 File project = directories[index];
525                 if ( project.isDirectory() )
526                 {
527                     try
528                     {
529                         File projectLocation =
530                             getProjectLocation( workspaceConfiguration.getWorkspaceDirectory(), project );
531                         if ( projectLocation != null )
532                         {
533                             IdeDependency ideDependency = readArtefact( projectLocation, logger );
534                             if ( ideDependency != null )
535                             {
536                                 dependencies.add( ideDependency );
537                             }
538                         }
539                     }
540                     catch ( Exception e )
541                     {
542                         logger.warn( "could not read workspace project:" + project, e );
543                     }
544                 }
545             }
546         }
547         workspaceConfiguration.setWorkspaceArtefacts( (IdeDependency[]) dependencies.toArray( new IdeDependency[dependencies.size()] ) );
548     }
549 }