| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.ace.repository.ext.impl; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| import org.apache.ace.range.RangeIterator; |
| import org.apache.ace.range.SortedRangeSet; |
| import org.apache.ace.repository.Repository; |
| import org.apache.ace.repository.ext.BackupRepository; |
| import org.apache.ace.repository.ext.CachedRepository; |
| |
| import aQute.bnd.annotation.ConsumerType; |
| |
| /** |
| * Provides a CachedRepository, which uses either a <code>Repository</code> and a <code>BackupRepository</code> |
| * as remote and local storage, or a URL location and two files, from which it will create a <code>Repository</code> |
| * and a <code>FileBasedBackupRepository</code>. Note that this class is not thread-safe, and should be synchronized |
| * by the caller. |
| */ |
| @ConsumerType |
| public class CachedRepositoryImpl implements CachedRepository { |
| public static final long UNCOMMITTED_VERSION = -1; |
| |
| private volatile long m_mostRecentVersion; |
| |
| private final BackupRepository m_local; |
| private final Repository m_remote; |
| |
| /** |
| * Creates a cached repository using. |
| * |
| * @param remote A repository which holds committed versions. |
| * @param backup A backup repository for local changes. |
| * @param mostRecentVersion The version from which <code>backup</code> was checked out or committed. |
| * If no version has been committed yet, use <code>UNCOMMITTED_VERSION</code>. |
| */ |
| public CachedRepositoryImpl(Repository remote, BackupRepository backup, long mostRecentVersion) { |
| m_remote = remote; |
| m_local = backup; |
| m_mostRecentVersion = mostRecentVersion; |
| } |
| |
| public InputStream checkout(boolean fail) throws IOException, IllegalArgumentException { |
| m_mostRecentVersion = highestRemoteVersion(); |
| if (m_mostRecentVersion == 0) { |
| // If there is no remote version, then simply return an empty stream. |
| if (fail) { |
| throw new IOException("No version has yet been checked in to the repository."); |
| } |
| else { |
| return new ByteArrayInputStream(new byte[0]); |
| } |
| } |
| return checkout(m_mostRecentVersion); |
| } |
| |
| public InputStream checkout(long version) throws IOException, IllegalArgumentException { |
| m_local.write(m_remote.checkout(version)); |
| m_local.backup(); |
| |
| m_mostRecentVersion = version; |
| |
| return m_local.read(); |
| } |
| |
| public boolean commit(InputStream data, long fromVersion) throws IOException, IllegalArgumentException { |
| m_local.write(data); |
| |
| return commit(fromVersion); |
| } |
| |
| public boolean commit() throws IOException { |
| if (m_mostRecentVersion < 0) { |
| throw new IllegalStateException("A commit should be preceded by a checkout."); |
| } |
| return commit(m_mostRecentVersion++); |
| } |
| |
| public boolean commit(long fromVersion) throws IOException, IllegalArgumentException { |
| boolean success = m_remote.commit(m_local.read(), fromVersion); |
| if (success) { |
| m_local.backup(); |
| } |
| |
| return success; |
| } |
| |
| public SortedRangeSet getRange() throws IOException { |
| return m_remote.getRange(); |
| } |
| |
| public InputStream getLocal(boolean fail) throws IllegalArgumentException, IOException { |
| // ACE-240: only fail in case there is no local version available; when mostRecentVersion |
| // equals to 0, it means that nothing has been committed locally... |
| if ((m_mostRecentVersion <= 0) && fail) { |
| throw new IOException("No local version available of " + m_local + ", remote " + m_remote); |
| } |
| return m_local.read(); |
| } |
| |
| public boolean revert() throws IOException { |
| return m_local.restore(); |
| } |
| |
| public void writeLocal(InputStream data) throws IllegalArgumentException, IOException { |
| m_local.write(data); |
| } |
| |
| public long getMostRecentVersion() { |
| return m_mostRecentVersion; |
| } |
| |
| public boolean isCurrent() throws IOException { |
| return highestRemoteVersion() == m_mostRecentVersion; |
| } |
| |
| public void deleteLocal() throws IOException { |
| m_local.delete(); |
| } |
| |
| private long highestRemoteVersion() throws IOException { |
| long result = 0; |
| RangeIterator ri = getRange().iterator(); |
| while (ri.hasNext()) { |
| result = ri.next(); |
| } |
| return result; |
| } |
| } |