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.http.commons.session; 019 020import static org.fcrepo.http.commons.session.TransactionConstants.ATOMIC_ID_HEADER; 021import static org.fcrepo.http.commons.session.TransactionConstants.TX_PREFIX; 022import static org.slf4j.LoggerFactory.getLogger; 023 024import java.net.URI; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028import javax.servlet.http.HttpServletRequest; 029 030import org.apache.commons.lang3.StringUtils; 031import org.fcrepo.kernel.api.Transaction; 032import org.fcrepo.kernel.api.TransactionManager; 033import org.fcrepo.kernel.api.exception.TransactionRuntimeException; 034import org.glassfish.hk2.api.Factory; 035import org.glassfish.jersey.uri.internal.JerseyUriBuilder; 036import org.slf4j.Logger; 037 038/** 039 * Provide a fedora tranasction within the current request context 040 * 041 * @author awoods 042 */ 043public class TransactionProvider implements Factory<Transaction> { 044 045 private static final Logger LOGGER = getLogger(TransactionProvider.class); 046 047 private final TransactionManager txManager; 048 049 private final HttpServletRequest request; 050 051 private final Pattern txIdPattern; 052 053 private final URI baseUri; 054 055 private final String jmsBaseUrl; 056 057 /** 058 * Create a new transaction provider for a request 059 * @param txManager the transaction manager 060 * @param request the request 061 * @param baseUri base uri for the application 062 * @param jmsBaseUrl base url to use for jms, optional 063 */ 064 public TransactionProvider(final TransactionManager txManager, 065 final HttpServletRequest request, 066 final URI baseUri, 067 final String jmsBaseUrl) { 068 this.txManager = txManager; 069 this.request = request; 070 this.txIdPattern = Pattern.compile("(^|" + baseUri + TX_PREFIX + ")([0-9a-f\\-]+)$"); 071 this.baseUri = baseUri; 072 this.jmsBaseUrl = jmsBaseUrl; 073 } 074 075 @Override 076 public Transaction provide() { 077 final Transaction transaction = getTransactionForRequest(); 078 if (!transaction.isShortLived()) { 079 transaction.refresh(); 080 } 081 LOGGER.trace("Providing new transaction {}", transaction.getId()); 082 return transaction; 083 } 084 085 @Override 086 public void dispose(final Transaction transaction) { 087 if (transaction.isShortLived()) { 088 LOGGER.trace("Disposing transaction {}", transaction.getId()); 089 transaction.expire(); 090 } 091 } 092 093 /** 094 * Returns the transaction for the Request. If the request has ATOMIC_ID_HEADER header, 095 * the transaction corresponding to that ID is returned, otherwise, a new transaction is 096 * created. 097 * 098 * @return the transaction for the request 099 */ 100 public Transaction getTransactionForRequest() { 101 String txId = null; 102 // Transaction id either comes from header or is the path 103 String txUri = request.getHeader(ATOMIC_ID_HEADER); 104 if (!StringUtils.isEmpty(txUri)) { 105 final Matcher txMatcher = txIdPattern.matcher(txUri); 106 if (txMatcher.matches()) { 107 txId = txMatcher.group(2); 108 } else { 109 throw new TransactionRuntimeException("Invalid transaction id"); 110 } 111 } else { 112 txUri = request.getPathInfo(); 113 if (txUri != null) { 114 final Matcher txMatcher = txIdPattern.matcher(txUri); 115 if (txMatcher.matches()) { 116 txId = txMatcher.group(2); 117 } 118 } 119 } 120 121 if (!StringUtils.isEmpty(txId)) { 122 return txManager.get(txId); 123 } else { 124 final var transaction = txManager.create(); 125 transaction.setUserAgent(request.getHeader("user-agent")); 126 transaction.setBaseUri(resolveBaseUri()); 127 return transaction; 128 } 129 } 130 131 private String resolveBaseUri() { 132 final String baseURL = getBaseUrlProperty(); 133 if (baseURL.length() > 0) { 134 return baseURL; 135 } 136 return baseUri.toString(); 137 } 138 139 /** 140 * Produce a baseURL for JMS events using the system property fcrepo.jms.baseUrl of the form http[s]://host[:port], 141 * if it exists. 142 * 143 * @return String the base Url 144 */ 145 private String getBaseUrlProperty() { 146 if (StringUtils.isNotBlank(jmsBaseUrl) && jmsBaseUrl.startsWith("http")) { 147 final URI propBaseUri = URI.create(jmsBaseUrl); 148 if (propBaseUri.getPort() < 0) { 149 return JerseyUriBuilder.fromUri(baseUri).port(-1).uri(propBaseUri).toString(); 150 } 151 return JerseyUriBuilder.fromUri(baseUri).uri(propBaseUri).toString(); 152 } 153 return ""; 154 } 155 156}