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