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