1 | |
package org.apache.maven.wagon.mercury; |
2 | |
|
3 | |
import java.io.File; |
4 | |
import java.io.FileInputStream; |
5 | |
import java.io.FileNotFoundException; |
6 | |
import java.io.IOException; |
7 | |
import java.net.MalformedURLException; |
8 | |
import java.net.URL; |
9 | |
import java.util.ArrayList; |
10 | |
import java.util.HashSet; |
11 | |
import java.util.List; |
12 | |
import java.util.Properties; |
13 | |
import java.util.Set; |
14 | |
|
15 | |
import org.apache.maven.mercury.crypto.api.StreamObserverFactory; |
16 | |
import org.apache.maven.mercury.crypto.api.StreamVerifierAttributes; |
17 | |
import org.apache.maven.mercury.crypto.api.StreamVerifierException; |
18 | |
import org.apache.maven.mercury.crypto.api.StreamVerifierFactory; |
19 | |
import org.apache.maven.mercury.crypto.pgp.PgpStreamVerifierFactory; |
20 | |
import org.apache.maven.mercury.spi.http.client.HttpClientException; |
21 | |
import org.apache.maven.mercury.spi.http.client.deploy.DefaultDeployRequest; |
22 | |
import org.apache.maven.mercury.spi.http.client.deploy.DefaultDeployer; |
23 | |
import org.apache.maven.mercury.spi.http.client.deploy.DeployResponse; |
24 | |
import org.apache.maven.mercury.spi.http.client.retrieve.DefaultRetrievalRequest; |
25 | |
import org.apache.maven.mercury.spi.http.client.retrieve.DefaultRetriever; |
26 | |
import org.apache.maven.mercury.spi.http.client.retrieve.RetrievalResponse; |
27 | |
import org.apache.maven.mercury.transport.api.Binding; |
28 | |
import org.apache.maven.mercury.transport.api.Credentials; |
29 | |
import org.apache.maven.mercury.transport.api.Server; |
30 | |
import org.apache.maven.wagon.AbstractWagon; |
31 | |
import org.apache.maven.wagon.ConnectionException; |
32 | |
import org.apache.maven.wagon.ResourceDoesNotExistException; |
33 | |
import org.apache.maven.wagon.TransferFailedException; |
34 | |
import org.apache.maven.wagon.Wagon; |
35 | |
import org.apache.maven.wagon.authentication.AuthenticationException; |
36 | |
import org.apache.maven.wagon.authorization.AuthorizationException; |
37 | |
import org.apache.maven.wagon.events.TransferEvent; |
38 | |
import org.apache.maven.wagon.proxy.ProxyInfo; |
39 | |
import org.apache.maven.wagon.resource.Resource; |
40 | |
import org.codehaus.plexus.lang.DefaultLanguage; |
41 | |
import org.codehaus.plexus.lang.Language; |
42 | |
import org.slf4j.Logger; |
43 | |
import org.slf4j.LoggerFactory; |
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | |
public class MercuryWagon |
56 | |
extends AbstractWagon |
57 | |
implements Wagon |
58 | |
{ |
59 | |
public static final String SYSTEM_PARAMETER_DEBUG_TRANSFER = "maven.mercury.wagon.debug.transfer"; |
60 | 0 | private boolean debugTransfer = Boolean.parseBoolean( System.getProperty( SYSTEM_PARAMETER_DEBUG_TRANSFER, "false" ) ); |
61 | |
|
62 | |
public static final String SYSTEM_PARAMETER_PGP_CONGIG = "maven.mercury.wagon.pgp.config"; |
63 | 0 | private String pgpConfig = System.getProperty( SYSTEM_PARAMETER_PGP_CONGIG, null ); |
64 | |
|
65 | |
|
66 | 0 | public static final String [][] protocolConversions = new String [][] |
67 | |
{ |
68 | |
{"http:", "http:"} |
69 | |
, {"https:", "https:"} |
70 | |
, {"dav:", "http:"} |
71 | |
, {"davs:", "https:"} |
72 | |
, {"dav:http:", "http:"} |
73 | |
, {"dav:https:", "https:"} |
74 | |
, {"mttp:", "http:"} |
75 | |
, {"mttps:", "https:"} |
76 | |
}; |
77 | |
|
78 | 0 | private static final Logger _log = LoggerFactory.getLogger(MercuryWagon.class); |
79 | 0 | private static final Language _lang = new DefaultLanguage( MercuryWagon.class ); |
80 | |
|
81 | |
private Server server; |
82 | |
private DefaultRetriever retriever; |
83 | |
private DefaultDeployer deployer; |
84 | |
|
85 | 0 | private List<TransferEvent> events = new ArrayList<TransferEvent>(8); |
86 | |
private String userAgent; |
87 | |
|
88 | |
|
89 | |
|
90 | |
|
91 | |
public MercuryWagon() |
92 | 0 | { |
93 | 0 | if( _log.isDebugEnabled() ) |
94 | 0 | _log.debug( "MercuryWagon instantiated" ); |
95 | 0 | } |
96 | |
|
97 | |
public MercuryWagon( Server server ) |
98 | |
throws IllegalArgumentException |
99 | 0 | { |
100 | 0 | init( server ); |
101 | 0 | } |
102 | |
|
103 | |
private void init( Server server ) |
104 | |
throws IllegalArgumentException |
105 | |
{ |
106 | 0 | if( server == null ) |
107 | 0 | throw new IllegalArgumentException( _lang.getMessage( "null.read.server" ) ); |
108 | |
|
109 | 0 | if( server.getURL() == null ) |
110 | 0 | throw new IllegalArgumentException( _lang.getMessage( "null.read.server.url", server.getId() ) ); |
111 | |
|
112 | 0 | this.server = server; |
113 | |
|
114 | 0 | Set<StreamVerifierFactory>[] pgpFac = null; |
115 | |
|
116 | |
try |
117 | |
{ |
118 | 0 | pgpFac = readPgpConfig(); |
119 | 0 | this.server.setReaderStreamVerifierFactories( pgpFac[0] ); |
120 | 0 | this.server.setWriterStreamVerifierFactories( pgpFac[1] ); |
121 | |
} |
122 | 0 | catch( Exception ex ) |
123 | |
{ |
124 | 0 | throw new IllegalArgumentException(ex); |
125 | 0 | } |
126 | |
|
127 | 0 | Set<StreamObserverFactory> rf = server.getReaderStreamObserverFactories(); |
128 | 0 | if( rf == null ) |
129 | |
{ |
130 | 0 | rf = new HashSet<StreamObserverFactory>(1); |
131 | 0 | this.server.setReaderStreamObserverFactories( rf ); |
132 | |
} |
133 | 0 | rf.add( new StupidWagonObserverFactory( this ) ); |
134 | |
|
135 | 0 | HashSet<Server> servers = new HashSet<Server>(1); |
136 | 0 | servers.add( this.server ); |
137 | |
|
138 | 0 | Set<StreamObserverFactory> wf = this.server.getWriterStreamObserverFactories(); |
139 | 0 | if( wf == null ) |
140 | |
{ |
141 | 0 | wf = new HashSet<StreamObserverFactory>(1); |
142 | 0 | this.server.setReaderStreamObserverFactories( wf ); |
143 | |
} |
144 | 0 | wf.add( new StupidWagonObserverFactory( this ) ); |
145 | |
|
146 | |
try |
147 | |
{ |
148 | 0 | retriever = new DefaultRetriever(); |
149 | |
} |
150 | 0 | catch( HttpClientException e ) |
151 | |
{ |
152 | 0 | throw new IllegalArgumentException(e); |
153 | 0 | } |
154 | 0 | retriever.setServers( servers ); |
155 | |
|
156 | |
try |
157 | |
{ |
158 | 0 | deployer = new DefaultDeployer(); |
159 | |
} |
160 | 0 | catch( HttpClientException e ) |
161 | |
{ |
162 | 0 | throw new IllegalArgumentException(e); |
163 | 0 | } |
164 | 0 | deployer.setServers( servers ); |
165 | 0 | } |
166 | |
|
167 | |
public void get( String resourceName, File destination ) |
168 | |
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException |
169 | |
{ |
170 | 0 | if( _log.isDebugEnabled() ) |
171 | 0 | _log.debug("MercuryWagon getting "+resourceName+" into "+destination); |
172 | |
|
173 | 0 | Binding binding = null; |
174 | |
|
175 | |
try |
176 | |
{ |
177 | 0 | binding = new Binding( new URL(server.getURL().toString()+'/'+resourceName), destination ); |
178 | |
} |
179 | 0 | catch( MalformedURLException e ) |
180 | |
{ |
181 | 0 | throw new TransferFailedException( e.getMessage() ); |
182 | 0 | } |
183 | |
|
184 | 0 | DefaultRetrievalRequest request = new DefaultRetrievalRequest(); |
185 | 0 | request.addBinding( binding ); |
186 | |
|
187 | 0 | Resource resource = new Resource( resourceName ); |
188 | |
|
189 | 0 | fireGetInitiated( resource, destination ); |
190 | |
|
191 | 0 | resource.setLastModified( 0l ); |
192 | |
|
193 | 0 | server.setUserAgent( userAgent ); |
194 | |
|
195 | 0 | fireGetStarted( resource, destination ); |
196 | |
|
197 | 0 | pushEvent( new TransferEvent(this, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_GET) ); |
198 | |
|
199 | 0 | long start = System.currentTimeMillis(); |
200 | |
|
201 | 0 | RetrievalResponse response = retriever.retrieve( request ); |
202 | |
|
203 | 0 | fireGetCompleted( resource, destination ); |
204 | |
|
205 | 0 | if( response.hasExceptions() ) |
206 | |
{ |
207 | 0 | _log.error( response.getExceptions().toString() ); |
208 | 0 | throw new ResourceDoesNotExistException( response.getExceptions().toString() ); |
209 | |
} |
210 | |
|
211 | 0 | if( _log.isDebugEnabled() ) |
212 | 0 | _log.debug("MercuryWagon got ["+resourceName+" ==> "+destination + "], time " + (System.currentTimeMillis()-start) + " millis"); |
213 | |
|
214 | 0 | } |
215 | |
|
216 | |
public boolean getIfNewer( String resourceName, File destination, long timestamp ) |
217 | |
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException |
218 | |
{ |
219 | 0 | get( resourceName, destination ); |
220 | 0 | return true; |
221 | |
} |
222 | |
|
223 | |
public void put( File source, String destination ) |
224 | |
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException |
225 | |
{ |
226 | 0 | if( _log.isDebugEnabled() ) |
227 | 0 | _log.debug( "put request for: "+source+" => "+destination ); |
228 | |
|
229 | 0 | Resource resource = new Resource( destination ); |
230 | |
|
231 | 0 | firePutInitiated( resource, source ); |
232 | |
|
233 | 0 | resource.setContentLength( source.length() ); |
234 | |
|
235 | 0 | resource.setLastModified( source.lastModified() ); |
236 | |
|
237 | 0 | Binding binding = null; |
238 | |
try |
239 | |
{ |
240 | 0 | binding = new Binding( new URL( this.server.getURL().toString()+'/'+destination), source ); |
241 | |
} |
242 | 0 | catch( MalformedURLException e ) |
243 | |
{ |
244 | 0 | throw new TransferFailedException( e.getMessage() ); |
245 | 0 | } |
246 | |
|
247 | 0 | HashSet<Binding> bindings = new HashSet<Binding>(1); |
248 | 0 | bindings.add( binding ); |
249 | |
|
250 | 0 | DefaultDeployRequest request = new DefaultDeployRequest(); |
251 | 0 | request.setBindings( bindings ); |
252 | |
|
253 | 0 | firePutStarted( resource, source ); |
254 | |
|
255 | 0 | server.setUserAgent( userAgent ); |
256 | |
|
257 | 0 | pushEvent( new TransferEvent(this, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT) ); |
258 | |
|
259 | 0 | long start = System.currentTimeMillis(); |
260 | |
|
261 | 0 | DeployResponse response = deployer.deploy( request ); |
262 | |
|
263 | 0 | firePutCompleted( resource, source ); |
264 | |
|
265 | 0 | if( response.hasExceptions() ) |
266 | |
{ |
267 | 0 | throw new TransferFailedException( response.getExceptions().toString() ); |
268 | |
} |
269 | |
|
270 | 0 | if( _log.isDebugEnabled() ) |
271 | 0 | _log.debug("MercuryWagon put ["+source+" ==> "+destination + "], time " + (System.currentTimeMillis()-start) + " millis"); |
272 | 0 | } |
273 | |
|
274 | |
protected void closeConnection() |
275 | |
throws ConnectionException |
276 | |
{ |
277 | 0 | } |
278 | |
|
279 | |
@Override |
280 | |
public void openConnection() |
281 | |
throws ConnectionException, AuthenticationException |
282 | |
{ |
283 | 0 | openConnectionInternal(); |
284 | 0 | } |
285 | |
|
286 | |
private final String convertProtocol( String pin ) |
287 | |
throws ConnectionException |
288 | |
{ |
289 | 0 | for( String [] pc : protocolConversions ) |
290 | |
{ |
291 | 0 | if( pc[0].equals( pin ) ) |
292 | 0 | return pc[1]; |
293 | |
} |
294 | |
|
295 | 0 | throw new ConnectionException("bad protocol: "+pin); |
296 | |
} |
297 | |
|
298 | |
@Override |
299 | |
protected void openConnectionInternal() |
300 | |
throws ConnectionException, AuthenticationException |
301 | |
{ |
302 | 0 | if( _log.isDebugEnabled() ) |
303 | 0 | _log.debug( "opening connection to repository "+repository ); |
304 | |
|
305 | |
try |
306 | |
{ |
307 | 0 | String repUrl = repository.getUrl(); |
308 | 0 | int index = repUrl.indexOf( '/' ); |
309 | 0 | String protocol = convertProtocol( repUrl.substring( 0, index ) ); |
310 | |
|
311 | 0 | String theRest = repUrl.substring( index ); |
312 | 0 | if( theRest.endsWith( "/" ) ) |
313 | 0 | theRest = theRest.substring( 0, theRest.length()-1 ); |
314 | |
|
315 | 0 | String url = protocol + theRest; |
316 | |
|
317 | 0 | if(_log.isDebugEnabled()) |
318 | 0 | _log.debug( "converted url "+repository.getUrl()+" ==> "+url ); |
319 | |
|
320 | 0 | Server server = new Server( repository.getId(), new URL( url ) ); |
321 | |
|
322 | 0 | if( authenticationInfo != null && authenticationInfo.getUserName() != null ) |
323 | |
{ |
324 | 0 | Credentials user = new Credentials( authenticationInfo.getUserName(), authenticationInfo.getPassword() ); |
325 | |
|
326 | 0 | server.setServerCredentials( user ); |
327 | |
|
328 | 0 | if( _log.isDebugEnabled() ) |
329 | 0 | _log.debug( "user ceredentials: "+user.getUser()+"/..." ); |
330 | |
} |
331 | |
|
332 | |
|
333 | 0 | ProxyInfo pi = getProxyInfo("http", getRepository().getHost()); |
334 | |
|
335 | 0 | String httpProxyType = ProxyInfo.PROXY_HTTP.toLowerCase(); |
336 | |
|
337 | 0 | if( pi != null && pi.getHost() != null ) |
338 | |
{ |
339 | 0 | String proxyType = pi.getType().toLowerCase(); |
340 | |
|
341 | 0 | if( !httpProxyType.equals( proxyType ) ) |
342 | |
{ |
343 | 0 | throw new ConnectionException( "Mercury wagon does not support "+proxyType+" proxies at this point. Only "+httpProxyType+" proxy is supported" ); |
344 | |
} |
345 | |
|
346 | 0 | server.setProxy( new URL("http://"+pi.getHost()+":" + (pi.getPort()>0 ? pi.getPort() : 80) ) ); |
347 | |
|
348 | 0 | if( pi.getUserName() != null ) |
349 | |
{ |
350 | 0 | Credentials proxyUser = new Credentials( pi.getUserName(), pi.getPassword() ); |
351 | |
|
352 | 0 | server.setProxyCredentials( proxyUser ); |
353 | |
|
354 | 0 | if(_log.isDebugEnabled()) |
355 | 0 | _log.debug( "proxy credentials set to : " + proxyUser.getUser()+"/..." ); |
356 | |
} |
357 | |
} |
358 | |
|
359 | 0 | if(_log.isDebugEnabled()) |
360 | 0 | _log.debug( "proxy url set to : " + server.getProxy() ); |
361 | |
|
362 | 0 | init( server ); |
363 | |
|
364 | 0 | if( debugTransfer ) |
365 | 0 | transferEventSupport.addTransferListener( new TransferEventDebugger() ); |
366 | |
} |
367 | |
|
368 | |
|
369 | |
|
370 | |
|
371 | 0 | catch( Throwable e ) |
372 | |
{ |
373 | 0 | e.printStackTrace(); |
374 | 0 | _log.error( e.getMessage() ); |
375 | 0 | throw new ConnectionException( e.getMessage() ); |
376 | 0 | } |
377 | 0 | } |
378 | |
|
379 | |
void bytesReady( TransferEvent transferEvent, byte [] buf, int len ) |
380 | |
{ |
381 | 0 | fireTransferProgress( transferEvent, buf, len ); |
382 | 0 | } |
383 | |
|
384 | |
|
385 | |
|
386 | |
|
387 | |
public final TransferEvent popEvent() |
388 | |
{ |
389 | 0 | if( events.isEmpty() ) |
390 | 0 | return null; |
391 | |
|
392 | 0 | TransferEvent event = events.get( 0 ); |
393 | 0 | events.remove( 0 ); |
394 | |
|
395 | 0 | return event; |
396 | |
} |
397 | |
|
398 | |
public final void pushEvent( final TransferEvent event) |
399 | |
{ |
400 | 0 | events.add( 0, event ); |
401 | 0 | } |
402 | |
|
403 | |
private final Set<StreamVerifierFactory>[] readPgpConfig() |
404 | |
throws FileNotFoundException, IOException, StreamVerifierException |
405 | |
{ |
406 | 0 | Set<StreamVerifierFactory> [] res = new Set [] { null, null }; |
407 | |
|
408 | 0 | if( pgpConfig == null ) |
409 | 0 | return res; |
410 | |
|
411 | 0 | if( _log.isDebugEnabled() ) |
412 | 0 | _log.debug( "PGP signature configuration will be read from "+pgpConfig ); |
413 | |
|
414 | 0 | Properties pgpProps = new Properties(); |
415 | 0 | pgpProps.load( new FileInputStream(pgpConfig) ); |
416 | |
|
417 | 0 | String readerKeyring = pgpProps.getProperty( "reader.keyring" ); |
418 | |
|
419 | 0 | if( readerKeyring != null ) |
420 | |
{ |
421 | 0 | StreamVerifierAttributes readerAttr = new StreamVerifierAttributes( |
422 | |
PgpStreamVerifierFactory.DEFAULT_EXTENSION |
423 | |
, Boolean.parseBoolean( pgpProps.getProperty( "reader.lenient", "true" ) ) |
424 | |
, false |
425 | |
); |
426 | |
|
427 | 0 | StreamVerifierFactory rf = new PgpStreamVerifierFactory( readerAttr, new FileInputStream(readerKeyring) ); |
428 | |
|
429 | 0 | if( _log.isDebugEnabled() ) |
430 | 0 | _log.debug( "public key file: "+new File(readerKeyring).getAbsolutePath() ); |
431 | |
|
432 | 0 | Set<StreamVerifierFactory> rs = new HashSet<StreamVerifierFactory>(1); |
433 | 0 | rs.add( rf ); |
434 | 0 | res[0] = rs; |
435 | |
} |
436 | |
|
437 | 0 | String writerKeyring = pgpProps.getProperty( "writer.keyring" ); |
438 | 0 | String writerKeyId = pgpProps.getProperty( "writer.key.id" ); |
439 | 0 | String writerKeyringPass = pgpProps.getProperty( "writer.key.pass" ); |
440 | |
|
441 | 0 | if( writerKeyring != null && writerKeyId != null && writerKeyringPass != null ) |
442 | |
{ |
443 | 0 | if( _log.isDebugEnabled() ) |
444 | 0 | _log.debug( "secret key file: "+new File(writerKeyring).getAbsolutePath() ); |
445 | |
|
446 | 0 | StreamVerifierAttributes writerAttr = new StreamVerifierAttributes( |
447 | |
PgpStreamVerifierFactory.DEFAULT_EXTENSION |
448 | |
, Boolean.parseBoolean( pgpProps.getProperty( "writer.lenient", "true" ) ) |
449 | |
, false |
450 | |
); |
451 | |
|
452 | 0 | StreamVerifierFactory wf = new PgpStreamVerifierFactory( writerAttr, new FileInputStream(writerKeyring) |
453 | |
, writerKeyId, writerKeyringPass ); |
454 | |
|
455 | 0 | Set<StreamVerifierFactory> ws = new HashSet<StreamVerifierFactory>(1); |
456 | 0 | ws.add( wf ); |
457 | 0 | res[1] = ws; |
458 | |
} |
459 | |
|
460 | 0 | return res; |
461 | |
} |
462 | |
|
463 | |
public void setHttpHeaders( Properties httpHeaders ) |
464 | |
{ |
465 | 0 | this.userAgent = httpHeaders.getProperty( "User-Agent", null ); |
466 | |
|
467 | 0 | if( _log.isDebugEnabled() ) |
468 | 0 | _log.debug( "userAgent set to : "+this.userAgent ); |
469 | |
|
470 | 0 | } |
471 | |
|
472 | |
@Override |
473 | |
public boolean resourceExists( String resourceName ) |
474 | |
throws TransferFailedException, AuthorizationException |
475 | |
{ |
476 | 0 | if( _log.isDebugEnabled() ) |
477 | 0 | _log.debug( "check if resourceExists: "+resourceName+" on server "+(server == null ? "null" : server.getURL() ) ); |
478 | |
|
479 | |
File temp; |
480 | |
try |
481 | |
{ |
482 | 0 | temp = File.createTempFile( "mercury-", ".temp" ); |
483 | |
} |
484 | 0 | catch( IOException e ) |
485 | |
{ |
486 | 0 | throw new TransferFailedException( e.getMessage() ); |
487 | 0 | } |
488 | 0 | temp.delete(); |
489 | |
|
490 | 0 | Binding binding = null; |
491 | |
|
492 | |
try |
493 | |
{ |
494 | 0 | binding = new Binding( new URL(server.getURL().toString()+'/'+resourceName), temp ); |
495 | |
} |
496 | 0 | catch( MalformedURLException e ) |
497 | |
{ |
498 | 0 | throw new TransferFailedException( e.getMessage() ); |
499 | 0 | } |
500 | |
|
501 | 0 | DefaultRetrievalRequest request = new DefaultRetrievalRequest(); |
502 | 0 | request.addBinding( binding ); |
503 | |
|
504 | 0 | server.setUserAgent( userAgent ); |
505 | |
|
506 | 0 | RetrievalResponse response = retriever.retrieve( request ); |
507 | |
|
508 | 0 | temp.delete(); |
509 | |
|
510 | 0 | if( response.hasExceptions() ) |
511 | |
{ |
512 | 0 | _log.error( response.getExceptions().toString() ); |
513 | 0 | return false; |
514 | |
} |
515 | 0 | return true; |
516 | |
} |
517 | |
|
518 | |
|
519 | |
|
520 | |
} |