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 javax.inject.Named;
23  import javax.inject.Singleton;
24  import java.io.File;
25  import java.io.IOException;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Enumeration;
29  import java.util.zip.ZipEntry;
30  import java.util.zip.ZipFile;
31  
32  import org.apache.lucene.document.Document;
33  import org.apache.maven.index.ArtifactContext;
34  import org.apache.maven.index.ArtifactInfo;
35  import org.apache.maven.index.IndexerField;
36  import org.apache.maven.index.IndexerFieldVersion;
37  import org.apache.maven.index.MAVEN;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  /**
41   * An index creator used to index Java class names from a Maven artifact (JAR, ZIP or WAR for now).
42   * Will open up the file and collect all the class names from it.
43   */
44  @Singleton
45  @Named( JarFileContentsIndexCreator.ID )
46  public class JarFileContentsIndexCreator
47      extends AbstractIndexCreator
48      implements LegacyDocumentUpdater
49  {
50      public static final String ID = "jarContent";
51  
52      public static final IndexerField FLD_CLASSNAMES = new IndexerField( MAVEN.CLASSNAMES, IndexerFieldVersion.V3,
53          "classnames", "Artifact Classes (tokenized)", IndexerField.ANALYZED_NOT_STORED );
54  
55      /**
56       * NexusAnalyzer makes exception with this field only, to keep backward compatibility with old consumers of
57       * nexus-indexer. This field is here for "backward" compat only! The order is important too! FLD_CLASSNAMES must be
58       * registered BEFORE FLD_CLASSNAMES_KW!
59       */
60      public static final IndexerField FLD_CLASSNAMES_KW = new IndexerField( MAVEN.CLASSNAMES, IndexerFieldVersion.V1,
61          "c", "Artifact Classes (tokenized on newlines only)", IndexerField.ANALYZED_STORED );
62  
63      public JarFileContentsIndexCreator()
64      {
65          super( ID );
66      }
67  
68      public void populateArtifactInfo( final ArtifactContext artifactContext )
69          throws IOException
70      {
71          ArtifactInfo ai = artifactContext.getArtifactInfo();
72  
73          File artifactFile = artifactContext.getArtifact();
74  
75          if ( artifactFile != null && artifactFile.isFile()
76              && ( artifactFile.getName().endsWith( ".jar" )
77                   || artifactFile.getName().endsWith( ".war" )
78                   || artifactFile.getName().endsWith( ".zip" ) ) )
79          {
80              updateArtifactInfo( ai, artifactFile );
81          }
82      }
83  
84      public void updateDocument( final ArtifactInfo ai, final Document doc )
85      {
86          if ( ai.getClassNames() != null )
87          {
88              doc.add( FLD_CLASSNAMES_KW.toField( ai.getClassNames() ) );
89              doc.add( FLD_CLASSNAMES.toField( ai.getClassNames() ) );
90          }
91      }
92  
93      public void updateLegacyDocument( final ArtifactInfo ai, final Document doc )
94      {
95          if ( ai.getClassNames() != null )
96          {
97              String classNames = ai.getClassNames();
98  
99              // downgrade the classNames if needed
100             if ( classNames.length() > 0 && classNames.charAt( 0 ) == '/' )
101             {
102                 // conversion from the new format
103                 String[] lines = classNames.split( "\\n" );
104                 StringBuilder sb = new StringBuilder();
105                 for ( String line : lines )
106                 {
107                     sb.append( line.substring( 1 ) ).append( '\n' );
108                 }
109 
110                 classNames = sb.toString();
111             }
112 
113             doc.add( FLD_CLASSNAMES_KW.toField( classNames ) );
114         }
115     }
116 
117     public boolean updateArtifactInfo( final Document doc, final ArtifactInfo artifactInfo )
118     {
119         String names = doc.get( FLD_CLASSNAMES_KW.getKey() );
120 
121         if ( names != null )
122         {
123             if ( names.length() == 0 || names.charAt( 0 ) == '/' )
124             {
125                 artifactInfo.setClassNames( names );
126             }
127             else
128             {
129                 // conversion from the old format
130                 String[] lines = names.split( "\\n" );
131                 StringBuilder sb = new StringBuilder();
132                 for ( String line : lines )
133                 {
134                     sb.append( '/' ).append( line ).append( '\n' );
135                 }
136                 artifactInfo.setClassNames( sb.toString() );
137             }
138 
139             return true;
140         }
141 
142         return false;
143     }
144 
145     private void updateArtifactInfo( final ArtifactInfo ai, final File f )
146         throws IOException
147     {
148         if ( f.getName().endsWith( ".jar" ) || f.getName().endsWith( ".zip" ) )
149         {
150             updateArtifactInfo( ai, f, null );
151         }
152         else if ( f.getName().endsWith( ".war" ) )
153         {
154             updateArtifactInfo( ai, f, "WEB-INF/classes/" );
155         }
156     }
157 
158     private void updateArtifactInfo( final ArtifactInfo ai, final File f, final String strippedPrefix )
159         throws IOException
160     {
161 
162         try ( ZipFile zipFile = new ZipFile( f ) )
163         {
164             final Enumeration<? extends ZipEntry> entries = zipFile.entries();
165 
166             final StringBuilder sb = new StringBuilder();
167 
168             while ( entries.hasMoreElements() )
169             {
170                 String name = entries.nextElement().getName();
171                 if ( name.endsWith( ".class" ) )
172                 {
173                     // TODO verify if class is public or protected
174                     // TODO skip all inner classes for now
175 
176                     int i = name.indexOf( "$" );
177 
178                     if ( i == -1 )
179                     {
180                         if ( name.charAt( 0 ) != '/' )
181                         {
182                             sb.append( '/' );
183                         }
184 
185                         if ( StringUtils.isBlank( strippedPrefix ) )
186                         {
187                             // class name without ".class"
188                             sb.append( name, 0, name.length() - 6 ).append( '\n' );
189                         }
190                         else if ( name.startsWith( strippedPrefix )
191                             && ( name.length() > ( strippedPrefix.length() + 6 ) ) )
192                         {
193                             // class name without ".class" and stripped prefix
194                             sb.append( name, 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.setClassNames( fieldValue );
205             }
206             else
207             {
208                 ai.setClassNames( null );
209             }
210         }
211     }
212 
213     @Override
214     public String toString()
215     {
216         return ID;
217     }
218 
219     @Override
220     public Collection<IndexerField> getIndexerFields()
221     {
222         return Arrays.asList( FLD_CLASSNAMES, FLD_CLASSNAMES_KW );
223     }
224 }