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