1   /*
2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3    *
4    * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
5    *
6    * Oracle licenses this file to You under the Apache License, Version 2.0
7    * (the "License"); you may not use this file except in compliance with
8    * the License.  You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   *
18   *
19   * This file incorporates work covered by the following copyright and
20   * permission notice:
21   *
22   * Copyright 2006 Guillaume Nodet
23   *
24   * Licensed under the Apache License, Version 2.0 (the "License");
25   * you may not use this file except in compliance with the License.
26   * You may obtain a copy of the License at
27   *
28   *      http://www.apache.org/licenses/LICENSE-2.0
29   *
30   * Unless required by applicable law or agreed to in writing, software
31   * distributed under the License is distributed on an "AS IS" BASIS,
32   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33   * See the License for the specific language governing permissions and
34   * limitations under the License.
35   */
36  
37  package org.codehaus.mojo.jaxws;
38  
39  import java.io.File;
40  import java.io.FileFilter;
41  import java.io.IOException;
42  import java.io.UnsupportedEncodingException;
43  import java.net.MalformedURLException;
44  import java.net.URI;
45  import java.net.URL;
46  import java.net.URLClassLoader;
47  import java.security.MessageDigest;
48  import java.security.NoSuchAlgorithmException;
49  import java.util.ArrayList;
50  import java.util.Enumeration;
51  import java.util.Formatter;
52  import java.util.List;
53  import java.util.jar.JarEntry;
54  import java.util.jar.JarFile;
55  import java.util.regex.Matcher;
56  import java.util.regex.Pattern;
57  
58  import org.apache.maven.plugin.MojoExecutionException;
59  import org.apache.maven.plugins.annotations.Parameter;
60  import org.apache.maven.settings.Proxy;
61  import org.apache.maven.settings.Settings;
62  
63  /**
64   * 
65   * @author gnodet (gnodet@apache.org)
66   * @author dantran (dantran@apache.org)
67   */
68  abstract class WsImportMojo
69      extends AbstractJaxwsMojo
70  {
71  
72      private static final String STALE_FILE_PREFIX = ".";
73  
74      private static final String PATTERN = "[^\\s]+\\.wsdl$";
75  
76      /**
77       * The package in which the source files will be generated.
78       */
79      @Parameter
80      private String packageName;
81  
82      /**
83       * Catalog file to resolve external entity references support TR9401, 
84       * XCatalog, and OASIS XML Catalog format.
85       */
86      @Parameter
87      private File catalog;
88  
89      /**
90       * Set HTTP/HTTPS proxy. Format is <code>[user[:password]@]proxyHost[:proxyPort]</code>.
91       */
92      @Parameter
93      private String httpproxy;
94  
95      /**
96       * Directory containing WSDL files.
97       */
98      @Parameter( defaultValue = "${project.basedir}/src/wsdl" )
99      private File wsdlDirectory;
100 
101     /**
102      * List of files to use for WSDLs. If not specified, all <code>.wsdl</code>
103      * files in the <code>wsdlDirectory</code> will be used.
104      */
105     @Parameter
106     protected List<String> wsdlFiles;
107 
108     /**
109      * List of external WSDL URLs to be compiled.
110      */
111     @Parameter
112     private List<?> wsdlUrls;
113 
114     /**
115      * Directory containing binding files.
116      */
117     @Parameter( defaultValue = "${project.basedir}/src/jaxws" )
118     protected File bindingDirectory;
119 
120     /**
121      * List of files to use for bindings. If not specified, all <code>.xml</code>
122      * files in the <code>bindingDirectory</code> will be used.
123      */
124     @Parameter
125     protected List<String> bindingFiles;
126 
127     /**
128      * &#64;WebService.wsdlLocation and &#64;WebServiceClient.wsdlLocation value.
129      * 
130      * <p>
131      * Can end with asterisk in which case relative path of the WSDL will
132      * be appended to the given <code>wsdlLocation</code>.
133      * </p>
134      *
135      * <p>Example:
136      * <pre>
137      *  ...
138      *  &lt;configuration>
139      *      &lt;wsdlDirectory>src/mywsdls&lt;/wsdlDirectory>
140      *      &lt;wsdlFiles>
141      *          &lt;wsdlFile>a.wsdl&lt;/wsdlFile>
142      *          &lt;wsdlFile>b/b.wsdl&lt;/wsdlFile>
143      *          &lt;wsdlFile>${project.basedir}/src/mywsdls/c.wsdl&lt;/wsdlFile>
144      *      &lt;/wsdlFiles>
145      *      &lt;wsdlLocation>http://example.com/mywebservices/*&lt;/wsdlLocation>;
146      *  &lt;/configuration>
147      *  ...
148      * </pre>
149      * wsdlLocation for <code>a.wsdl</code> will be http://example.com/mywebservices/a.wsdl<br/>;
150      * wsdlLocation for <code>b/b.wsdl</code> will be http://example.com/mywebservices/b/b.wsdl<br/>;
151      * wsdlLocation for <code>${project.basedir}/src/mywsdls/c.wsdl</code> will be file://absolute/path/to/c.wsdl
152      * </p>
153      *
154      * <p>
155      * Note: External binding files cannot be used if asterisk notation is in place.
156      * </p>
157      */
158     @Parameter
159     private String wsdlLocation;
160 
161     /**
162      * Generate code as per the given JAXWS specification version.
163      * Setting "2.0" will cause JAX-WS to generate artifacts
164      * that run with JAX-WS 2.0 runtime.
165      */
166     @Parameter
167     private String target;
168 
169     /**
170      * Suppress wsimport output.
171      */
172     @Parameter( defaultValue = "false" )
173     private boolean quiet;
174 
175     /**
176      * Local portion of service name for generated JWS implementation.
177      * Implies <code>genJWS=true</code>.
178      *
179      * Note: It is a QName string, formatted as: "{" + Namespace URI + "}" + local part
180      */
181     @Parameter
182     private String implServiceName;
183 
184     /**
185      * Local portion of port name for generated JWS implementation.
186      * Implies <code>genJWS=true</code>.
187      *
188      * Note: It is a QName string, formatted as: "{" + Namespace URI + "}" + local part
189      */
190     @Parameter
191     private String implPortName;
192 
193     /**
194      * Generate stubbed JWS implementation file.
195      */
196     @Parameter( defaultValue = "false" )
197     private boolean genJWS;
198 
199     /**
200      * Turn off compilation after code generation and let generated sources be
201      * compiled by maven during compilation phase; keep is turned on with this option.
202      */
203     @Parameter( defaultValue = "true" )
204     private boolean xnocompile;
205 
206     /**
207      * Maps headers not bound to the request or response messages to Java method parameters.
208      */
209     @Parameter( defaultValue = "false" )
210     private boolean xadditionalHeaders;
211 
212     /**
213      * Turn on debug message.
214      */
215     @Parameter( defaultValue = "false" )
216     private boolean xdebug;
217 
218     /**
219      * Binding W3C EndpointReferenceType to Java. By default WsImport follows spec and does not bind
220      * EndpointReferenceType to Java and uses the spec provided {@link javax.xml.ws.wsaddressing.W3CEndpointReference}
221      */
222     @Parameter( defaultValue = "false" )
223     private boolean xnoAddressingDataBinding;
224 
225     /**
226      * Specify the location of authorization file.
227      */
228     @Parameter
229     protected File xauthFile;
230 
231     /**
232      * Disable the SSL Hostname verification while fetching WSDL(s).
233      */
234     @Parameter( defaultValue = "false" )
235     private boolean xdisableSSLHostnameVerification;
236 
237     /**
238      * If set, the generated Service classes will load the WSDL file from a URL generated from the base resource.
239      */
240     @Parameter( defaultValue = "false" )
241     private boolean xuseBaseResourceAndURLToLoadWSDL;
242 
243     /**
244      * Disable Authenticator used by JAX-WS RI, <code>xauthfile</code> will be ignored if set.
245      */
246     @Parameter( defaultValue = "false" )
247     private boolean xdisableAuthenticator;
248 
249     /**
250      * Specify optional XJC-specific parameters that should simply be passed to <code>xjc</code>
251      * using <code>-B</code> option of WsImport command.
252      * <p>
253      * Multiple elements can be specified, and each token must be placed in its own list.
254      * </p>
255      */
256     @Parameter
257     private List<String> xjcArgs;
258 
259     /**
260      * The folder containing flag files used to determine if the output is stale.
261      */
262     @Parameter( defaultValue = "${project.build.directory}/jaxws/stale" )
263     private File staleFile;
264 
265     /**
266      */
267     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
268     private Settings settings;
269 
270     protected abstract File getImplDestDir();
271 
272     /**
273      * Returns the dependencies that should be placed on the classpath for the classloader
274      * to lookup wsdl files.
275      */
276     protected abstract List<String> getWSDLFileLookupClasspathElements();
277 
278     @Override
279     public void executeJaxws()
280         throws MojoExecutionException
281     {
282         try
283         {
284             URL[] wsdls = getWSDLFiles();
285             if ( wsdls.length == 0 && ( wsdlUrls == null || wsdlUrls.isEmpty() ) )
286             {
287                 getLog().info( "No WSDLs are found to process, Specify at least one of the following parameters: "
288                         + "wsdlFiles, wsdlDirectory or wsdlUrls." );
289                 return;
290             }
291             this.processWsdlViaUrls();
292             this.processLocalWsdlFiles( wsdls );
293         }
294         catch ( MojoExecutionException e )
295         {
296             throw e;
297         }
298         catch ( IOException e )
299         {
300             throw new MojoExecutionException( e.getMessage(), e );
301         }
302     }
303 
304     @Override
305     protected String getMain()
306     {
307         return "com.sun.tools.ws.wscompile.WsimportTool";
308     }
309 
310     @Override
311     protected String getToolName()
312     {
313         return "wsimport";
314     }
315 
316     @Override
317     protected boolean isXnocompile()
318     {
319         return xnocompile;
320     }
321 
322     private void processLocalWsdlFiles( URL[] wsdls )
323         throws MojoExecutionException, IOException
324     {
325         for ( URL u : wsdls )
326         {
327             String url = u.toExternalForm();
328             if ( isOutputStale( url ) )
329             {
330                 getLog().info( "Processing: " + url );
331                 String relPath = null;
332                 if ( "file".equals( u.getProtocol() ) )
333                 {
334                     relPath = getRelativePath( new File( u.getPath() ) );
335                 }
336                 ArrayList<String> args = getWsImportArgs( relPath );
337                 args.add( "\"" + url + "\"" );
338                 getLog().info( "jaxws:wsimport args: " + args );
339                 exec( args );
340                 touchStaleFile( url );
341             }
342             else
343             {
344                 getLog().info( "Ignoring: " + url );
345             }
346             addSourceRoot( getSourceDestDir().getAbsolutePath() );
347         }
348     }
349 
350     /**
351      * Process external wsdl
352      */
353     private void processWsdlViaUrls()
354         throws MojoExecutionException, IOException
355     {
356         for ( int i = 0; wsdlUrls != null && i < wsdlUrls.size(); i++ )
357         {
358             String wsdlUrl = wsdlUrls.get( i ).toString();
359             if ( isOutputStale( wsdlUrl ) )
360             {
361                 getLog().info( "Processing: " + wsdlUrl );
362                 ArrayList<String> args = getWsImportArgs( null );
363                 args.add( "\"" + wsdlUrl + "\"" );
364                 getLog().info( "jaxws:wsimport args: " + args );
365                 exec( args );
366                 touchStaleFile( wsdlUrl );
367             }
368             addSourceRoot( getSourceDestDir().getAbsolutePath() );
369         }
370     }
371 
372     /**
373      * Returns wsimport's command arguments as a list
374      */
375     private ArrayList<String> getWsImportArgs( String relativePath )
376         throws MojoExecutionException
377     {
378         ArrayList<String> args = new ArrayList<>();
379         args.addAll( getCommonArgs() );
380 
381         if ( httpproxy != null )
382         {
383             args.add( "-httpproxy:" + httpproxy );
384         }
385         else if ( settings != null )
386         {
387             String proxyString = getActiveHttpProxy( settings );
388             if ( proxyString != null )
389             {
390                 args.add( "-httpproxy:" + proxyString );
391             }
392 
393             String nonProxyHostsString = getActiveNonProxyHosts( settings );
394             if ( nonProxyHostsString != null )
395             {
396                 addVmArg( "-Dhttp.nonProxyHosts=" + nonProxyHostsString.replace( "|", "^|" ) );
397             }
398         }
399 
400         if ( packageName != null )
401         {
402             args.add( "-p" );
403             args.add( packageName );
404         }
405 
406         if ( catalog != null )
407         {
408             args.add( "-catalog" );
409             args.add( "'" + catalog.getAbsolutePath() + "'" );
410         }
411 
412         if ( wsdlLocation != null )
413         {
414             if ( relativePath != null )
415             {
416                 args.add( "-wsdllocation" );
417                 args.add( wsdlLocation.replaceAll( "\\*", relativePath ) );
418             }
419             else if ( !wsdlLocation.contains( "*" ) )
420             {
421                 args.add( "-wsdllocation" );
422                 args.add( wsdlLocation );
423             }
424         }
425 
426         if ( target != null )
427         {
428             args.add( "-target" );
429             args.add( target );
430         }
431 
432         if ( quiet )
433         {
434             args.add( "-quiet" );
435         }
436 
437         if ( ( genJWS || implServiceName != null || implPortName != null ) && isArgSupported( "-generateJWS" ) )
438         {
439             args.add( "-generateJWS" );
440             if ( implServiceName != null && isArgSupported( "-implServiceName" ) )
441             {
442                 args.add( "-implServiceName" );
443                 args.add( implServiceName );
444             }
445             if ( implPortName != null && isArgSupported( "-implPortName" ) )
446             {
447                 args.add( "-implPortName" );
448                 args.add( implPortName );
449             }
450             File implDestDir = getImplDestDir();
451             if ( !implDestDir.mkdirs() && !implDestDir.exists() )
452             {
453                 getLog().warn( "Cannot create directory: " + implDestDir.getAbsolutePath() );
454             }
455             args.add( "-implDestDir" );
456             args.add( "'" + implDestDir.getAbsolutePath() + "'" );
457             if ( !project.getCompileSourceRoots().contains( implDestDir.getAbsolutePath() ) )
458             {
459                 project.addCompileSourceRoot( implDestDir.getAbsolutePath() );
460             }
461         }
462 
463         if ( xdebug )
464         {
465             args.add( "-Xdebug" );
466         }
467 
468         if ( xnoAddressingDataBinding )
469         {
470             args.add( "-Xno-addressing-databinding" );
471         }
472 
473         if ( xadditionalHeaders )
474         {
475             args.add( "-XadditionalHeaders" );
476         }
477 
478         if ( xauthFile != null )
479         {
480             args.add( "-Xauthfile" );
481             args.add( xauthFile.getAbsolutePath() );
482         }
483 
484         if ( xdisableSSLHostnameVerification )
485         {
486             args.add( "-XdisableSSLHostnameVerification" );
487         }
488         if ( xuseBaseResourceAndURLToLoadWSDL )
489         {
490             args.add( "-XuseBaseResourceAndURLToLoadWSDL" );
491         }
492         if ( xdisableAuthenticator && isArgSupported( "-XdisableAuthenticator" ) )
493         {
494             args.add( "-XdisableAuthenticator" );
495         }
496 
497         if ( xjcArgs != null )
498         {
499             for ( String xjcArg : xjcArgs )
500             {
501                 if ( xjcArg.startsWith( "-" ) )
502                 {
503                     args.add( "-B" + xjcArg );
504                 }
505                 else
506                 {
507                     args.add( xjcArg );
508                 }
509             }
510         }
511 
512         // Bindings
513         File[] bindings = getBindingFiles();
514         if ( bindings.length > 0 && wsdlLocation != null && wsdlLocation.contains( "*" ) )
515         {
516             throw new MojoExecutionException( "External binding file(s) can not be bound to more WSDL files ("
517                 + wsdlLocation + ")\n" + "Please use either inline binding(s) or multiple execution tags." );
518         }
519         for ( File binding : bindings )
520         {
521             args.add( "-b" );
522             args.add( "'" + binding.toURI() + "'" );
523         }
524 
525         return args;
526     }
527 
528     /**
529      * Returns a file array of xml files to translate to object models.
530      * 
531      * @return An array of schema files to be parsed by the schema compiler.
532      */
533     public final File[] getBindingFiles()
534     {
535         File[] bindings;
536 
537         if ( bindingFiles != null )
538         {
539             bindings = new File[bindingFiles.size()];
540             for ( int i = 0; i < bindingFiles.size(); ++i )
541             {
542                 String schemaName = bindingFiles.get( i );
543                 File file = new File( schemaName );
544                 if ( !file.isAbsolute() )
545                 {
546                     file = new File( bindingDirectory, schemaName );
547                 }
548                 bindings[i] = file;
549             }
550         }
551         else
552         {
553             getLog().debug( "The binding Directory is " + bindingDirectory );
554             bindings = bindingDirectory.listFiles( XML_FILE_FILTER );
555             if ( bindings == null )
556             {
557                 bindings = new File[0];
558             }
559         }
560         return bindings;
561     }
562 
563     /**
564      * Returns an array of wsdl files to translate to object models.
565      * 
566      * @return An array of schema files to be parsed by the schema compiler.
567      */
568     private URL[] getWSDLFiles()
569         throws MojoExecutionException
570     {
571         List<URL> files = new ArrayList<>();
572 
573         List<String> classpathElements = getWSDLFileLookupClasspathElements();
574         List<URL> urlCpath = new ArrayList<>( classpathElements.size() );
575         for ( String el : classpathElements )
576         {
577             try
578             {
579                 URL u = new File( el ).toURI().toURL();
580                 urlCpath.add( u );
581             }
582             catch ( MalformedURLException e )
583             {
584                 throw new MojoExecutionException( "Error while retrieving list of WSDL files to process", e );
585             }
586         }
587         
588         try ( URLClassLoader loader = new URLClassLoader( urlCpath.toArray( new URL[0] ) ) )
589         {
590             if ( wsdlFiles != null )
591             {
592                 for ( String wsdlFileName : wsdlFiles )
593                 {
594                     File wsdl = new File( wsdlFileName );
595                     URL toAdd = null;
596                     if ( !wsdl.isAbsolute() )
597                     {
598                         wsdl = new File( wsdlDirectory, wsdlFileName );
599                     }
600                     if ( !wsdl.exists() )
601                     {
602                         toAdd = loader.getResource( wsdlFileName );
603                     }
604                     else
605                     {
606                         try
607                         {
608                             toAdd = wsdl.toURI().toURL();
609                         }
610                         catch ( MalformedURLException ex )
611                         {
612                             getLog().error( ex );
613                         }
614                     }
615                     getLog().debug( "The wsdl File is '" + wsdlFileName + "' from '" + toAdd + "'" );
616                     if ( toAdd != null )
617                     {
618                         files.add( toAdd );
619                     }
620                     else
621                     {
622                         throw new MojoExecutionException( "'" + wsdlFileName + "' not found." );
623                     }
624                 }
625             }
626             else
627             {
628                 getLog().debug( "The wsdl Directory is " + wsdlDirectory );
629                 if ( wsdlDirectory.exists() )
630                 {
631                     File[] wsdls = wsdlDirectory.listFiles( WSDL_FILE_FILTER );
632                     for ( File wsdl : wsdls )
633                     {
634                         files.add( wsdl.toURI().toURL() );
635                     }
636                 }
637                 else
638                 {
639                     URI rel = project.getBasedir().toURI().relativize( wsdlDirectory.toURI() );
640                     String dir = rel.getPath();
641                     URL u = loader.getResource( dir );
642                     if ( u == null )
643                     {
644                         dir = "WEB-INF/wsdl/";
645                         u = loader.getResource( dir );
646                     }
647                     if ( u == null )
648                     {
649                         dir = "META-INF/wsdl/";
650                         u = loader.getResource( dir );
651                     }
652                     if ( !( u == null || !"jar".equalsIgnoreCase( u.getProtocol() ) ) )
653                     {
654                         String path = u.getPath();
655                         Pattern p = Pattern.compile( dir.replace( File.separatorChar, '/' ) + PATTERN,
656                                                      Pattern.CASE_INSENSITIVE );
657                         try ( JarFile jarFile = new JarFile( path.substring( 5, path.indexOf( "!/" ) ) ) )
658                         {
659                             Enumeration<JarEntry> jes = jarFile.entries();
660                             while ( jes.hasMoreElements() )
661                             {
662                                 JarEntry je = jes.nextElement();
663                                 Matcher m = p.matcher( je.getName() );
664                                 if ( m.matches() )
665                                 {
666                                     String s = "jar:" + path.substring( 0, path.indexOf( "!/" ) + 2 ) + je.getName();
667                                     files.add( new URL( s ) );
668                                 }
669                             }
670                         }
671                         catch ( IOException ex )
672                         {
673                             getLog().error( ex );
674                         }
675                     }
676                 }
677             }
678         }
679         catch ( MojoExecutionException e )
680         {
681             throw e;
682         }
683         catch ( Exception e )
684         {
685             throw new MojoExecutionException( "Error while retrieving list of WSDL files to process", e );
686         }
687 
688         return files.toArray( new URL[0] );
689     }
690 
691     /**
692      * A class used to look up .xml documents from a given directory.
693      */
694     private static final FileFilter XML_FILE_FILTER = f -> f.getName().endsWith( ".xml" );
695 
696     /**
697      * A class used to look up .wsdl documents from a given directory.
698      */
699     private static final FileFilter WSDL_FILE_FILTER = f -> f.getName().endsWith( ".wsdl" );
700 
701     private String getRelativePath( File f )
702     {
703         if ( wsdlFiles != null )
704         {
705             for ( String s : wsdlFiles )
706             {
707                 String path = f.getPath().replace( File.separatorChar, '/' );
708                 if ( path.endsWith( s ) && path.length() != s.length() )
709                 {
710                     return s;
711                 }
712             }
713         }
714         else if ( wsdlDirectory != null && wsdlDirectory.exists() )
715         {
716             File[] wsdls = wsdlDirectory.listFiles( WSDL_FILE_FILTER );
717             for ( File wsdl : wsdls )
718             {
719                 String path = f.getPath().replace( File.separatorChar, '/' );
720                 if ( path.endsWith( wsdl.getName() ) )
721                 {
722                     return wsdl.getName();
723                 }
724             }
725         }
726         return null;
727     }
728 
729     /**
730      * Returns true if given WSDL resource or any binding file is newer than the <code>staleFlag</code> file.
731      * 
732      * @return True if wsdl files have been modified since the last build.
733      */
734     private boolean isOutputStale( String resource )
735     {
736         File[] sourceBindings = getBindingFiles();
737         File stFile = new File( staleFile, STALE_FILE_PREFIX + getHash( resource ) );
738         boolean stale = !stFile.exists();
739         if ( !stale )
740         {
741             getLog().debug( "Stale flag file exists, comparing to wsdls and bindings." );
742             long staleMod = stFile.lastModified();
743 
744             try
745             {
746                 // resource can be URL
747                 URL sourceWsdl = new URL( resource );
748                 if ( sourceWsdl.openConnection().getLastModified() > staleMod )
749                 {
750                     getLog().debug( resource + " is newer than the stale flag file." );
751                     stale = true;
752                 }
753             }
754             catch ( MalformedURLException mue )
755             {
756                 // or a file
757                 File sourceWsdl = new File( resource );
758                 if ( sourceWsdl.lastModified() > staleMod )
759                 {
760                     getLog().debug( resource + " is newer than the stale flag file." );
761                     stale = true;
762                 }
763             }
764             catch ( IOException ioe )
765             {
766                 // possible error while opening connection
767                 getLog().error( ioe );
768             }
769 
770             for ( File sourceBinding : sourceBindings )
771             {
772                 if ( sourceBinding.lastModified() > staleMod )
773                 {
774                     getLog().debug( sourceBinding.getName() + " is newer than the stale flag file." );
775                     stale = true;
776                 }
777             }
778         }
779         return stale;
780     }
781 
782     private void touchStaleFile( String resource )
783         throws IOException
784     {
785         File stFile = new File( staleFile, STALE_FILE_PREFIX + getHash( resource ) );
786         if ( !stFile.exists() )
787         {
788             File staleDir = stFile.getParentFile();
789             if ( !staleDir.mkdirs() && !staleDir.exists() )
790             {
791                 getLog().warn( "Cannot create directory: " + staleDir.getAbsolutePath() );
792             }
793             if ( !stFile.createNewFile() )
794             {
795                 getLog().warn( "Cannot create file: " + stFile.getAbsolutePath() );
796             }
797             getLog().debug( "Stale flag file created.[" + stFile.getAbsolutePath() + "]" );
798         }
799         else
800         {
801             if ( !stFile.setLastModified( System.currentTimeMillis() ) )
802             {
803                 getLog().warn( "Stale file has not been updated!" );
804             }
805         }
806     }
807 
808     private String getHash( String s )
809     {
810         try ( Formatter formatter = new Formatter() )
811         {
812             MessageDigest md = MessageDigest.getInstance( "SHA" );
813             for ( byte b : md.digest( s.getBytes( "UTF-8" ) ) )
814             {
815                 formatter.format( "%02x", b );
816             }
817             return formatter.toString();
818         }
819         catch ( UnsupportedEncodingException ex )
820         {
821             getLog().debug( ex.getMessage(), ex );
822         }
823         catch ( NoSuchAlgorithmException ex )
824         {
825             getLog().debug( ex.getMessage(), ex );
826         }
827 
828         // fallback to some default
829         getLog().warn( "Could not compute hash for " + s + ". Using fallback method." );
830         return s.substring( s.lastIndexOf( '/' ) ).replaceAll( "\\.", "-" );
831     }
832 
833     /**
834      * @return proxy string as [user[:password]@]proxyHost[:proxyPort] or null
835      */
836     static String getActiveHttpProxy( Settings s )
837     {
838         String retVal = null;
839         for ( Proxy p : s.getProxies() )
840         {
841             if ( p.isActive() && "http".equals( p.getProtocol() ) )
842             {
843                 StringBuilder sb = new StringBuilder();
844                 String user = p.getUsername();
845                 String pwd = p.getPassword();
846                 if ( user != null )
847                 {
848                     sb.append( user );
849                     if ( pwd != null )
850                     {
851                         sb.append( ":" );
852                         sb.append( pwd );
853                     }
854                     sb.append( "@" );
855                 }
856                 sb.append( p.getHost() );
857                 sb.append( ":" );
858                 sb.append( p.getPort() );
859                 retVal = sb.toString().trim();
860                 break;
861             }
862         }
863         return retVal;
864     }
865 
866     static String getActiveNonProxyHosts( Settings s )
867     {
868         String retVal = null;
869         for ( Proxy p : s.getProxies() )
870         {
871             if ( p.isActive() && "http".equals( p.getProtocol() ) )
872             {
873                 retVal = p.getNonProxyHosts();
874                 break;
875             }
876         }
877         return retVal;
878     }
879 }