001/** 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.integration; 017 018import static java.lang.Integer.MAX_VALUE; 019import static java.lang.Integer.parseInt; 020import static javax.ws.rs.core.Response.Status.CREATED; 021import static javax.ws.rs.core.Response.Status.NO_CONTENT; 022import static javax.ws.rs.core.Response.Status.OK; 023import static org.fcrepo.http.commons.test.util.TestHelpers.parseTriples; 024import static org.junit.Assert.assertEquals; 025import static org.slf4j.LoggerFactory.getLogger; 026 027import java.io.ByteArrayInputStream; 028import java.io.IOException; 029import java.io.UnsupportedEncodingException; 030import java.util.UUID; 031 032import com.hp.hpl.jena.update.GraphStore; 033import org.apache.http.HttpHost; 034import org.apache.http.HttpResponse; 035import org.apache.http.auth.AuthScope; 036import org.apache.http.auth.UsernamePasswordCredentials; 037import org.apache.http.client.AuthCache; 038import org.apache.http.client.ClientProtocolException; 039import org.apache.http.client.CredentialsProvider; 040import org.apache.http.client.HttpClient; 041import org.apache.http.client.methods.CloseableHttpResponse; 042import org.apache.http.client.methods.HttpUriRequest; 043import org.apache.http.client.methods.HttpPost; 044import org.apache.http.client.methods.HttpPut; 045import org.apache.http.client.methods.HttpPatch; 046import org.apache.http.client.protocol.HttpClientContext; 047import org.apache.http.entity.BasicHttpEntity; 048import org.apache.http.entity.StringEntity; 049import org.apache.http.impl.auth.BasicScheme; 050 051import org.apache.http.impl.client.BasicAuthCache; 052import org.apache.http.impl.client.HttpClientBuilder; 053import org.apache.http.impl.client.BasicCredentialsProvider; 054import org.apache.http.impl.client.CloseableHttpClient; 055import org.apache.http.impl.client.HttpClients; 056 057import org.apache.http.util.EntityUtils; 058import org.junit.Before; 059import org.slf4j.Logger; 060 061/** 062 * Base class for ITs 063 * @author awoods 064 * @author escowles 065**/ 066public abstract class AbstractResourceIT { 067 068 protected Logger logger; 069 070 @Before 071 public void setLogger() { 072 logger = getLogger(this.getClass()); 073 } 074 075 protected static final int SERVER_PORT = parseInt(System.getProperty( 076 "fcrepo.dynamic.test.port", "8080")); 077 078 private static final String CONTEXT_PATH = System 079 .getProperty("fcrepo.test.context.path"); 080 081 protected static final String HOSTNAME = "localhost"; 082 083 protected static final String PROTOCOL = "http"; 084 085 protected static final String serverAddress = PROTOCOL + "://" + HOSTNAME + ":" + 086 SERVER_PORT + CONTEXT_PATH + "rest/"; 087 088 protected static HttpClient client = createClient(); 089 090 protected static HttpClient createClient() { 091 return HttpClientBuilder.create().setMaxConnPerRoute(MAX_VALUE) 092 .setMaxConnTotal(MAX_VALUE).build(); 093 } 094 095 protected static HttpPost postObjMethod(final String pid) { 096 return new HttpPost(serverAddress + pid); 097 } 098 099 protected static HttpPut putObjMethod(final String pid) { 100 return new HttpPut(serverAddress + pid); 101 } 102 103 protected static HttpPost postObjMethod(final String pid, final String query) { 104 if (query.equals("")) { 105 return new HttpPost(serverAddress + pid); 106 } 107 return new HttpPost(serverAddress + pid + "?" + query); 108 } 109 110 protected static HttpPost postDSMethod(final String pid, final String ds, 111 final String content) throws UnsupportedEncodingException { 112 final HttpPost post = 113 new HttpPost(serverAddress + pid + "/" + ds + 114 "/fcr:content"); 115 post.setEntity(new StringEntity(content)); 116 return post; 117 } 118 119 protected static HttpPut putDSMethod(final String pid, final String ds, 120 final String content) throws UnsupportedEncodingException { 121 final HttpPut put = 122 new HttpPut(serverAddress + pid + "/" + ds + 123 "/fcr:content"); 124 125 put.setEntity(new StringEntity(content)); 126 return put; 127 } 128 129 protected HttpResponse execute(final HttpUriRequest method) 130 throws ClientProtocolException, IOException { 131 logger.debug("Executing: " + method.getMethod() + " to " + 132 method.getURI()); 133 return client.execute(method); 134 } 135 136 // Executes requests with preemptive basic authentication 137 protected HttpResponse executeWithBasicAuth(final HttpUriRequest request, 138 final String username, 139 final String password) 140 throws IOException { 141 final HttpHost target = new HttpHost(HOSTNAME, SERVER_PORT, PROTOCOL); 142 final CredentialsProvider credsProvider = new BasicCredentialsProvider(); 143 credsProvider.setCredentials( 144 new AuthScope(target.getHostName(), target.getPort()), 145 new UsernamePasswordCredentials(username, password)); 146 try (final CloseableHttpClient httpclient = HttpClients.custom() 147 .setDefaultCredentialsProvider(credsProvider).build()) { 148 149 final AuthCache authCache = new BasicAuthCache(); 150 final BasicScheme basicAuth = new BasicScheme(); 151 authCache.put(target, basicAuth); 152 153 final HttpClientContext localContext = HttpClientContext.create(); 154 localContext.setAuthCache(authCache); 155 156 final CloseableHttpResponse response = httpclient.execute(request, localContext); 157 return response; 158 } 159 } 160 161 162 protected int getStatus(final HttpUriRequest method) 163 throws ClientProtocolException, IOException { 164 final HttpResponse response = execute(method); 165 final int result = response.getStatusLine().getStatusCode(); 166 if (!(result > 199) || !(result < 400)) { 167 logger.warn(EntityUtils.toString(response.getEntity())); 168 } 169 return result; 170 } 171 172 protected String getContentType(final HttpUriRequest method) 173 throws ClientProtocolException, IOException { 174 final HttpResponse response = execute(method); 175 final int result = response.getStatusLine().getStatusCode(); 176 assertEquals(OK.getStatusCode(), result); 177 return response.getFirstHeader("Content-Type").getValue(); 178 } 179 180 protected GraphStore getGraphStore(final HttpClient client, final HttpUriRequest method) throws IOException { 181 182 if (method.getFirstHeader("Accept") == null) { 183 method.addHeader("Accept", "application/n-triples"); 184 } else { 185 logger.debug("Retrieving RDF in mimeType: {}", method 186 .getFirstHeader("Accept")); 187 } 188 189 final HttpResponse response = client.execute(method); 190 assertEquals(OK.getStatusCode(), response.getStatusLine() 191 .getStatusCode()); 192 final GraphStore result = parseTriples(response.getEntity()); 193 logger.trace("Retrieved RDF: {}", result); 194 return result; 195 196 } 197 protected GraphStore getGraphStore(final HttpResponse response) throws IOException { 198 assertEquals(OK.getStatusCode(), response.getStatusLine().getStatusCode()); 199 final GraphStore result = parseTriples(response.getEntity()); 200 logger.trace("Retrieved RDF: {}", result); 201 return result; 202 } 203 204 protected GraphStore getGraphStore(final HttpUriRequest method) throws IOException { 205 return getGraphStore(client, method); 206 } 207 208 protected HttpResponse createObject(final String pid) throws IOException { 209 final HttpPost httpPost = postObjMethod("/"); 210 if (pid.length() > 0) { 211 httpPost.addHeader("Slug", pid); 212 } 213 final HttpResponse response = client.execute(httpPost); 214 assertEquals(CREATED.getStatusCode(), response.getStatusLine().getStatusCode()); 215 return response; 216 } 217 218 protected HttpResponse createDatastream(final String pid, final String dsid, final String content) 219 throws IOException { 220 logger.trace( 221 "Attempting to create datastream for object: {} at datastream ID: {}", 222 pid, dsid); 223 final HttpResponse response = 224 client.execute(postDSMethod(pid, dsid, content)); 225 assertEquals(CREATED.getStatusCode(), response.getStatusLine().getStatusCode()); 226 return response; 227 } 228 229 protected HttpResponse setProperty(final String pid, 230 final String propertyUri, 231 final String value) throws IOException { 232 return setProperty(pid, null, propertyUri, value); 233 } 234 235 protected HttpResponse setProperty(final String pid, final String txId, 236 final String propertyUri, 237 final String value) throws IOException { 238 final HttpPatch postProp = new HttpPatch(serverAddress 239 + (txId != null ? txId + "/" : "") + pid); 240 postProp.setHeader("Content-Type", "application/sparql-update"); 241 final String updateString = 242 "INSERT { <" 243 + serverAddress + pid 244 + "> <" + propertyUri + "> \"" + value + "\" } WHERE { }"; 245 postProp.setEntity(new StringEntity(updateString)); 246 final HttpResponse dcResp = execute(postProp); 247 assertEquals(dcResp.getStatusLine().toString(), 248 204, dcResp.getStatusLine().getStatusCode()); 249 postProp.releaseConnection(); 250 return dcResp; 251 } 252 253 protected static void addMixin(final String pid, final String mixinUrl) throws IOException { 254 final HttpPatch updateObjectGraphMethod = 255 new HttpPatch(serverAddress + pid); 256 updateObjectGraphMethod.addHeader("Content-Type", 257 "application/sparql-update"); 258 final BasicHttpEntity e = new BasicHttpEntity(); 259 260 e.setContent(new ByteArrayInputStream( 261 ("INSERT DATA { <> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <" + mixinUrl + "> . } ") 262 .getBytes())); 263 updateObjectGraphMethod.setEntity(e); 264 final HttpResponse response = client.execute(updateObjectGraphMethod); 265 assertEquals(NO_CONTENT.getStatusCode(), response.getStatusLine() 266 .getStatusCode()); 267 } 268 269 /** 270 * Gets a random (but valid) pid for use in testing. This pid 271 * is guaranteed to be unique within runs of this application. 272 * 273 * @return a random UUID 274 */ 275 protected static String getRandomUniquePid() { 276 return UUID.randomUUID().toString(); 277 } 278 279 /** 280 * Gets a random (but valid) property name for use in testing. 281 * 282 * @return a random property name 283 */ 284 protected static String getRandomPropertyName() { 285 return UUID.randomUUID().toString(); 286 } 287 288 /** 289 * Gets a random (but valid) property value for use in testing. 290 * 291 * @return a random property value 292 */ 293 protected static String getRandomPropertyValue() { 294 return UUID.randomUUID().toString(); 295 } 296 297 298}