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.kernel.modeshape;
019
020import static java.lang.System.currentTimeMillis;
021import static java.util.UUID.randomUUID;
022import static org.fcrepo.kernel.api.Transaction.State.COMMITED;
023import static org.fcrepo.kernel.api.Transaction.State.DIRTY;
024import static org.fcrepo.kernel.api.Transaction.State.NEW;
025import static org.fcrepo.kernel.api.Transaction.State.ROLLED_BACK;
026
027import java.util.Calendar;
028import java.util.Date;
029
030import javax.jcr.RepositoryException;
031import javax.jcr.Session;
032
033import org.fcrepo.kernel.api.Transaction;
034import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
035
036/**
037 * A Fedora Transaction wraps a JCR session with some expiration logic.
038 * Whenever the transaction's session is requested, the expiration is extended
039 *
040 * @author bbpennel
041 */
042public class TransactionImpl implements Transaction {
043
044    // the default timeout is 3 minutes
045    public static final long DEFAULT_TIMEOUT = 3L * 60L * 1000L;
046
047    public static final String TIMEOUT_SYSTEM_PROPERTY = "fcrepo.transactions.timeout";
048
049    private final Session session;
050
051    private final String id;
052
053    private final String userName;
054
055    private final Date created;
056
057    private final Calendar expires;
058
059    private State state = NEW;
060
061    /**
062     * Create a transaction for the given Session
063     * @param session the given session
064     * @param userName the user name
065     */
066
067    public TransactionImpl(final Session session, final String userName) {
068        super();
069        this.session = session;
070        this.created = new Date();
071        this.id = randomUUID().toString();
072        this.expires = Calendar.getInstance();
073        this.updateExpiryDate();
074        this.userName = userName;
075    }
076
077    /* (non-Javadoc)
078     * @see org.fcrepo.kernel.api.Transaction#getSession()
079     */
080    @Override
081    public Session getSession() {
082        updateExpiryDate();
083        return TxAwareSession.newInstance(session, id);
084    }
085
086    /* (non-Javadoc)
087     * @see org.fcrepo.kernel.api.Transaction#getCreated()
088     */
089    @Override
090    public Date getCreated() {
091        return new Date(created.getTime());
092    }
093
094    /* (non-Javadoc)
095     * @see org.fcrepo.kernel.api.Transaction#getId()
096     */
097    @Override
098    public String getId() {
099        return id;
100    }
101
102    /* (non-Javadoc)
103     * @see org.fcrepo.kernel.api.Transaction#getState()
104     */
105    @Override
106    public State getState() {
107        try {
108            if (this.session != null && this.session.hasPendingChanges()) {
109                return DIRTY;
110            }
111            return state;
112        } catch (final RepositoryException e) {
113            throw new RepositoryRuntimeException(e);
114        }
115    }
116
117    /* (non-Javadoc)
118     * @see org.fcrepo.kernel.api.Transaction#getExpires()
119     */
120    @Override
121    public Date getExpires() {
122        return expires.getTime();
123    }
124
125    /* (non-Javadoc)
126     * @see org.fcrepo.kernel.api.Transaction#commit(org.fcrepo.kernel.api.services.VersionService)
127     */
128    @Override
129    public void commit() {
130
131        try {
132            this.session.save();
133            this.state = COMMITED;
134            this.expire();
135        } catch (final RepositoryException e) {
136            throw new RepositoryRuntimeException(e);
137        }
138    }
139
140    /* (non-Javadoc)
141     * @see org.fcrepo.kernel.api.Transaction#expire()
142     */
143    @Override
144    public void expire() {
145        this.session.logout();
146        this.expires.setTimeInMillis(currentTimeMillis());
147    }
148
149    /* (non-Javadoc)
150     * @see org.fcrepo.kernel.api.Transaction#isAssociatedWithUser()
151     */
152    @Override
153    public boolean isAssociatedWithUser(final String userName) {
154        boolean associatedWith = false;
155        if (this.userName == null) {
156            if (userName == null) {
157                associatedWith = true;
158            }
159        } else {
160            associatedWith = this.userName.equals(userName);
161        }
162        return associatedWith;
163    }
164
165    /* (non-Javadoc)
166     * @see org.fcrepo.kernel.api.Transaction#rollback()
167     */
168    @Override
169    public void rollback() {
170        this.state = ROLLED_BACK;
171        try {
172            this.session.refresh(false);
173        } catch (final RepositoryException e) {
174            throw new RepositoryRuntimeException(e);
175        }
176        this.expire();
177    }
178
179    /* (non-Javadoc)
180     * @see org.fcrepo.kernel.api.Transaction#updateExpiryDate()
181     */
182    @Override
183    public void updateExpiryDate() {
184        long duration;
185        if (System.getProperty(TIMEOUT_SYSTEM_PROPERTY) != null) {
186            duration =
187                    Long.parseLong(System.getProperty(TIMEOUT_SYSTEM_PROPERTY));
188        } else {
189            duration = DEFAULT_TIMEOUT;
190        }
191        this.expires.setTimeInMillis(currentTimeMillis() + duration);
192    }
193}