1 package org.apache.maven.artifact.deployer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import org.apache.commons.lang.StringUtils;
21 import org.apache.commons.lang.SystemUtils;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.maven.MavenConstants;
25 import org.apache.maven.MavenException;
26 import org.apache.maven.artifact.PomRewriter;
27 import org.apache.maven.project.Project;
28 import org.apache.maven.repository.ArtifactTypeHandler;
29 import org.apache.maven.repository.DefaultArtifactTypeHandler;
30 import org.apache.maven.wagon.ConnectionException;
31 import org.apache.maven.wagon.ResourceDoesNotExistException;
32 import org.apache.maven.wagon.TransferFailedException;
33 import org.apache.maven.wagon.Wagon;
34 import org.apache.maven.wagon.authentication.AuthenticationException;
35 import org.apache.maven.wagon.authentication.AuthenticationInfo;
36 import org.apache.maven.wagon.authorization.AuthorizationException;
37 import org.apache.maven.wagon.events.TransferListener;
38 import org.apache.maven.wagon.observers.ChecksumObserver;
39 import org.apache.maven.wagon.providers.file.FileWagon;
40 import org.apache.maven.wagon.providers.ftp.FtpWagon;
41 import org.apache.maven.wagon.providers.http.HttpWagon;
42 import org.apache.maven.wagon.providers.ssh.external.ScpExternalWagon;
43 import org.apache.maven.wagon.providers.ssh.jsch.ScpWagon;
44 import org.apache.maven.wagon.providers.ssh.jsch.SftpWagon;
45 import org.apache.maven.wagon.repository.Repository;
46 import org.codehaus.plexus.util.FileUtils;
47 import org.codehaus.plexus.util.cli.CommandLineException;
48 import org.codehaus.plexus.util.cli.CommandLineUtils;
49 import org.codehaus.plexus.util.cli.Commandline;
50 import org.codehaus.plexus.util.cli.DefaultConsumer;
51
52 import java.io.BufferedReader;
53 import java.io.ByteArrayInputStream;
54 import java.io.File;
55 import java.io.IOException;
56 import java.io.InputStream;
57 import java.io.InputStreamReader;
58 import java.net.MalformedURLException;
59 import java.security.NoSuchAlgorithmException;
60 import java.text.DateFormat;
61 import java.text.SimpleDateFormat;
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.Date;
65 import java.util.HashMap;
66 import java.util.Iterator;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.TimeZone;
70
71 /**
72 * Default implementation of Artifact Deployer interface.
73 *
74 * @author <a href="mailto:michal.maczka@dimatics.com">Michal Maczka</a>
75 * @version $Id: DefaultArtifactDeployer.java 538499 2007-05-16 09:30:20Z ltheussl $
76 */
77 public class DefaultArtifactDeployer implements ArtifactDeployer
78 {
79
80 protected static final String POM_TYPE = "pom";
81
82 public static final String SIGNATURE_EXTENSION = ".asc";
83
84 private static final ArtifactTypeHandler POM_ARTIFACT_TYPE_HANDLER = new DefaultArtifactTypeHandler();
85
86 /**
87 * Date/time stamp which is appended to snapshot filenames
88 */
89 protected final static String SNAPSHOT_FORMAT = "yyyyMMdd.HHmmss";
90
91 protected String snapshotSignature;
92
93 private static final Log LOG = LogFactory.getLog( DefaultArtifactDeployer.class );
94
95 /**
96 * @see ArtifactDeployer#deploy(String, String, Project, ArtifactTypeHandler)
97 */
98 public void deploy( final String artifact, final String type, final Project project,
99 final ArtifactTypeHandler handler ) throws MavenException
100 {
101 this.handleDeploy( type, project, project.getArtifactId(), artifact, handler, project.getCurrentVersion() );
102 }
103
104 /**
105 * @see DefaultArtifactDeployer#deploySnapshot(String, String, Project, ArtifactTypeHandler)
106 */
107 public void deploySnapshot( final String artifact, final String type, final Project project,
108 final ArtifactTypeHandler handler ) throws MavenException
109 {
110 this.handleDeploy( type, project, project.getArtifactId(), artifact, handler, MavenConstants.SNAPSHOT_SIGNIFIER );
111 }
112
113 protected void handleDeploy( final String type, final Project project, final String artifactId,
114 final String artifact, final ArtifactTypeHandler handler, final String version )
115 throws MavenException
116 {
117 Boolean gpgSkip = Boolean.valueOf( (String) project.getContext().getVariable( "maven.artifact.gpg.skip" ) );
118 String gpgPassphrase = null;
119 if ( !gpgSkip.booleanValue() )
120 {
121 try
122 {
123 gpgPassphrase = getPassphrase( project );
124 }
125 catch ( IOException e )
126 {
127 throw new MavenException( "Error while retreiving the passphrase for gpg", e );
128 }
129 }
130 String gpgKeyname = (String) project.getContext().getVariable( "maven.artifact.gpg.keyname" );
131 Boolean gpgUseagent =
132 Boolean.valueOf( (String) project.getContext().getVariable( "maven.artifact.gpg.useagent" ) );
133
134 File file;
135 if ( DefaultArtifactDeployer.POM_TYPE.equals( type ) )
136 {
137 file = PomRewriter.getRewrittenPom( project );
138 }
139 else
140 {
141 file = this.getFileForArtifact( artifact );
142 }
143
144
145 if ( !DefaultArtifactDeployer.POM_TYPE.equals( type ) )
146 {
147 File pomFile = PomRewriter.getRewrittenPom( project );
148 if ( !gpgSkip.booleanValue() )
149 {
150 generateSignatureForArtifact( pomFile, gpgPassphrase, gpgUseagent.booleanValue(), gpgKeyname );
151 }
152 this.doDeploy( pomFile, project, artifactId, DefaultArtifactDeployer.POM_ARTIFACT_TYPE_HANDLER, version,
153 DefaultArtifactDeployer.POM_TYPE, gpgSkip.booleanValue(), gpgPassphrase,
154 gpgUseagent.booleanValue(), gpgKeyname );
155 }
156 if ( !gpgSkip.booleanValue() )
157 {
158 generateSignatureForArtifact( file, gpgPassphrase, gpgUseagent.booleanValue(), gpgKeyname );
159 }
160 this.doDeploy( file, project, artifactId, handler, version, type, gpgSkip.booleanValue(), gpgPassphrase,
161 gpgUseagent.booleanValue(), gpgKeyname );
162
163 this.snapshotSignature = null;
164
165 }
166
167 /**
168 * @see ArtifactDeployer#install(String, String, Project, ArtifactTypeHandler)
169 */
170 public void install( final String artifact, final String type, final Project project,
171 final ArtifactTypeHandler handler ) throws MavenException
172 {
173 this.handleInstall( type, project, artifact, handler, project.getCurrentVersion() );
174 }
175
176 /**
177 * @see ArtifactDeployer#installSnapshot(String, String, Project, ArtifactTypeHandler)
178 */
179 public void installSnapshot( final String artifact, final String type, final Project project,
180 final ArtifactTypeHandler handler ) throws MavenException
181 {
182 this.handleInstall( type, project, artifact, handler, MavenConstants.SNAPSHOT_SIGNIFIER );
183 }
184
185 private void handleInstall( final String type, final Project project, final String artifact,
186 final ArtifactTypeHandler handler, final String version ) throws MavenException
187 {
188 File file;
189 if ( DefaultArtifactDeployer.POM_TYPE.equals( type ) )
190 {
191 file = PomRewriter.getRewrittenPom( project );
192 }
193 else
194 {
195 file = this.getFileForArtifact( artifact );
196 }
197
198 this.doInstall( file, type, project, version, handler );
199
200
201 if ( !DefaultArtifactDeployer.POM_TYPE.equals( type ) )
202 {
203 this.doInstall( PomRewriter.getRewrittenPom( project ), DefaultArtifactDeployer.POM_TYPE, project, version,
204 DefaultArtifactDeployer.POM_ARTIFACT_TYPE_HANDLER );
205 }
206 }
207
208 /**
209 * Install given file in local repository
210 *
211 * @param file
212 * the artifact file to install
213 * @param type
214 * The type of the artiafct
215 * @param project
216 * @param version
217 * String denominating the version of the artifact
218 * @throws MavenException
219 */
220 private void doInstall( final File file, final String type, final Project project, final String version,
221 final ArtifactTypeHandler handler ) throws MavenException
222 {
223 try
224 {
225 final Repository repository = new Repository( "local", "file:" + project.getContext().getMavenRepoLocal() );
226 final String repositoryPath = handler.constructRepositoryFullPath( type, project, version );
227 this.deployFile( repository, file, repositoryPath, project, true, null, false, null );
228 }
229 catch ( final Exception e )
230 {
231 final String msg = "Cannot install file: '" + file + "'. Reason: " + e.getMessage();
232 throw new MavenException( msg, e );
233 }
234 }
235
236 protected String findSshIdentity()
237 {
238 String key = this.findSshIdentity( System.getProperty( "user.home" ) );
239 if ( key != null )
240 {
241 return key;
242 }
243 if ( System.getProperty( "user.home" ).equals( System.getProperty( "user.home.env" ) ) == false )
244 {
245 key = this.findSshIdentity( System.getProperty( "user.home.env" ) );
246 if ( key != null )
247 {
248 return key;
249 }
250 }
251 DefaultArtifactDeployer.LOG.warn( "Unable to locate identity id_rsa, id_dsa or identity - set maven.repo.default.privatekey" );
252 return null;
253 }
254
255 private String findSshIdentity( final String home )
256 {
257 if ( home == null )
258 {
259 return null;
260 }
261 final File sshHome = new File( home, ".ssh" );
262 DefaultArtifactDeployer.LOG.debug( "Looking for SSH keys in " + sshHome );
263 File key = new File( sshHome, "id_dsa" );
264 if ( key.exists() )
265 {
266 DefaultArtifactDeployer.LOG.debug( "found " + key );
267 return key.getAbsolutePath();
268 }
269 key = new File( sshHome, "id_rsa" );
270 if ( key.exists() )
271 {
272 DefaultArtifactDeployer.LOG.debug( "found " + key );
273 return key.getAbsolutePath();
274 }
275 key = new File( sshHome, "identity" );
276 if ( key.exists() )
277 {
278 DefaultArtifactDeployer.LOG.debug( "found " + key );
279 return key.getAbsolutePath();
280 }
281 return null;
282 }
283
284 private void doDeploy( final File file, final Project project, final String artifactId,
285 final ArtifactTypeHandler handler, final String version, final String type,
286 final boolean gpgSkip, final String gpgPass, final boolean gpgUseAgent,
287 final String gpgKeyname ) throws MavenException
288 {
289 final List srcFiles = new ArrayList( 3 );
290 final List destFiles = new ArrayList( 3 );
291
292 srcFiles.add( file );
293 destFiles.add( handler.constructRepositoryFullPath( type, project, version ) );
294
295 if ( version.indexOf( MavenConstants.SNAPSHOT_SIGNIFIER ) >= 0 )
296 {
297 final String signature = this.getSnapshotSignature();
298 final String v = StringUtils.replace( version, MavenConstants.SNAPSHOT_SIGNIFIER, signature );
299
300 final File snapshotVersionFile = this.createSnapshotVersionFile( file, v, artifactId, type );
301
302 final String snapshotVersionsFilename =
303 handler.constructRepositoryDirectoryPath( type, project ) + artifactId + "-snapshot-version";
304
305 srcFiles.add( snapshotVersionFile );
306 destFiles.add( snapshotVersionsFilename );
307
308 final String deployTimestamp =
309 (String) project.getContext().getVariable( "maven.artifact.deploy.timestamps" );
310 if ( deployTimestamp.equals( "true" ) )
311 {
312 srcFiles.add( file );
313 destFiles.add( handler.constructRepositoryFullPath( type, project, v ) );
314 }
315 }
316
317
318
319 String repoStr = (String) project.getContext().getVariable( "maven.repo.list" );
320
321 if ( ( repoStr == null ) || ( repoStr.trim().length() == 0 ) )
322 {
323 String central = project.getDistributionSite();
324 String centralDirectory = project.getDistributionDirectory();
325 if ( ( central == null ) || ( central.trim().length() == 0 ) )
326 {
327 central = (String) project.getContext().getVariable( "maven.repo.central" );
328 centralDirectory = (String) project.getContext().getVariable( "maven.repo.central.directory" );
329 }
330 if ( ( central != null ) && ( central.trim().length() > 0 ) )
331 {
332 repoStr = "default";
333 project.getContext().setVariable( "maven.repo.default", "scp://" + central );
334 if ( project.getContext().getVariable( "maven.repo.default.privatekey" ) == null )
335 {
336 project.getContext().setVariable( "maven.repo.default.privatekey", this.findSshIdentity() );
337 }
338 if ( project.getContext().getVariable( "maven.repo.default.passphrase" ) == null )
339 {
340 DefaultArtifactDeployer.LOG.warn( "WARNING: assuming empty passphrase. Specify maven.repo.default.passphrase if needed" );
341 project.getContext().setVariable( "maven.repo.default.passphrase", "" );
342 }
343 project.getContext().setVariable( "maven.repo.default.directory", centralDirectory );
344 project.getContext().setVariable( "maven.repo.default.username",
345 project.getContext().getVariable( "maven.username" ) );
346 project.getContext().setVariable( "maven.repo.default.group",
347 project.getContext().getVariable( "maven.remote.group" ) );
348 }
349 }
350
351 final String[] repos = StringUtils.split( repoStr, "," );
352
353 DefaultArtifactDeployer.LOG.info( "Will deploy to " + repos.length + " repository(ies): " + repoStr );
354 boolean success = false;
355 for ( int i = 0; i < repos.length; i++ )
356 {
357
358 final String repo = repos[i].trim();
359 DefaultArtifactDeployer.LOG.info( "Deploying to repository: " + repo );
360 final Repository repository = RepositoryBuilder.getRepository( project, repo );
361 final AuthenticationInfo authenticationInfo = RepositoryBuilder.getAuthenticationInfo( project, repo );
362 try
363 {
364 this.deployFiles( repository, srcFiles, destFiles, authenticationInfo, project, gpgSkip, gpgPass,
365 gpgUseAgent, gpgKeyname );
366 success = true;
367 }
368 catch ( final Exception e )
369 {
370 final String msg = "Failed to deploy to: " + repository.getId() + " Reason: " + e;
371 DefaultArtifactDeployer.LOG.warn( msg, e );
372
373 continue;
374 }
375
376 }
377 if ( !success )
378 {
379 throw new MavenException( "Unable to deploy to any repositories" );
380 }
381 }
382
383 protected void deployFile( final Repository repository, final File src, final String dest, final Project project,
384 final boolean gpgSkip, final String gpgPass, final boolean gpgUseAgent,
385 final String gpgKeyname )
386 throws ResourceDoesNotExistException, MalformedURLException, NoSuchAlgorithmException, TransferFailedException,
387 ConnectionException, AuthenticationException, AuthorizationException, MavenException
388 {
389 this.deployFiles( repository, Collections.singletonList( src ), Collections.singletonList( dest ), null,
390 project, gpgSkip, gpgPass, gpgUseAgent, gpgKeyname );
391 }
392
393 protected void deployFiles( final Repository repository, final List srcFiles, final List destFiles,
394 final AuthenticationInfo authenticationInfo, final Project project,
395 final boolean gpgSkip, final String gpgPass, final boolean gpgUseAgent,
396 final String gpgKeyname )
397 throws ConnectionException, AuthenticationException, ResourceDoesNotExistException, TransferFailedException,
398 AuthorizationException, MalformedURLException, NoSuchAlgorithmException, MavenException
399 {
400
401 if ( srcFiles.size() != destFiles.size() )
402 {
403 final String msg = "Lengths of the lists should be the same";
404 throw new IllegalArgumentException( msg );
405 }
406
407 final Wagon wagon = this.getWagon( repository.getProtocol(), project, repository.getId() );
408
409 final TransferListener uploadMonitor = new UploadMeter();
410
411 final Map checksums = new HashMap( 2 );
412
413 ChecksumObserver observer = new ChecksumObserver( "MD5" );
414 checksums.put( "md5", observer );
415 wagon.addTransferListener( observer );
416 observer = new ChecksumObserver( "SHA-1" );
417 checksums.put( "sha1", observer );
418 wagon.addTransferListener( observer );
419
420 try
421 {
422 wagon.connect( repository, authenticationInfo );
423 final Iterator srcIterator = srcFiles.iterator();
424 final Iterator destIterator = destFiles.iterator();
425 while ( srcIterator.hasNext() )
426 {
427 wagon.addTransferListener( uploadMonitor );
428
429 final File srcFile = (File) srcIterator.next();
430 final String destFile = (String) destIterator.next();
431
432 wagon.put( srcFile, destFile );
433
434 wagon.removeTransferListener( uploadMonitor );
435
436 final Map sums = new HashMap( 2 );
437 for ( final Iterator i = checksums.keySet().iterator(); i.hasNext(); )
438 {
439
440 final String extension = (String) i.next();
441 observer = (ChecksumObserver) checksums.get( extension );
442 sums.put( extension, observer.getActualChecksum() );
443 }
444
445 for ( final Iterator i = checksums.keySet().iterator(); i.hasNext(); )
446 {
447 final String extension = (String) i.next();
448
449
450 final File temp = File.createTempFile( "maven-artifact", null );
451 temp.deleteOnExit();
452 FileUtils.fileWrite( temp.getAbsolutePath(), (String) sums.get( extension ) );
453
454 wagon.put( temp, destFile + "." + extension );
455 }
456
457 if ( !gpgSkip )
458 {
459 File gpgSignature = generateSignatureForArtifact( srcFile, gpgPass, gpgUseAgent, gpgKeyname );
460 wagon.put( gpgSignature, destFile + SIGNATURE_EXTENSION );
461 }
462
463 }
464 }
465 catch ( final IOException e )
466 {
467 throw new MavenException( "Error creating temporary file to transfer checksums", e );
468 }
469 finally
470 {
471 try
472 {
473 wagon.disconnect();
474 }
475 catch ( final Exception e )
476 {
477 DefaultArtifactDeployer.LOG.error( "Error cleaning up from the deployer", e );
478 }
479 }
480 }
481
482 private Wagon getWagon( final String protocol, final Project project, final String id )
483 throws MalformedURLException
484 {
485 Wagon wagon;
486
487 if ( protocol.equals( "http" ) )
488 {
489 wagon = new HttpWagon();
490 }
491 else if ( protocol.equals( "ftp" ) )
492 {
493 wagon = new FtpWagon();
494 RepositoryBuilder.configureFtpWagon( project, id, (FtpWagon) wagon );
495 }
496 else if ( protocol.equals( "sftp" ) )
497 {
498 wagon = new SftpWagon();
499 RepositoryBuilder.configureSftpWagon( project, id, (SftpWagon) wagon );
500 }
501 else if ( protocol.equals( "file" ) )
502 {
503 wagon = new FileWagon();
504 }
505 else if ( protocol.equals( "scp" ) )
506 {
507 wagon = new ScpWagon();
508 RepositoryBuilder.configureScpWagon( project, id, (ScpWagon) wagon );
509 }
510 else if ( protocol.equals( "scpexe" ) )
511 {
512 wagon = new ScpExternalWagon();
513 RepositoryBuilder.configureSshExternalWagon( project, id, (ScpExternalWagon) wagon );
514 return wagon;
515 }
516 else
517 {
518 throw new MalformedURLException( "Unknown Wagon protocol: " + protocol );
519 }
520
521 return wagon;
522 }
523
524 protected String getSnapshotSignature()
525 {
526 if ( this.snapshotSignature == null )
527 {
528 final DateFormat fmt = new SimpleDateFormat( DefaultArtifactDeployer.SNAPSHOT_FORMAT );
529 fmt.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
530 this.snapshotSignature = fmt.format( new Date() );
531 }
532 return this.snapshotSignature;
533 }
534
535 protected File getFileForArtifact( final String artifact ) throws MavenException
536 {
537 final File file = new File( artifact );
538 if ( !file.exists() )
539 {
540 final String msg = "Artifact file: '" + artifact + "' must exist";
541 throw new MavenException( msg );
542 }
543 if ( !file.canRead() )
544 {
545 final String msg = "Artifact file: '" + artifact + "' must be readable";
546 throw new MavenException( msg );
547 }
548 if ( file.isDirectory() )
549 {
550 final String msg = "Artifact file: '" + artifact + "' must not be a directory";
551 throw new MavenException( msg );
552 }
553 return file.getAbsoluteFile();
554 }
555
556 /**
557 * Create a file which contains timestamp of the latetst snapshot
558 */
559 protected File createSnapshotVersionFile( final File artifact, final String snapshotVersion,
560 final String artifactId, final String type ) throws MavenException
561 {
562 File file = null;
563 final String filename = artifactId + "-" + type + "-snapshot-version";
564 try
565 {
566 file = new File( artifact.getParent(), filename );
567 FileUtils.fileWrite( file.getAbsolutePath(), snapshotVersion );
568 file.deleteOnExit();
569 }
570 catch ( final Exception e )
571 {
572 throw new MavenException( "Cannot create snapshot-version file:" + file );
573 }
574 return file;
575 }
576
577 /**
578 * @param pass
579 * The passphrase to use when signing. "${maven.artifact.gpg.passphrase}"
580 * @param keyname
581 * The "name" of the key to sign with. Passed to gpg as --local-user. "${maven.artifact.gpg.keyname}"
582 * @param useAgent
583 * Passes --use-agent or --no-use-agent to gpg. If using an agent, the password is optional as the agent
584 * will provide it. "${maven.artifact.gpg.useagent}"
585 */
586 protected File generateSignatureForArtifact( File file, String pass, boolean useAgent, String keyname )
587 throws MavenException
588 {
589 File signature = new File( file + SIGNATURE_EXTENSION );
590
591 if ( signature.exists() )
592 {
593 signature.delete();
594 }
595
596 Commandline cmd = new Commandline();
597
598 cmd.setExecutable( "gpg" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" ) );
599
600 if ( useAgent )
601 {
602 cmd.createArgument().setValue( "--use-agent" );
603 }
604 else
605 {
606 cmd.createArgument().setValue( "--no-use-agent" );
607 }
608
609 InputStream in = null;
610 if ( null != pass )
611 {
612 cmd.createArgument().setValue( "--passphrase-fd" );
613
614 cmd.createArgument().setValue( "0" );
615
616
617 in = new ByteArrayInputStream( pass.getBytes() );
618 }
619
620 if ( null != keyname )
621 {
622 cmd.createArgument().setValue( "--local-user" );
623
624 cmd.createArgument().setValue( keyname );
625 }
626
627 cmd.createArgument().setValue( "--armor" );
628
629 cmd.createArgument().setValue( "--detach-sign" );
630
631 cmd.createArgument().setFile( file );
632
633 try
634 {
635 LOG.debug( "GPG cmd : " + cmd );
636 int exitCode = CommandLineUtils.executeCommandLine( cmd, in, new DefaultConsumer(), new DefaultConsumer() );
637
638 if ( exitCode != 0 )
639 {
640 throw new MavenException( "Exit code: " + exitCode );
641 }
642 }
643 catch ( CommandLineException e )
644 {
645 throw new MavenException( "Unable to execute gpg command", e );
646 }
647
648 return signature;
649 }
650
651 protected String getPassphrase( Project project ) throws IOException
652 {
653 String pass = (String) project.getContext().getVariable( "maven.artifact.gpg.passphrase" );
654 if ( pass == null )
655 {
656
657
658 BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
659 while ( System.in.available() != 0 )
660 {
661
662
663 System.in.read();
664 }
665
666 System.out.print( "GPG Passphrase: " );
667 MaskingThread thread = new MaskingThread();
668 thread.start();
669
670 pass = in.readLine();
671
672
673 thread.stopMasking();
674 }
675 project.getContext().setVariable( "maven.artifact.gpg.passphrase", pass );
676 return pass;
677 }
678
679
680 class MaskingThread extends Thread
681 {
682 private volatile boolean stop;
683
684 /**
685 * Begin masking until asked to stop.
686 */
687 public void run()
688 {
689
690
691
692 int priority = Thread.currentThread().getPriority();
693 Thread.currentThread().setPriority( Thread.MAX_PRIORITY );
694
695 try
696 {
697 stop = false;
698 while ( !stop )
699 {
700
701 System.out.print( "\010*" );
702 try
703 {
704
705 Thread.sleep( 1 );
706 }
707 catch ( InterruptedException iex )
708 {
709 Thread.currentThread().interrupt();
710 return;
711 }
712 }
713 }
714 finally
715 {
716
717 Thread.currentThread().setPriority( priority );
718 }
719 }
720
721 /**
722 * Instruct the thread to stop masking.
723 */
724 public void stopMasking()
725 {
726 this.stop = true;
727 }
728 }
729 }