001 /******************************************************************************* 002 * Portions created by Sebastian Thomschke are copyright (c) 2005-2013 Sebastian 003 * Thomschke. 004 * 005 * All Rights Reserved. This program and the accompanying materials 006 * are made available under the terms of the Eclipse Public License v1.0 007 * which accompanies this distribution, and is available at 008 * http://www.eclipse.org/legal/epl-v10.html 009 * 010 * Contributors: 011 * Sebastian Thomschke - initial implementation. 012 * Makkari - live connect support. 013 *******************************************************************************/ 014 package net.sf.oval.constraint; 015 016 import static net.sf.oval.Validator.getCollectionFactory; 017 018 import java.io.IOException; 019 import java.net.HttpURLConnection; 020 import java.net.URI; 021 import java.net.URL; 022 import java.net.URLConnection; 023 import java.util.List; 024 import java.util.Locale; 025 026 import net.sf.oval.ConstraintTarget; 027 import net.sf.oval.Validator; 028 import net.sf.oval.configuration.annotation.AbstractAnnotationCheck; 029 import net.sf.oval.context.OValContext; 030 import net.sf.oval.internal.Log; 031 import net.sf.oval.internal.util.ArrayUtils; 032 033 /** 034 * @author Sebastian Thomschke 035 */ 036 public class AssertURLCheck extends AbstractAnnotationCheck<AssertURL> 037 { 038 /** 039 * http://en.wikipedia.org/wiki/URI_scheme 040 * 041 * @author Sebastian Thomschke 042 * 043 */ 044 public static enum URIScheme 045 { 046 FTP("ftp"), 047 HTTP("http"), 048 HTTPS("https"); 049 050 private final String scheme; 051 052 private URIScheme(final String scheme) 053 { 054 this.scheme = scheme; 055 } 056 057 /** 058 * @return the scheme 059 */ 060 public String getScheme() 061 { 062 return scheme; 063 } 064 065 @Override 066 public String toString() 067 { 068 return scheme; 069 } 070 } 071 072 private static final long serialVersionUID = 1L; 073 074 private static final Log LOG = Log.getLog(AssertURLCheck.class); 075 076 private static boolean canConnect(final String url) 077 { 078 try 079 { 080 final URL theURL = new URL(url); 081 final URLConnection conn = theURL.openConnection(); 082 conn.connect(); 083 conn.getInputStream().close(); 084 if (conn instanceof HttpURLConnection) 085 { 086 final HttpURLConnection httpConnection = (HttpURLConnection) conn; 087 final int rc = httpConnection.getResponseCode(); 088 089 if (rc < HttpURLConnection.HTTP_BAD_REQUEST) return true; 090 LOG.debug("Connecting failed with HTTP response code " + rc); 091 return false; 092 } 093 return true; 094 } 095 catch (final IOException ex) 096 { 097 LOG.debug("Connecting failed with exception", ex); 098 return false; 099 } 100 } 101 102 /** 103 * Specifies if a connection to the URL should be attempted to verify its validity. 104 */ 105 private boolean connect = false; 106 107 /** 108 * Specifies the allowed URL schemes. 109 */ 110 private final List<URIScheme> permittedURISchemes = getCollectionFactory().createList(2); 111 112 /** 113 * {@inheritDoc} 114 */ 115 @Override 116 public void configure(final AssertURL constraintAnnotation) 117 { 118 super.configure(constraintAnnotation); 119 setConnect(constraintAnnotation.connect()); 120 setPermittedURISchemes(constraintAnnotation.permittedURISchemes()); 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 protected ConstraintTarget[] getAppliesToDefault() 128 { 129 return new ConstraintTarget[]{ConstraintTarget.VALUES}; 130 } 131 132 /** 133 * Gets the allowed URL schemes. 134 * @return the permittedURISchemes 135 */ 136 public URIScheme[] getPermittedURISchemes() 137 { 138 return permittedURISchemes.size() == 0 ? null : permittedURISchemes.toArray(new URIScheme[permittedURISchemes.size()]); 139 } 140 141 /** 142 * Specifies if a connection to the URL should be attempted to verify its validity. 143 * 144 * @return the connect 145 */ 146 public boolean isConnect() 147 { 148 return connect; 149 } 150 151 /** 152 * {@inheritDoc} 153 */ 154 public boolean isSatisfied(final Object validatedObject, final Object valueToValidate, final OValContext context, 155 final Validator validator) 156 { 157 if (valueToValidate == null) return true; 158 159 final String uriString = valueToValidate.toString(); 160 161 try 162 { 163 // By constructing a java.net.URI object, the string representing the URI will be parsed against RFC 2396. 164 // In case of non compliance a java.net.URISyntaxException will be thrown 165 final URI uri = new URI(uriString); 166 167 // Make sure that the URI contains: [scheme; scheme-specific-part] 168 final String scheme = uri.getScheme(); 169 if (scheme == null || uri.getRawSchemeSpecificPart() == null) 170 { 171 LOG.debug("URI scheme or scheme-specific-part not specified"); 172 return false; 173 } 174 175 // Check whether the URI scheme is supported 176 if (!isURISchemeValid(scheme.toLowerCase(Locale.getDefault()))) return false; 177 178 // If the connect flag is true then attempt to connect to the URL 179 if (connect) return canConnect(uriString); 180 } 181 catch (final java.net.URISyntaxException ex) 182 { 183 LOG.debug("URI scheme or scheme-specific-part not specified"); 184 return false; 185 } 186 187 return true; 188 } 189 190 private boolean isURISchemeValid(final String url) 191 { 192 for (final URIScheme scheme : permittedURISchemes) 193 if (url.startsWith(scheme.getScheme())) return true; 194 return false; 195 } 196 197 /** 198 * Specifies if a connection to the URL should be attempted to verify its validity. 199 * 200 * @param connect the connect to set 201 */ 202 public void setConnect(final boolean connect) 203 { 204 this.connect = connect; 205 } 206 207 /** 208 * Specifies the allowed URL schemes. 209 * 210 * @param permittedURISchemes the permittedURISchemes to set 211 */ 212 public void setPermittedURISchemes(final URIScheme[] permittedURISchemes) 213 { 214 this.permittedURISchemes.clear(); 215 ArrayUtils.addAll(this.permittedURISchemes, permittedURISchemes); 216 } 217 }