View Javadoc
1   /*******************************************************************************
2    * Portions created by Sebastian Thomschke are copyright (c) 2005-2015 Sebastian
3    * Thomschke.
4    *
5    * All Rights Reserved. This program and the accompanying materials
6    * are made available under the terms of the Eclipse Public License v1.0
7    * which accompanies this distribution, and is available at
8    * http://www.eclipse.org/legal/epl-v10.html
9    *
10   * Contributors:
11   *     Sebastian Thomschke - initial implementation.
12   *     Makkari - live connect support.
13   *******************************************************************************/
14  package net.sf.oval.constraint;
15  
16  import static net.sf.oval.Validator.*;
17  
18  import java.io.IOException;
19  import java.net.HttpURLConnection;
20  import java.net.URI;
21  import java.net.URL;
22  import java.net.URLConnection;
23  import java.util.List;
24  
25  import net.sf.oval.ConstraintTarget;
26  import net.sf.oval.Validator;
27  import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
28  import net.sf.oval.context.OValContext;
29  import net.sf.oval.internal.Log;
30  import net.sf.oval.internal.util.ArrayUtils;
31  
32  /**
33   * @author Sebastian Thomschke
34   */
35  public class AssertURLCheck extends AbstractAnnotationCheck<AssertURL>
36  {
37  	/**
38  	 * http://en.wikipedia.org/wiki/URI_scheme
39  	 *
40  	 * @author Sebastian Thomschke
41  	 *
42  	 */
43  	public static enum URIScheme
44  	{
45  		FTP("ftp"),
46  		HTTP("http"),
47  		HTTPS("https");
48  
49  		private final String scheme;
50  
51  		private URIScheme(final String scheme)
52  		{
53  			this.scheme = scheme;
54  		}
55  
56  		/**
57  		 * @return the scheme
58  		 */
59  		public String getScheme()
60  		{
61  			return scheme;
62  		}
63  
64  		@Override
65  		public String toString()
66  		{
67  			return scheme;
68  		}
69  	}
70  
71  	private static final long serialVersionUID = 1L;
72  
73  	private static final Log LOG = Log.getLog(AssertURLCheck.class);
74  
75  	private static boolean canConnect(final String url)
76  	{
77  		try
78  		{
79  			final URL theURL = new URL(url);
80  			final URLConnection conn = theURL.openConnection();
81  			conn.connect();
82  			conn.getInputStream().close();
83  			if (conn instanceof HttpURLConnection)
84  			{
85  				final HttpURLConnection httpConnection = (HttpURLConnection) conn;
86  				final int rc = httpConnection.getResponseCode();
87  
88  				if (rc < HttpURLConnection.HTTP_BAD_REQUEST) return true;
89  				LOG.debug("Connecting failed with HTTP response code " + rc);
90  				return false;
91  			}
92  			return true;
93  		}
94  		catch (final IOException ex)
95  		{
96  			LOG.debug("Connecting failed with exception", ex);
97  			return false;
98  		}
99  	}
100 
101 	/**
102 	 * Specifies if a connection to the URL should be attempted to verify its validity.
103 	 */
104 	private boolean connect = false;
105 
106 	/**
107 	 * Specifies the allowed URL schemes.
108 	 */
109 	private final List<URIScheme> permittedURISchemes = getCollectionFactory().createList(2);
110 
111 	/**
112 	 * {@inheritDoc}
113 	 */
114 	@Override
115 	public void configure(final AssertURL constraintAnnotation)
116 	{
117 		super.configure(constraintAnnotation);
118 		setConnect(constraintAnnotation.connect());
119 		setPermittedURISchemes(constraintAnnotation.permittedURISchemes());
120 	}
121 
122 	/**
123 	 * {@inheritDoc}
124 	 */
125 	@Override
126 	protected ConstraintTarget[] getAppliesToDefault()
127 	{
128 		return new ConstraintTarget[]{ConstraintTarget.VALUES};
129 	}
130 
131 	/**
132 	 * Gets the allowed URL schemes.
133 	 * @return the permittedURISchemes
134 	 */
135 	public URIScheme[] getPermittedURISchemes()
136 	{
137 		return permittedURISchemes.size() == 0 ? null : permittedURISchemes.toArray(new URIScheme[permittedURISchemes.size()]);
138 	}
139 
140 	/**
141 	 * Specifies if a connection to the URL should be attempted to verify its validity.
142 	 *
143 	 * @return the connect
144 	 */
145 	public boolean isConnect()
146 	{
147 		return connect;
148 	}
149 
150 	/**
151 	 * {@inheritDoc}
152 	 */
153 	public boolean isSatisfied(final Object validatedObject, final Object valueToValidate, final OValContext context,
154 			final Validator validator)
155 	{
156 		if (valueToValidate == null) return true;
157 
158 		final String uriString = valueToValidate.toString();
159 
160 		try
161 		{
162 			// By constructing a java.net.URI object, the string representing the URI will be parsed against RFC 2396.
163 			// In case of non compliance a java.net.URISyntaxException will be thrown
164 			final URI uri = new URI(uriString);
165 
166 			// Make sure that the URI contains: [scheme; scheme-specific-part]
167 			final String scheme = uri.getScheme();
168 			if (scheme == null || uri.getRawSchemeSpecificPart() == null)
169 			{
170 				LOG.debug("URI scheme or scheme-specific-part not specified");
171 				return false;
172 			}
173 
174 			// Check whether the URI scheme is supported
175 			if (!isURISchemeValid(scheme.toLowerCase(Validator.getLocaleProvider().getLocale()))) return false;
176 
177 			// If the connect flag is true then attempt to connect to the URL
178 			if (connect) return canConnect(uriString);
179 		}
180 		catch (final java.net.URISyntaxException ex)
181 		{
182 			LOG.debug("URI scheme or scheme-specific-part not specified", ex);
183 			return false;
184 		}
185 
186 		return true;
187 	}
188 
189 	private boolean isURISchemeValid(final String url)
190 	{
191 		for (final URIScheme scheme : permittedURISchemes)
192 			if (url.startsWith(scheme.getScheme())) return true;
193 		return false;
194 	}
195 
196 	/**
197 	 * Specifies if a connection to the URL should be attempted to verify its validity.
198 	 *
199 	 * @param connect the connect to set
200 	 */
201 	public void setConnect(final boolean connect)
202 	{
203 		this.connect = connect;
204 	}
205 
206 	/**
207 	 * Specifies the allowed URL schemes.
208 	 *
209 	 * @param permittedURISchemes the permittedURISchemes to set
210 	 */
211 	public void setPermittedURISchemes(final URIScheme[] permittedURISchemes)
212 	{
213 		this.permittedURISchemes.clear();
214 		ArrayUtils.addAll(this.permittedURISchemes, permittedURISchemes);
215 	}
216 }