View Javadoc

1   /*
2    * To change this license header, choose License Headers in Project Properties.
3    * To change this template file, choose Tools | Templates
4    * and open the template in the editor.
5    */
6   package us.johnmeyer.utilities;
7   
8   import java.net.URL;
9   import java.net.HttpURLConnection;
10  import org.apache.commons.validator.routines.UrlValidator;
11  
12  /**
13   * Immutable {@link java.net.URL} wrapper. Provides URL syntax validation using
14   * Apache Commons validator code.
15   *
16   * <p>Used due to URL's validator not being as good as Apache's, and to avoid
17   *    providing validation for each method that consumes a URL.</p>
18   */
19  
20  public final class UrlWrapper {
21  /* TECHNICAL NOTE
22   * <p>There are many java.net.URL methods
23   * that can be further wrapped, for example, to support connectivity or
24   * to decompose the URL. These methods are left for future implementation.</p>
25   *
26   */
27  
28      /** List of HTTP reponse status codes.
29        */
30      public enum Status {
31  
32          /* TECHNICAL NOTE this is a subset of the status
33             codes and system will retunr UNSUPPORTED if it
34             encounters a code not on this list.
35  
36             HttpURLConnection References are to java library
37             constants, also
38             a subset.*/
39  
40          /** No problem.*/
41          OK(HttpURLConnection.HTTP_OK),
42  
43          /** Unsupported status -- the status code is not
44           *  being tracked in the code.
45           */
46          UNSUPPORTED(-1),
47  
48          /** No Server was found or hostname not found in DNS. */
49          UNKNOWN_HOST(-1),
50  
51          /** Could not find doc.*/
52          NOT_FOUND(HttpURLConnection.HTTP_NOT_FOUND);
53  
54          /** Numeric code depicting http status, e.g. 404 */
55          private int code;
56  
57          /** Text 'reason' explanation for http status. By default
58           *  this is the name of the enumeration element.
59           */
60          private String reason;
61  
62          /** Constructor.
63           *
64           * @param codeParam numeric code of http status.
65           */
66          Status(final int codeParam) {
67  
68              this.code = codeParam;
69              if (codeParam != -1) {
70                  this.reason = "HTTP_" + this.toString();
71              } else {
72                  this.reason = this.toString();
73              }
74  
75         }
76  
77          /**
78           * Numeric code depicting http status, e.g. 404
79           * @return the code
80           */
81          public int getCode() {
82              return code;
83          }
84  
85          /**
86           * Text 'reason' explanation for http status.
87           * @return the reason
88           */
89          public String getReason() {
90              return reason;
91          }
92  
93      }
94  
95      /** Holds URL persistently. */
96      private URL url;
97  
98      /** Private constructor requires factory construction.
99       *
100      *  @param urlParam URL to be wrapped
101      */
102     private UrlWrapper(final URL urlParam) {
103 
104         this.url = urlParam;
105 
106     }
107 
108 
109     /** Returns UrlWrapper instance. Checks validity of URL syntax.
110      *
111      * @param urlStringParam Url to create
112      *
113      * @return a wrapped URL
114      *
115      * @throws IllegalArgumentException if URL is invalid.
116      */
117 
118     public static UrlWrapper newUrlWrapper(final String urlStringParam) {
119 
120         URL url = toValidatedUrl(urlStringParam);
121         String assertUrlString = url.toExternalForm();
122 
123         if (url == null) {
124             throw new IllegalArgumentException("Param " + urlStringParam
125                     + " output is null.");
126         }
127 
128 
129         //verify URL output matches String input
130         if (!urlStringParam.equals(assertUrlString)) {
131             throw new IllegalStateException("String input " + urlStringParam
132                     + " java.net.URL output " + assertUrlString
133                     + ". Match input syntax to this output.");
134          }
135 
136 
137 
138          return new UrlWrapper(url);
139 
140     }
141 
142     /**
143      * Converts String into URL. Checks validity of URL syntax using Apache
144      * Commons validation routine
145      * {@link org.apache.commons.validator.routines.UrlValidator}.
146      *
147      * @param newUrl URL to validate
148      *
149      * @return syntax-validated URL
150      *
151      * @throws IllegalArgumentException if URL is not valid or
152         if java.net.UrlWrapper returns malformed URL exception.
153      */
154     private static java.net.URL toValidatedUrl(final String newUrl) {
155 
156         UrlValidator urlValidator = new UrlValidator();
157 
158         URL returnUrl;
159 
160         String invalidMessage
161                 = " (URL) is invalid per "
162                 + "org.apache.commons.validator.routines.UrlValidator.";
163 
164         if (!urlValidator.isValid(newUrl)) {
165 
166             String validationIssue = newUrl
167                     + invalidMessage;
168 
169             throw new IllegalArgumentException(validationIssue);
170 
171         }
172 
173         try {
174             returnUrl = new URL(newUrl);
175         } catch (java.net.MalformedURLException m) {
176 
177             throw new IllegalArgumentException(m.getMessage());
178 
179         }
180 
181         return returnUrl;
182 
183     }
184 
185     /**
186      * Checks String if it is a valid URL.
187      *
188      * @param urlParam URL to check
189      *
190      * @return true if String is valid URL
191      */
192     public static boolean isValidUrl(final String urlParam) {
193 
194         try {
195 
196             UrlWrapper urlWrapper = UrlWrapper.newUrlWrapper(urlParam);
197 
198             return true;
199 
200         } catch (IllegalArgumentException e) {
201 
202             return false;
203         }
204 
205     }
206 
207     /**Returns pre-validated URL.
208      *
209      * @return immutable java.net.URL value validated at instantiation.
210      */
211     public URL getUrl() {
212 
213         return url;
214 
215     }
216 
217     /**Provides cleartext value.
218       *
219       * @return  pre-validated URL as String.
220      */
221     public String toString() {
222         return url.toExternalForm();
223     }
224 
225 
226     /** Returns http status response.
227      *
228      * @return remote server-reported or network-reported status of this URL
229      *
230      *  @throws java.io.IOException on any input/output error
231      */
232     public Status getResponse() throws java.io.IOException {
233 
234         Integer responseCode = null;
235         Status statusLocal = null;
236         StringBuilder builder = new StringBuilder();
237         HttpURLConnection connection;
238 
239         /* This typecast is really the only way to do this. It's the way
240            the class was designed, as there is now way to retrieve the
241            HTTPURLConnection directly from a connection.*/
242 
243         connection = (HttpURLConnection) url.openConnection();
244 
245         try {
246             responseCode = connection.getResponseCode();
247         } catch (java.net.UnknownHostException e) {
248             statusLocal = Status.UNKNOWN_HOST;
249         } catch (java.io.IOException e) {
250             e.printStackTrace();
251             statusLocal = Status.UNSUPPORTED;
252         }
253 
254 
255         if (statusLocal == null) {
256 
257             for (Status status : Status.values()) {
258                 if (status.getCode() == responseCode) {
259                     statusLocal = status;
260                     break;
261                 }
262             }
263 
264             if (statusLocal == null) {
265 
266                 statusLocal = Status.UNSUPPORTED;
267             }
268 
269         }
270 
271         return statusLocal;
272     }
273 
274 }