View Javadoc

1   package org.apache.maven.index.creator;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0    
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.List;
27  
28  import org.apache.lucene.document.Document;
29  import org.apache.lucene.document.Field.Index;
30  import org.apache.lucene.document.Field.Store;
31  import org.apache.maven.index.ArtifactContext;
32  import org.apache.maven.index.ArtifactInfo;
33  import org.apache.maven.index.IndexerField;
34  import org.apache.maven.index.IndexerFieldVersion;
35  import org.apache.maven.index.MAVEN;
36  import org.apache.maven.index.context.IndexCreator;
37  import org.apache.maven.index.util.zip.ZipFacade;
38  import org.apache.maven.index.util.zip.ZipHandle;
39  import org.codehaus.plexus.component.annotations.Component;
40  import org.codehaus.plexus.util.StringUtils;
41  
42  /**
43   * An index creator used to index Java class names from a Maven artifact (JAR or WAR for now). Will open up the file and
44   * collect all the class names from it.
45   */
46  @Component( role = IndexCreator.class, hint = JarFileContentsIndexCreator.ID )
47  public class JarFileContentsIndexCreator
48      extends AbstractIndexCreator
49      implements LegacyDocumentUpdater
50  {
51      public static final String ID = "jarContent";
52  
53      public static final IndexerField FLD_CLASSNAMES = new IndexerField( MAVEN.CLASSNAMES, IndexerFieldVersion.V3,
54          "classnames", "Artifact Classes (tokenized)", Store.NO, Index.ANALYZED );
55  
56      /**
57       * NexusAnalyzer makes exception with this field only, to keep backward compatibility with old consumers of
58       * nexus-indexer. This field is here for "backward" compat only! The order is important too! FLD_CLASSNAMES must be
59       * registered BEFORE FLD_CLASSNAMES_KW!
60       */
61      public static final IndexerField FLD_CLASSNAMES_KW = new IndexerField( MAVEN.CLASSNAMES, IndexerFieldVersion.V1,
62          "c", "Artifact Classes (tokenized on newlines only)", Store.YES, Index.ANALYZED );
63  
64      public JarFileContentsIndexCreator()
65      {
66          super( ID );
67      }
68  
69      public void populateArtifactInfo( final ArtifactContext artifactContext )
70          throws IOException
71      {
72          ArtifactInfo ai = artifactContext.getArtifactInfo();
73  
74          File artifactFile = artifactContext.getArtifact();
75  
76          if ( artifactFile != null && artifactFile.isFile()
77              && ( artifactFile.getName().endsWith( ".jar" ) || artifactFile.getName().endsWith( ".war" ) ) )
78          {
79              updateArtifactInfo( ai, artifactFile );
80          }
81      }
82  
83      public void updateDocument( final ArtifactInfo ai, final Document doc )
84      {
85          if ( ai.classNames != null )
86          {
87              doc.add( FLD_CLASSNAMES_KW.toField( ai.classNames ) );
88              doc.add( FLD_CLASSNAMES.toField( ai.classNames ) );
89          }
90      }
91  
92      public void updateLegacyDocument( final ArtifactInfo ai, final Document doc )
93      {
94          if ( ai.classNames != null )
95          {
96              String classNames = ai.classNames;
97  
98              // downgrade the classNames if needed
99              if ( classNames.length() > 0 && classNames.charAt( 0 ) == '/' )
100             {
101                 // conversion from the new format
102                 String[] lines = classNames.split( "\\n" );
103                 StringBuilder sb = new StringBuilder();
104                 for ( String line : lines )
105                 {
106                     sb.append( line.substring( 1 ) ).append( '\n' );
107                 }
108 
109                 classNames = sb.toString();
110             }
111 
112             doc.add( FLD_CLASSNAMES_KW.toField( classNames ) );
113         }
114     }
115 
116     public boolean updateArtifactInfo( final Document doc, final ArtifactInfo artifactInfo )
117     {
118         String names = doc.get( FLD_CLASSNAMES_KW.getKey() );
119 
120         if ( names != null )
121         {
122             if ( names.length() == 0 || names.charAt( 0 ) == '/' )
123             {
124                 artifactInfo.classNames = names;
125             }
126             else
127             {
128                 // conversion from the old format
129                 String[] lines = names.split( "\\n" );
130                 StringBuilder sb = new StringBuilder();
131                 for ( String line : lines )
132                 {
133                     sb.append( '/' ).append( line ).append( '\n' );
134                 }
135                 artifactInfo.classNames = sb.toString();
136             }
137 
138             return true;
139         }
140 
141         return false;
142     }
143 
144     private void updateArtifactInfo( final ArtifactInfo ai, final File f )
145         throws IOException
146     {
147         if ( f.getName().endsWith( ".jar" ) )
148         {
149             updateArtifactInfo( ai, f, null );
150         }
151         else if ( f.getName().endsWith( ".war" ) )
152         {
153             updateArtifactInfo( ai, f, "WEB-INF/classes/" );
154         }
155     }
156 
157     private void updateArtifactInfo( final ArtifactInfo ai, final File f, final String strippedPrefix )
158         throws IOException
159     {
160         ZipHandle handle = null;
161 
162         try
163         {
164             handle = ZipFacade.getZipHandle( f );
165 
166             final List<String> entries = handle.getEntries();
167 
168             final StringBuilder sb = new StringBuilder();
169 
170             for ( String name : entries )
171             {
172                 if ( name.endsWith( ".class" ) )
173                 {
174                     // TODO verify if class is public or protected
175                     // TODO skip all inner classes for now
176 
177                     int i = name.indexOf( "$" );
178 
179                     if ( i == -1 )
180                     {
181                         if ( name.charAt( 0 ) != '/' )
182                         {
183                             sb.append( '/' );
184                         }
185 
186                         if ( StringUtils.isBlank( strippedPrefix ) )
187                         {
188                             // class name without ".class"
189                             sb.append( name.substring( 0, name.length() - 6 ) ).append( '\n' );
190                         }
191                         else if ( name.startsWith( strippedPrefix ) && (name.length() > ( strippedPrefix.length() + 6 )) )
192                         {
193                             // class name without ".class" and stripped prefix
194                             sb.append( name.substring( strippedPrefix.length(), name.length() - 6 ) ).append( '\n' );
195                         }
196                     }
197                 }
198             }
199 
200             final String fieldValue = sb.toString().trim();
201 
202             if ( fieldValue.length() != 0 )
203             {
204                 ai.classNames = fieldValue;
205             }
206             else
207             {
208                 ai.classNames = null;
209             }
210         }
211         finally
212         {
213             try
214             {
215                 ZipFacade.close( handle );
216             }
217             catch ( Exception e )
218             {
219                 getLogger().error( "Could not close jar file properly.", e );
220             }
221         }
222     }
223 
224     @Override
225     public String toString()
226     {
227         return ID;
228     }
229 
230     public Collection<IndexerField> getIndexerFields()
231     {
232         return Arrays.asList( FLD_CLASSNAMES, FLD_CLASSNAMES_KW );
233     }
234 }