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}