14
14
*/
15
15
16
16
import java .io .File ;
17
- import java .io .FileInputStream ;
18
17
import java .io .FileOutputStream ;
19
18
import java .io .IOException ;
20
19
import java .io .InputStream ;
21
- import java .net . HttpURLConnection ;
22
- import java .net .URL ;
20
+ import java .io . OutputStream ;
21
+ import java .net .URI ;
23
22
import java .net .URLEncoder ;
23
+ import java .net .http .HttpClient ;
24
+ import java .net .http .HttpRequest ;
25
+ import java .net .http .HttpResponse ;
24
26
import java .nio .charset .StandardCharsets ;
27
+ import java .nio .file .Files ;
28
+ import java .nio .file .Path ;
25
29
import java .util .Base64 ;
26
30
import java .util .Date ;
27
31
import java .util .EnumSet ;
31
35
import java .util .jar .JarEntry ;
32
36
import java .util .jar .JarOutputStream ;
33
37
import java .util .jar .Manifest ;
38
+ import java .util .stream .Collectors ;
39
+ import java .util .stream .StreamSupport ;
34
40
35
- import org .apache .commons .io .FilenameUtils ;
36
- import org .apache .commons .io .IOUtils ;
37
- import org .apache .commons .lang .StringUtils ;
38
41
import org .apache .felix .utils .json .JSONParser ;
39
42
import org .apache .maven .plugin .AbstractMojo ;
40
43
import org .apache .maven .plugin .MojoExecutionException ;
@@ -64,7 +67,7 @@ public class DownloadContentClassificationMojo extends AbstractMojo {
64
67
* the base URL where AEM is deployed
65
68
*/
66
69
@ Parameter (property ="baseUrl" , defaultValue ="http://localhost:4502" )
67
- URL baseUrl ;
70
+ URI baseUrl ;
68
71
69
72
/**
70
73
* the user name to access the {@link baseUrl}
@@ -84,12 +87,13 @@ public class DownloadContentClassificationMojo extends AbstractMojo {
84
87
*/
85
88
@ Parameter (property ="relativeFileNameInJar" , required = false )
86
89
File relativeFileNameInJar ;
87
-
90
+
88
91
@ Override
89
92
public void execute () throws MojoExecutionException , MojoFailureException {
90
93
Log log = getLog ();
94
+ HttpClient httpClient = HttpClient .newHttpClient ();
91
95
try {
92
- String aemVersion = getAemVersion ();
96
+ String aemVersion = getAemVersion (httpClient );
93
97
94
98
log .warn ("Make sure that the relevant search index definitions are deployed on AEM at " + baseUrl + ". Otherwise this goal will fail!" );
95
99
log .info ("Start retrieving the classification and deprecation data from " + baseUrl );
@@ -102,31 +106,34 @@ public void execute() throws MojoExecutionException, MojoFailureException {
102
106
if (classification .isLabelMixin () == false ) {
103
107
continue ;
104
108
}
105
- retrieveClassificationForMixin (classification , map );
109
+ retrieveClassificationForMixin (httpClient , classification , map );
106
110
}
107
111
108
112
// 2. update map with deprecation entries
109
- retrieveDeprecatedResourceTypes (map );
113
+ retrieveDeprecatedResourceTypes (httpClient , map );
110
114
111
115
// 3. persist the map
112
- File outputFile = File .createTempFile ("contentclassification" , ".map" );
113
- try (FileOutputStream fileOutputStream = new FileOutputStream (outputFile )) {
116
+ Path outputFile = Files .createTempFile ("contentclassification" , ".map" );
117
+ try (OutputStream fileOutputStream = Files . newOutputStream (outputFile )) {
114
118
map .write (fileOutputStream );
115
119
}
116
120
log .info ("Written classification map to " + outputFile + " containing " + map .size () + " entries." );
117
121
118
122
// 4. optionally wrap in a JAR
119
123
if (relativeFileNameInJar != null ) {
120
- File jarFile = createJarWrapper (outputFile , relativeFileNameInJar );
124
+ File jarFile = createJarWrapper (outputFile , relativeFileNameInJar . toPath () );
121
125
log .info ("Written wrapper jar to " + jarFile );
122
126
}
127
+ } catch (InterruptedException e ) {
128
+ Thread .currentThread ().interrupt ();
129
+ throw new MojoFailureException ("Could not retrieve classification metadata: " + e .getMessage (), e );
123
130
} catch (IOException |IllegalStateException e ) {
124
- throw new MojoFailureException ("Could not generate classification JAR: " + e .getMessage (), e );
131
+ throw new MojoFailureException ("Could not retrieve classification metadata: " + e .getMessage (), e );
125
132
}
126
133
}
127
134
128
- String getAemVersion () throws IOException {
129
- try (InputStream input = getHttpConnectionInputStream ( "/libs/granite/operations/content/systemoverview/export.json" )) {
135
+ String getAemVersion (HttpClient httpClient ) throws IOException , InterruptedException {
136
+ try (InputStream input = downloadFromAem ( httpClient , "/libs/granite/operations/content/systemoverview/export.json" )) {
130
137
JSONParser parser = new JSONParser (input );
131
138
Map <String , Object > response = parser .getParsed ();
132
139
getLog ().debug ("Received JSON response " + response );
@@ -142,15 +149,15 @@ String getAemVersion() throws IOException {
142
149
}
143
150
144
151
@ SuppressWarnings ({ "unchecked" , "rawtypes" })
145
- void retrieveClassificationForMixin (ContentClassification classification , MutableContentClassificationMap map ) throws IOException {
152
+ void retrieveClassificationForMixin (HttpClient httpClient , ContentClassification classification , MutableContentClassificationMap map ) throws IOException , InterruptedException {
146
153
// Uses the crxde search to find the current classification
147
154
// (http://localhost:8080/crx/de/query.jsp?_dc=1536334082630&_charset_=utf-8&type=JCR_SQL2&stmt=SELECT%20*%20FROM%20%5Bgranite%3AInternalArea%5D%0A&showResults=true)
148
155
// the index is crucial for that though (property index limited to properties jcr:primaryType and jcr:mixinTypes)
149
156
// for AEM 6.4 we talk about roughly 300 entries
150
157
String query = "SELECT * FROM [" + classification .getLabel () + "]" ;
151
158
StringBuilder urlParameters = new StringBuilder ();
152
159
urlParameters .append ("_dc=" ).append (new Date ().getTime ()).append ("&_charset_=utf-8&type=JCR-SQL2&stmt=" ).append (URLEncoder .encode (query , "ASCII" )).append ("&showResults=true" );
153
- try (InputStream input = getHttpConnectionInputStream ( "/crx/de/query.jsp?" + urlParameters )) {
160
+ try (InputStream input = downloadFromAem ( httpClient , "/crx/de/query.jsp?" + urlParameters )) {
154
161
JSONParser parser = new JSONParser (input );
155
162
Map <String , Object > response = parser .getParsed ();
156
163
getLog ().debug ("Received JSON response " + response );
@@ -171,7 +178,7 @@ void retrieveClassificationForMixin(ContentClassification classification, Mutabl
171
178
}
172
179
for (Map <String , Object > result : (List <Map <String , Object >>)results ) {
173
180
String resourceType = (String )result .get ("path" );
174
- if (StringUtils . isNotBlank ( resourceType ) ) {
181
+ if (resourceType != null ) {
175
182
map .put (resourceType , classification , null );
176
183
}
177
184
}
@@ -181,11 +188,11 @@ void retrieveClassificationForMixin(ContentClassification classification, Mutabl
181
188
}
182
189
183
190
@ SuppressWarnings ("unchecked" )
184
- void retrieveDeprecatedResourceTypes (MutableContentClassificationMap map ) throws IOException {
191
+ void retrieveDeprecatedResourceTypes (HttpClient httpClient , MutableContentClassificationMap map ) throws IOException , InterruptedException {
185
192
// uses query builder api to retrieve all deprecation metadata
186
193
String query = "1_property=cq:deprecated&1_property.operation=exists&p.limit=-1&p.hits=selective&p.properties=" + URLEncoder .encode ("jcr:mixinTypes jcr:path cq:deprecated cq:deprecatedReason" , "ASCII" );
187
194
EnumSet <ContentUsage > allContentUsages = EnumSet .allOf (ContentUsage .class );
188
- try (InputStream input = getHttpConnectionInputStream ( "/bin/querybuilder.json?" + query )) {
195
+ try (InputStream input = downloadFromAem ( httpClient , "/bin/querybuilder.json?" + query )) {
189
196
JSONParser parser = new JSONParser (input );
190
197
Map <String , Object > response = parser .getParsed ();
191
198
getLog ().debug ("Received JSON response " + response );
@@ -211,31 +218,37 @@ void retrieveDeprecatedResourceTypes(MutableContentClassificationMap map) throws
211
218
}
212
219
213
220
@ SuppressWarnings ("java:S2647" ) // basic auth is ok in this context
214
- private InputStream getHttpConnectionInputStream (String path ) throws IOException {
215
- URL url = new URL (baseUrl , path );
216
- getLog ().debug ("Connecting to " + url + "..." );
217
- HttpURLConnection connection = (HttpURLConnection ) url .openConnection ();
221
+ private InputStream downloadFromAem (HttpClient httpClient , String path ) throws IOException , InterruptedException {
218
222
String credentials = username +":" +password ;
219
- // use basic auth
220
- String encoded = Base64 .getEncoder ().encodeToString (credentials .getBytes (StandardCharsets .UTF_8 )); //Java 8
221
- connection .setRequestProperty ("Authorization" , "Basic " +encoded );
222
- return connection .getInputStream ();
223
+ String encoded = Base64 .getEncoder ().encodeToString (credentials .getBytes (StandardCharsets .UTF_8 ));
224
+ URI uri = baseUrl .resolve (path );
225
+ HttpRequest request = HttpRequest .newBuilder ()
226
+ .uri (uri )
227
+ // preemptive-auth only natively supported once authentication in cache (after first successful request), https://stackoverflow.com/a/58612586
228
+ .header ("Authorization" , "Basic " +encoded )
229
+ .build ();
230
+ getLog ().debug ("Connecting to " + uri + "..." );
231
+ HttpResponse <InputStream > response = httpClient .send (request , HttpResponse .BodyHandlers .ofInputStream ());
232
+ return response .body ();
223
233
}
224
234
225
- File createJarWrapper (File sourceFile , File relativeFileNameInJar ) throws IOException {
235
+ File createJarWrapper (Path sourceFile , Path relativeFileNameInJar ) throws IOException {
226
236
Manifest manifest = new Manifest ();
227
237
manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
228
238
File outputFile = File .createTempFile ("contentclassification" , ".jar" );
229
239
try (JarOutputStream target = new JarOutputStream (new FileOutputStream (outputFile ), manifest )) {
230
- // convert to forward slashes
231
- JarEntry entry = new JarEntry (FilenameUtils .separatorsToUnix (relativeFileNameInJar .getPath ()));
232
- entry .setTime (sourceFile .lastModified ());
240
+ JarEntry entry = new JarEntry (getPathWithUnixSeparators (relativeFileNameInJar ));
241
+ entry .setTime (Files .getLastModifiedTime (sourceFile ).toMillis ());
233
242
target .putNextEntry (entry );
234
- try (InputStream input = new FileInputStream (sourceFile )) {
235
- IOUtils . copy ( input , target );
243
+ try (InputStream input = Files . newInputStream (sourceFile )) {
244
+ input . transferTo ( target );
236
245
}
237
246
target .closeEntry ();
238
247
}
239
248
return outputFile ;
240
249
}
250
+
251
+ static String getPathWithUnixSeparators (Path path ) {
252
+ return StreamSupport .stream (path .spliterator (), false ).map (Path ::toString ).collect (Collectors .joining ("/" ));
253
+ }
241
254
}
0 commit comments