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.camel.integration; 019 020import static org.junit.Assert.assertNotNull; 021 022import java.util.HashMap; 023import java.util.Map; 024 025import org.apache.camel.EndpointInject; 026import org.apache.camel.Exchange; 027import org.apache.camel.Produce; 028import org.apache.camel.ProducerTemplate; 029import org.apache.camel.builder.RouteBuilder; 030import org.apache.camel.builder.xml.Namespaces; 031import org.apache.camel.builder.xml.XPathBuilder; 032import org.apache.camel.component.mock.MockEndpoint; 033import org.apache.camel.impl.JndiRegistry; 034import org.apache.camel.spring.spi.SpringTransactionPolicy; 035import org.apache.camel.test.junit4.CamelTestSupport; 036import org.apache.jena.vocabulary.RDF; 037import org.fcrepo.camel.FcrepoHeaders; 038import org.fcrepo.camel.FcrepoTransactionManager; 039import org.fcrepo.client.FcrepoOperationFailedException; 040import org.junit.Test; 041import org.junit.Before; 042import org.springframework.transaction.TransactionDefinition; 043import org.springframework.transaction.support.TransactionTemplate; 044 045/** 046 * Test adding a new resource with POST 047 * @author Aaron Coburn 048 * @since November 7, 2014 049 */ 050public class FcrepoTransactionIT extends CamelTestSupport { 051 052 private static final String REPOSITORY = "http://fedora.info/definitions/v4/repository#"; 053 054 private TransactionTemplate txTemplate; 055 056 private FcrepoTransactionManager txMgr; 057 058 @EndpointInject(uri = "mock:created") 059 protected MockEndpoint createdEndpoint; 060 061 @EndpointInject(uri = "mock:transactedput") 062 protected MockEndpoint midtransactionEndpoint; 063 064 @EndpointInject(uri = "mock:notfound") 065 protected MockEndpoint notfoundEndpoint; 066 067 @EndpointInject(uri = "mock:verified") 068 protected MockEndpoint verifiedEndpoint; 069 070 @EndpointInject(uri = "mock:transacted") 071 protected MockEndpoint transactedEndpoint; 072 073 @EndpointInject(uri = "mock:rollback") 074 protected MockEndpoint rollbackEndpoint; 075 076 @EndpointInject(uri = "mock:deleted") 077 protected MockEndpoint deletedEndpoint; 078 079 @EndpointInject(uri = "mock:missing") 080 protected MockEndpoint missingEndpoint; 081 082 @Produce(uri = "direct:create") 083 protected ProducerTemplate template; 084 085 @Before 086 public void setUp() throws Exception { 087 super.setUp(); 088 089 txTemplate = new TransactionTemplate(txMgr); 090 txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 091 txTemplate.afterPropertiesSet(); 092 } 093 094 @Override 095 protected JndiRegistry createRegistry() throws Exception { 096 final JndiRegistry reg = super.createRegistry(); 097 098 txMgr = new FcrepoTransactionManager(); 099 txMgr.setBaseUrl(FcrepoTestUtils.getFcrepoBaseUrl()); 100 reg.bind("txManager", txMgr); 101 102 final SpringTransactionPolicy txPolicy = new SpringTransactionPolicy(); 103 txPolicy.setTransactionManager(txMgr); 104 txPolicy.setPropagationBehaviorName("PROPAGATION_REQUIRED"); 105 reg.bind("required", txPolicy); 106 107 return reg; 108 } 109 110 @Test 111 public void testTransaction() throws InterruptedException { 112 // Assertions 113 deletedEndpoint.expectedMessageCount(4); 114 deletedEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 204); 115 116 transactedEndpoint.expectedMessageCount(1); 117 118 verifiedEndpoint.expectedMessageCount(3); 119 120 midtransactionEndpoint.expectedMessageCount(3); 121 midtransactionEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 201); 122 123 notfoundEndpoint.expectedMessageCount(6); 124 notfoundEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 404); 125 126 // Start the transaction 127 final Map<String, Object> headers = new HashMap<>(); 128 headers.put(Exchange.HTTP_METHOD, "POST"); 129 headers.put(Exchange.CONTENT_TYPE, "text/turtle"); 130 131 // Create the object 132 final String fullPath = template.requestBodyAndHeaders( 133 "direct:create", FcrepoTestUtils.getTurtleDocument(), headers, String.class); 134 135 assertNotNull(fullPath); 136 137 final String identifier = fullPath.replaceAll(FcrepoTestUtils.getFcrepoBaseUrl(), ""); 138 139 // Test the creation of several objects 140 template.sendBodyAndHeader("direct:transact", null, "TestIdentifierBase", identifier); 141 142 // Test the object 143 template.sendBodyAndHeader("direct:verify", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/one"); 144 template.sendBodyAndHeader("direct:verify", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/two"); 145 template.sendBodyAndHeader("direct:verify", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/three"); 146 147 // Teardown 148 template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/one"); 149 template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/two"); 150 template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/three"); 151 template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier); 152 153 // Confirm assertions 154 verifiedEndpoint.assertIsSatisfied(); 155 deletedEndpoint.assertIsSatisfied(); 156 transactedEndpoint.assertIsSatisfied(); 157 notfoundEndpoint.assertIsSatisfied(); 158 midtransactionEndpoint.assertIsSatisfied(); 159 } 160 161 @Test 162 public void testTransactionWithRollback() throws InterruptedException { 163 // Assertions 164 deletedEndpoint.expectedMessageCount(1); 165 deletedEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 204); 166 167 transactedEndpoint.expectedMessageCount(0); 168 169 verifiedEndpoint.expectedMessageCount(0); 170 171 midtransactionEndpoint.expectedMessageCount(2); 172 midtransactionEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 201); 173 174 notfoundEndpoint.expectedMessageCount(3); 175 notfoundEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 404); 176 177 // Start the transaction 178 final Map<String, Object> headers = new HashMap<>(); 179 headers.put(Exchange.HTTP_METHOD, "POST"); 180 headers.put(Exchange.CONTENT_TYPE, "text/turtle"); 181 182 // Create the object 183 final String fullPath = template.requestBodyAndHeaders( 184 "direct:create", FcrepoTestUtils.getTurtleDocument(), headers, String.class); 185 186 assertNotNull(fullPath); 187 188 final String identifier = fullPath.replaceAll(FcrepoTestUtils.getFcrepoBaseUrl(), ""); 189 190 // Test the creation of several objects 191 template.sendBodyAndHeader("direct:transactWithError", null, "TestIdentifierBase", identifier); 192 193 // Test the object 194 template.sendBodyAndHeader("direct:verifyMissing", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/one"); 195 template.sendBodyAndHeader("direct:verifyMissing", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/two"); 196 197 // Teardown 198 template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier); 199 200 // Confirm assertions 201 verifiedEndpoint.assertIsSatisfied(); 202 deletedEndpoint.assertIsSatisfied(); 203 transactedEndpoint.assertIsSatisfied(); 204 notfoundEndpoint.assertIsSatisfied(); 205 midtransactionEndpoint.assertIsSatisfied(); 206 } 207 208 @Override 209 protected RouteBuilder createRouteBuilder() { 210 return new RouteBuilder() { 211 @Override 212 public void configure() { 213 final String fcrepo_uri = FcrepoTestUtils.getFcrepoEndpointUri(); 214 final String http4_uri = fcrepo_uri.replaceAll("fcrepo:", "http4:"); 215 216 final Namespaces ns = new Namespaces("rdf", RDF.uri); 217 218 final XPathBuilder titleXpath = new XPathBuilder("/rdf:RDF/rdf:Description/dc:title/text()"); 219 titleXpath.namespaces(ns); 220 titleXpath.namespace("dc", "http://purl.org/dc/elements/1.1/"); 221 222 onException(FcrepoOperationFailedException.class) 223 .handled(true) 224 .to("mock:missing"); 225 226 from("direct:create") 227 .to(fcrepo_uri) 228 .to("mock:created"); 229 230 from("direct:transactWithError") 231 .transacted("required") 232 .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/one") 233 .setHeader(Exchange.HTTP_METHOD).constant("PUT") 234 .to(fcrepo_uri) 235 .to("mock:transactedput") 236 237 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one") 238 .setHeader(Exchange.HTTP_METHOD).constant("GET") 239 .to(http4_uri + "?throwExceptionOnFailure=false") 240 .to("mock:notfound") 241 242 .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/two") 243 .setHeader(Exchange.HTTP_METHOD).constant("PUT") 244 .to(fcrepo_uri) 245 .to("mock:transactedput") 246 247 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one") 248 .setHeader(Exchange.HTTP_METHOD).constant("GET") 249 .to(http4_uri + "?throwExceptionOnFailure=false") 250 .to("mock:notfound") 251 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/two") 252 .setHeader(Exchange.HTTP_METHOD).constant("GET") 253 .to(http4_uri + "?throwExceptionOnFailure=false") 254 .to("mock:notfound") 255 256 // this should throw an error 257 .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/foo/") 258 .setHeader(Exchange.HTTP_METHOD).constant("POST") 259 .to(fcrepo_uri) 260 .to("mock:transactedput") 261 262 // this should never be reached 263 .to("mock:transacted"); 264 265 from("direct:transact") 266 .transacted("required") 267 .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/one") 268 .setHeader(Exchange.HTTP_METHOD).constant("PUT") 269 .to(fcrepo_uri) 270 .to("mock:transactedput") 271 272 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one") 273 .setHeader(Exchange.HTTP_METHOD).constant("GET") 274 .to(http4_uri + "?throwExceptionOnFailure=false") 275 .to("mock:notfound") 276 277 .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/two") 278 .setHeader(Exchange.HTTP_METHOD).constant("PUT") 279 .to(fcrepo_uri) 280 .to("mock:transactedput") 281 282 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one") 283 .setHeader(Exchange.HTTP_METHOD).constant("GET") 284 .to(http4_uri + "?throwExceptionOnFailure=false") 285 .to("mock:notfound") 286 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/two") 287 .setHeader(Exchange.HTTP_METHOD).constant("GET") 288 .to(http4_uri + "?throwExceptionOnFailure=false") 289 .to("mock:notfound") 290 291 .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/three") 292 .setHeader(Exchange.HTTP_METHOD).constant("PUT") 293 .to(fcrepo_uri) 294 .to("mock:transactedput") 295 296 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one") 297 .setHeader(Exchange.HTTP_METHOD).constant("GET") 298 .to(http4_uri + "?throwExceptionOnFailure=false") 299 .to("mock:notfound") 300 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/two") 301 .setHeader(Exchange.HTTP_METHOD).constant("GET") 302 .to(http4_uri + "?throwExceptionOnFailure=false") 303 .to("mock:notfound") 304 305 .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/three") 306 .setHeader(Exchange.HTTP_METHOD).constant("GET") 307 .to(http4_uri + "?throwExceptionOnFailure=false") 308 .to("mock:notfound") 309 310 .to("mock:transacted"); 311 312 from("direct:verify") 313 .to(fcrepo_uri) 314 .filter().xpath( 315 "/rdf:RDF/rdf:Description/rdf:type" + 316 "[@rdf:resource='" + REPOSITORY + "Resource']", ns) 317 .to("mock:verified"); 318 319 from("direct:verifyMissing") 320 .to(fcrepo_uri + "?throwExceptionOnFailure=false") 321 .filter(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo("404")) 322 .to("mock:notfound"); 323 324 from("direct:teardown") 325 .setHeader(Exchange.HTTP_METHOD).constant("DELETE") 326 .to(fcrepo_uri) 327 .to("mock:deleted"); 328 } 329 }; 330 } 331}