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