You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2013/04/09 10:12:36 UTC
svn commit: r1465922 [2/3] - in /ace/trunk: build/
org.apache.ace.authentication.itest/
org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/
org.apache.ace.client.repository.itest/
org.apache.ace.client.repository.itest/src/org/apa...
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/RepositoryReplication.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ace.range.SortedRangeSet;
+
+/**
+ * The interface for replication of the data in a repository.
+ */
+public interface RepositoryReplication
+{
+ /**
+ * Determines the versions inside the repository.
+ *
+ * @returns A <code>SortedRangeSet</code> representing all the versions currently inside the repository.
+ * @throws java.io.IOException If there is an error determining the current versions.
+ */
+ public SortedRangeSet getRange() throws IOException;
+
+ /**
+ * Gets the specified version.
+ *
+ * @return A stream containing the specified version's data or <code>null</code> if the version does not exist.
+ * @throws java.io.IOException If there is an error reading the version.
+ * @throws IllegalArgumentException If the specified version is not greater than 0.
+ */
+ public InputStream get(long version) throws IOException, IllegalArgumentException;
+
+ /**
+ * Store the stream data as the specified version.
+ *
+ * @return returns True if all went fine, false if the version already existed.
+ * @throws java.io.IOException If the stream data could not be stored successfully due to I/O problems.
+ * @throws IllegalArgumentException If the version number is not greater than 0.
+ */
+ public boolean put(InputStream data, long version) throws IOException, IllegalArgumentException;
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/BackupRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/BackupRepository.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/BackupRepository.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/BackupRepository.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Provides an interface for backing up objects. <code>write</code> and <code>read</code>
+ * allow writing and reading of the current version of the object. <code>backup</code>
+ * backs up the object, and <code>restore</code> restores it from a previously backed up
+ * version, if any. There is no way to directly use the backup.
+ */
+public interface BackupRepository
+{
+
+ /**
+ * Writes the input stream to the current object.
+ * @param data The data to be written. Remember to close this stream, if necessary.
+ * @throws java.io.IOException Will be thrown when (a) the input stream gets closed
+ * unexpectedly, or (b) there is an error writing the data.
+ */
+ public void write(InputStream data) throws IOException;
+
+ /**
+ * Reads the input stream from the current object. If there is no current version,
+ * an empty stream will be returned.
+ * @return An input stream, from which can be read. Remember to close it.
+ * @throws java.io.IOException Will be thrown when there is a problem storing the data.
+ */
+ public InputStream read() throws IOException;
+
+ /**
+ * Restores a previously backuped version of the object.
+ * @return True when there was a previously backup version which has
+ * now been restored, false otherwise.
+ * @throws java.io.IOException Thrown when the restore process goes bad.
+ */
+ public boolean restore() throws IOException;
+
+ /**
+ * Backs up the current version of the object, overwriting a previous
+ * backup, if any.
+ * @return True when there was a current version to be backed up, false
+ * otherwise.
+ * @throws java.io.IOException Thrown when the restore process goes bad.
+ */
+ public boolean backup() throws IOException;
+
+ /**
+ * Deletes the whole repository.
+ *
+ * @throws IOException when the repository could not be deleted.
+ */
+ public void delete() throws IOException;
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/CachedRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/CachedRepository.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/CachedRepository.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/CachedRepository.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.ace.repository.Repository;
+
+/**
+ * Provides a cached repository representation, allowing the storing of local changes, without
+ * committing them to the actual repository immediately.
+ */
+public interface CachedRepository extends Repository {
+
+ /**
+ * Checks our the most current version from the actual repository.
+ * @param fail Indicates that this method should throw an IOException when no data
+ * is available. Setting it to <code>false</code> will make it return an
+ * empty stream in that case.
+ * @return An input stream representing the checked out object.
+ * @throws java.io.IOException Is thrown when the actual repository's commit does.
+ */
+ public InputStream checkout(boolean fail) throws IOException;
+
+ /**
+ * Commits the most current version to the actual repository.
+ * @return true on success, false on failure (e.g. bad version number)
+ * @throws java.io.IOException Is thrown when the actual repository's commit does.
+ */
+ public boolean commit() throws IOException;
+
+ /**
+ * Gets the most recent version of the object. If no current version is available,
+ * and empty stream will be returned.
+ * @param fail Indicates that this method should throw an IOException when no data
+ * is available. Setting it to <code>false</code> will make it return an
+ * empty stream in that case.
+ * @return An input stream representing the most recently written object.
+ * @throws java.io.IOException Thrown when there is a problem retrieving the data.
+ */
+ public InputStream getLocal(boolean fail) throws IOException;
+
+ /**
+ * Writes the most recent version of the object.
+ * @throws java.io.IOException Thrown when there is a problem storing the data.
+ */
+ public void writeLocal(InputStream data) throws IOException;
+
+ /**
+ * Undoes all changes made using <code>writeLocal()</code> since the
+ * last <code>commit</code> or <code>checkout</code>.
+ * @throws java.io.IOException
+ */
+ public boolean revert() throws IOException;
+
+ /**
+ * Gets the most recent version of this repository, that is, the most recent version
+ * number that is either committed (successfully) or checked out.
+ * @return The most recent version of the underlying repository.
+ */
+ public long getMostRecentVersion();
+
+ /**
+ * Checks whether the version we have locally is current with respect to the version
+ * on the server.
+ * @return whether the version we have locally is current with respect to the version
+ * on the server.
+ * @throws java.io.IOException Thrown when an error occurs communicating with the server.
+ */
+ public boolean isCurrent() throws IOException;
+
+ /**
+ * Deletes the local repository.
+ *
+ * @throws IOException when the local repository could not be deleted
+ */
+ public void deleteLocal() throws IOException;
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/CachedRepositoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/CachedRepositoryImpl.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/CachedRepositoryImpl.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/CachedRepositoryImpl.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+/**
+ * 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.
+ */
+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;
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/FilebasedBackupRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/FilebasedBackupRepository.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/FilebasedBackupRepository.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/FilebasedBackupRepository.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,163 @@
+/*
+ * 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.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.ace.repository.ext.BackupRepository;
+
+/**
+ * A file-based implementation of the Backup Repository, using two files to store the current
+ * and backup version.
+ */
+public class FilebasedBackupRepository implements BackupRepository {
+ private static final int COPY_BUFFER_SIZE = 4096;
+
+ private final File m_current;
+ private final File m_backup;
+
+ /**
+ * Creates a FilebasedBackupRepository. The file objects should point to a correct file,
+ * but the files will be created when necessary.
+ * @param current A file to store the current revision in.
+ * @param backup A file to store a backup version in.
+ */
+ public FilebasedBackupRepository(File current, File backup) {
+ m_current = current;
+ m_backup = backup;
+ }
+
+ public InputStream read() throws IOException {
+ if (!m_current.exists()) {
+ return new ByteArrayInputStream(new byte[0]);
+ }
+
+ try {
+ return new FileInputStream(m_current);
+ }
+ catch (FileNotFoundException e) {
+ throw new IOException("Unable to open file: " + e.getMessage(), e);
+ }
+ }
+
+ public void write(InputStream data) throws IOException {
+ try {
+ if (!m_current.exists()) {
+ m_current.createNewFile();
+ }
+ }
+ catch (IOException e) {
+ throw new IOException("Unable to create file: " + e.getMessage(), e);
+ }
+
+ try {
+ FileOutputStream out = new FileOutputStream(m_current);
+ copy(data, out);
+ out.close();
+ }
+ catch (FileNotFoundException e) {
+ throw new IOException("Unable to open file: " + e.getMessage(), e);
+ }
+ }
+
+ public boolean backup() throws IOException {
+ if (!m_current.exists()) {
+ return false;
+ }
+ copy(m_current, m_backup);
+ return true;
+ }
+
+ public boolean restore() throws IOException {
+ if (!m_backup.exists()) {
+ return false;
+ }
+ copy(m_backup, m_current);
+ return true;
+ }
+
+ public void delete() throws IOException {
+ boolean deletedCurrent = true, deletedBackup = true;
+ if (m_current.exists()) {
+ deletedCurrent = m_current.delete();
+ }
+ if (m_backup.exists()) {
+ deletedBackup = m_backup.delete();
+ }
+ if (!(deletedCurrent && deletedBackup)) {
+ throw new IOException("Could not delete: " + (deletedCurrent ? "" : "current ") + (deletedBackup ? "" : "backup"));
+ }
+ }
+
+ /**
+ * Helper function that writes the contents of one file to another.
+ * @param source The source file.
+ * @param destination The destination file.
+ * @throws java.io.IOException Thrown when file IO goes wrong.
+ */
+ private static void copy(File source, File destination) throws IOException {
+ if (destination.exists()) {
+ destination.delete();
+ }
+ destination.createNewFile();
+
+ FileOutputStream out = new FileOutputStream(destination);
+ FileInputStream in = new FileInputStream(source);
+
+ try {
+ copy(in, out);
+ }
+ finally {
+ if (in != null) {
+ in.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+ /**
+ * Copies the contents of an input stream to an output stream.
+ * @param in The input stream.
+ * @param out The output stream.
+ * @throws java.io.IOException Thrown when the output stream is closed unexpectedly.
+ */
+ private static void copy(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ int bytes = in.read(buffer);
+ while (bytes != -1) {
+ out.write(buffer, 0, bytes);
+ out.flush();
+ bytes = in.read(buffer);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "FilebasedBackupRepository[" + m_current + "," + m_backup + "]";
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/RemoteRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/RemoteRepository.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/RemoteRepository.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/RemoteRepository.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,211 @@
+/*
+ * 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.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.ace.repository.Repository;
+
+/**
+ * This class works as a local interface for a remote repository by handling the network
+ * communication.
+ */
+public class RemoteRepository implements Repository {
+ private static final String COMMAND_QUERY = "/query";
+ private static final String COMMAND_CHECKOUT = "/checkout";
+ private static final String COMMAND_COMMIT = "/commit";
+
+ private static final String MIME_APPLICATION_OCTET_STREAM = "application/octet-stream";
+
+ private static final int COPY_BUFFER_SIZE = 64 * 1024;
+
+ private final URL m_url;
+ private final String m_customer;
+ private final String m_name;
+
+ private volatile ConnectionFactory m_connectionFactory;
+
+ /**
+ * Creates a remote repository that connects to a given location with a given customer- and repository name.
+ *
+ * @param url The location of the repository.
+ * @param customer The customer name to use.
+ * @param name The repository name to use.
+ */
+ public RemoteRepository(URL url, String customer, String name) {
+ if (url == null || customer == null || name == null) {
+ throw new IllegalArgumentException("None of the parameters can be null!");
+ }
+
+ m_url = url;
+ m_customer = customer;
+ m_name = name;
+ }
+
+ public InputStream checkout(long version) throws IOException, IllegalArgumentException {
+ if (version <= 0) {
+ throw new IllegalArgumentException("Version must be greater than 0.");
+ }
+
+ URL url = buildCommand(m_url, COMMAND_CHECKOUT, version);
+ HttpURLConnection connection = (HttpURLConnection) m_connectionFactory.createConnection(url);
+
+ if (connection.getResponseCode() == HttpServletResponse.SC_NOT_FOUND) {
+ connection.disconnect();
+ throw new IllegalArgumentException("Requested version not found in remote repository. (" + connection.getResponseMessage() + ")");
+ }
+ if (connection.getResponseCode() != HttpServletResponse.SC_OK) {
+ connection.disconnect();
+ throw new IOException("Connection error: " + connection.getResponseMessage());
+ }
+
+ return connection.getInputStream();
+
+ }
+
+ public boolean commit(InputStream data, long fromVersion) throws IOException, IllegalArgumentException {
+ URL url = buildCommand(m_url, COMMAND_COMMIT, fromVersion);
+ HttpURLConnection connection = (HttpURLConnection) m_connectionFactory.createConnection(url);
+
+ // ACE-294: enable streaming mode causing only small amounts of memory to be
+ // used for this commit. Otherwise, the entire input stream is cached into
+ // memory prior to sending it to the server...
+ connection.setChunkedStreamingMode(8192);
+ connection.setRequestProperty("Content-Type", MIME_APPLICATION_OCTET_STREAM);
+ connection.setDoOutput(true);
+
+ OutputStream out = connection.getOutputStream();
+ try {
+ copy(data, out);
+ } finally {
+ out.flush();
+ out.close();
+ }
+
+ try {
+ return connection.getResponseCode() == HttpServletResponse.SC_OK;
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public SortedRangeSet getRange() throws IOException {
+ URL url = buildCommand(m_url, COMMAND_QUERY, 0);
+
+ HttpURLConnection connection = (HttpURLConnection) m_connectionFactory.createConnection(url);
+
+ try {
+ if (connection.getResponseCode() == HttpServletResponse.SC_OK) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ try {
+ String line = reader.readLine();
+ if (line == null) {
+ throw new IOException("Repository not found: customer=" + m_customer + ", name=" + m_name);
+ }
+
+ String representation = line.substring(line.lastIndexOf(','));
+ return new SortedRangeSet(representation);
+ } finally {
+ reader.close();
+ }
+ }
+
+ throw new IOException("Connection error: " + connection.getResponseMessage());
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ /**
+ * Helper method which copies the contents of an input stream to an output stream.
+ * @param in The input stream.
+ * @param out The output stream.
+ * @throws java.io.IOException Thrown when one of the streams is closed unexpectedly.
+ */
+ private static void copy(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ int bytes = in.read(buffer);
+ while (bytes != -1) {
+ out.write(buffer, 0, bytes);
+ bytes = in.read(buffer);
+ }
+ }
+
+ /**
+ * Builds a command string to use in the request to the server, based on the parameters
+ * this object was created with. The version is only mandatory for <code>CHECKOUT</code>
+ * and <code>COMMIT</code>.
+ *
+ * @param command A command string, use the <code>COMMAND_</code> constants in this file.
+ * @param version A version statement.
+ * @return The command string.
+ */
+ private URL buildCommand(URL url, String command, long version) {
+ StringBuilder params = new StringBuilder();
+
+ if (m_customer != null) {
+ if (params.length() != 0) {
+ params.append("&");
+ }
+ params.append("customer=").append(m_customer);
+ }
+ if (m_name != null) {
+ if (params.length() != 0) {
+ params.append("&");
+ }
+ params.append("name=").append(m_name);
+ }
+ if (command != COMMAND_QUERY) {
+ if (params.length() != 0) {
+ params.append("&");
+ }
+ params.append("version=").append(version);
+ }
+
+ StringBuilder newURL = new StringBuilder();
+ newURL.append(url.toExternalForm());
+ newURL.append(command);
+ if (params.length() > 0) {
+ newURL.append("?").append(params);
+ }
+
+ try {
+ return new URL(newURL.toString());
+ }
+ catch (MalformedURLException e) {
+ throw new IllegalArgumentException("Could not create URL: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "RemoteRepository[" + m_url + "," + m_customer + "," + m_name + "]";
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/packageinfo?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/packageinfo (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/impl/packageinfo Tue Apr 9 08:12:33 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/packageinfo?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/packageinfo (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/ext/packageinfo Tue Apr 9 08:12:33 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/Activator.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/Activator.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/Activator.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,48 @@
+/*
+ * 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.impl;
+
+import java.util.Properties;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.PreferencesService;
+
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, "org.apache.ace.server.repository.factory");
+ manager.add(createComponent()
+ .setInterface(ManagedServiceFactory.class.getName(), props)
+ .setImplementation(new RepositoryFactory(manager))
+ .add(createServiceDependency().setService(PreferencesService.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do here
+ }
+}
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryFactory.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,181 @@
+/*
+ * 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.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.RepositoryReplication;
+import org.apache.ace.repository.impl.constants.RepositoryConstants;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+
+/**
+ * A <code>ManagedServiceFactory</code> responsible for creating a (<code>Replication</code>)<code>Repository</code>
+ * instance for each valid configuration that is received from the <code>ConfigurationAdmin</code>.
+ */
+public class RepositoryFactory implements ManagedServiceFactory {
+
+ private volatile LogService m_log; /* injected by dependency manager */
+ private volatile BundleContext m_context; /* injected by dependency manager */
+ private volatile PreferencesService m_prefsService; /* injected by dependency manager */
+
+ private File m_tempDir;
+ private File m_baseDir;
+ private Preferences m_prefs;
+ private final DependencyManager m_manager;
+ private final Map<String, Component> m_instances = new HashMap<String, Component>();
+
+ public RepositoryFactory(DependencyManager manager) {
+ m_manager = manager;
+ }
+
+ public synchronized void deleted(String pid) {
+ // pull service
+ Component service = m_instances.remove(pid);
+ if (service != null) {
+ m_manager.remove(service);
+ }
+
+ // remove persisted data
+ File dir = new File(m_baseDir, pid);
+ if (dir.isDirectory()) {
+ File[] files = dir.listFiles();
+ for (int i = 0; (files != null) && (i < files.length); i++) {
+ files[i].delete();
+ }
+ if (!dir.delete()) {
+ m_log.log(LogService.LOG_WARNING, "Unable to clean up files ( in " + dir.getAbsolutePath() + ") after removing repository");
+ }
+ }
+ try {
+ m_prefs.node(pid).removeNode();
+ m_prefs.sync();
+ }
+ catch (BackingStoreException e) {
+ // Not much we can do
+ }
+ }
+
+ public String getName() {
+ return "RepositoryFactory";
+ }
+
+ public synchronized void init() {
+ m_tempDir = m_context.getDataFile("tmp");
+ if ((m_tempDir != null) && !m_tempDir.isDirectory() && !m_tempDir.mkdirs()) {
+ throw new IllegalArgumentException("Unable to create temp directory (" + m_tempDir.getAbsolutePath() + ")");
+ }
+ m_baseDir = m_context.getDataFile("repos");
+ if ((m_baseDir != null) && !m_baseDir.isDirectory() && !m_baseDir.mkdirs()) {
+ throw new IllegalArgumentException("Unable to create base directory (" + m_baseDir.getAbsolutePath() + ")");
+ }
+ }
+
+ /**
+ * Creates a new instance if the supplied dictionary contains a valid configuration. A configuration is valid if
+ * <code>RepositoryConstants.REPOSITORY_NAME</code> and <code>RepositoryConstants.REPOSITORY_CUSTOMER</code> properties
+ * are present, not empty and the combination of the two is unique in respect to other previously created instances.
+ * Finally a property <code>RepositoryConstants.REPOSITORY_MASTER</code> should be present and be either <code>true</code>
+ * or <code>false</code>.
+ *
+ * @param pid A unique identifier for the instance, generated by <code>ConfigurationAdmin</code> normally.
+ * @param dict The configuration properties for the instance, see description above.
+ * @throws ConfigurationException If any of the above explanation fails <b>or</b>when there is an internal error creating the repository.
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized void updated(String pid, Dictionary dict) throws ConfigurationException {
+ String name = (String) dict.get(RepositoryConstants.REPOSITORY_NAME);
+ if ((name == null) || "".equals(name)) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_NAME, "Repository name has to be specified.");
+ }
+
+ String customer = (String) dict.get(RepositoryConstants.REPOSITORY_CUSTOMER);
+ if ((customer == null) || "".equals(customer)) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_CUSTOMER, "Repository customer has to be specified.");
+ }
+
+ String master = (String) dict.get(RepositoryConstants.REPOSITORY_MASTER);
+ if (!("false".equalsIgnoreCase(master.trim()) || "true".equalsIgnoreCase(master.trim()))) {
+ throw new ConfigurationException(RepositoryConstants.REPOSITORY_MASTER, "Have to specify whether the repository is the master or a slave.");
+ }
+ boolean isMaster = Boolean.parseBoolean(master);
+
+ String initialContents = (String) dict.get(RepositoryConstants.REPOSITORY_INITIAL_CONTENT);
+
+ if (m_prefs == null) {
+ m_prefs = m_prefsService.getSystemPreferences();
+ }
+
+ String[] nodes;
+ try {
+ nodes = m_prefs.childrenNames();
+ }
+ catch (BackingStoreException e) {
+ throw new ConfigurationException("none", "Internal error while validating configuration.");
+ }
+ for (int i = 0; i < nodes.length; i++) {
+ Preferences node = m_prefs.node(nodes[i]);
+ if (name.equalsIgnoreCase(node.get(RepositoryConstants.REPOSITORY_NAME, "")) && name.equalsIgnoreCase(node.get(RepositoryConstants.REPOSITORY_CUSTOMER, ""))) {
+ throw new ConfigurationException("name and customer", "Name and customer combination already exists");
+ }
+ }
+
+ Preferences node = m_prefs.node(pid);
+ node.put(RepositoryConstants.REPOSITORY_NAME, name);
+ node.put(RepositoryConstants.REPOSITORY_CUSTOMER, customer);
+
+ Component service = m_instances.get(pid);
+ if (service == null) {
+ // new instance
+ File dir = new File(m_baseDir, pid);
+ RepositoryImpl store = new RepositoryImpl(dir, m_tempDir, isMaster);
+ if ((initialContents != null) && isMaster) {
+ try {
+ store.commit(new ByteArrayInputStream(initialContents.getBytes()), 0);
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "Unable to set initial contents of the repository.", e);
+ }
+ }
+ service = m_manager.createComponent()
+ .setInterface(new String[] {RepositoryReplication.class.getName(), Repository.class.getName()}, dict)
+ .setImplementation(store)
+ .add(m_manager.createServiceDependency().setService(LogService.class).setRequired(false));
+ m_manager.add(service);
+ m_instances.put(pid, service);
+ } else {
+ // update existing instance
+ RepositoryImpl store = (RepositoryImpl) service.getService();
+ store.updated(isMaster);
+ }
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/RepositoryImpl.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,296 @@
+/*
+ * 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.impl;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
+
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.RepositoryReplication;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Implementation of an object repository. The object repository holds (big) chunks of data identified by
+ * a version. To interact with the repository two interfaces are implemented:
+ * <ul>
+ * <li><code>Repository</code> - a read-write interface to the repository, you can commit and checkout versions</li>
+ * <li><code>RepositoryReplication</code> - interface used only for replication of the repository, you can get and put versions</li>
+ * </ul>
+ * A repository can be either a master or a slave repository. Committing a new version is only possible on a master repository.
+ */
+public class RepositoryImpl implements RepositoryReplication, Repository {
+
+ private volatile LogService m_log; /* will be injected by dependency manager */
+ private volatile boolean m_isMaster;
+
+ private final File m_tempDir;
+ private final File m_dir;
+
+ /**
+ * Creates a new repository.
+ *
+ * @param dir Directory to be used for storage of the repository data, will be created if needed.
+ * @param temp Directory to be used as temp directory, will be created if needed.
+ * @param isMaster True if this repository is a master repository, false otherwise.
+ * @throws IllegalArgumentException If <code>dir</code> and/or <code>temp</code> could not be created or is not a directory.
+ */
+ public RepositoryImpl(File dir, File temp, boolean isMaster) {
+ m_isMaster = isMaster;
+ if (!dir.isDirectory() && !dir.mkdirs()) {
+ throw new IllegalArgumentException("Repository location is not a valid directory (" + dir.getAbsolutePath() + ")");
+ }
+ if (!temp.isDirectory() && !temp.mkdirs()) {
+ throw new IllegalArgumentException("Temp location is not a valid directory (" + temp.getAbsolutePath() + ")");
+ }
+ m_tempDir = temp;
+ m_dir = dir;
+ }
+
+ public InputStream get(long version) throws IOException, IllegalArgumentException {
+ return checkout(version);
+ }
+
+ public boolean put(InputStream data, long version) throws IOException, IllegalArgumentException {
+ if (version <= 0) {
+ throw new IllegalArgumentException("Version must be greater than 0.");
+ }
+ File file = new File(m_dir, Long.toString(version));
+ if (file.exists()) {
+ return false;
+ }
+
+ // store stream in temp file
+ File tempFile = File.createTempFile("repository", null, m_tempDir);
+ OutputStream fileStream = null;
+ try {
+ fileStream = new FileOutputStream(tempFile);
+
+ byte[] buffer = new byte[1024];
+ int bytes;
+ while ((bytes = data.read(buffer)) >= 0) {
+ fileStream.write(buffer, 0, bytes);
+ }
+ }
+ catch (IOException e) {
+ String deleteMsg = "";
+ if (!tempFile.delete()) {
+ deleteMsg = " and was unable to remove temp file " + tempFile.getAbsolutePath();
+ }
+ m_log.log(LogService.LOG_WARNING, "Error occurred while storing new version in repository" + deleteMsg, e);
+ throw e;
+ }
+ finally {
+ if (fileStream != null) {
+ try {
+ fileStream.close();
+ }
+ catch (IOException ioe) {
+ // Not much we can do
+ }
+ }
+ }
+
+ // move temp file to final location
+ renameFile(tempFile, file);
+
+ return true;
+ }
+
+ public InputStream checkout(long version) throws IOException, IllegalArgumentException {
+ if (version <= 0) {
+ throw new IllegalArgumentException("Version must be greater than 0.");
+ }
+ File file = new File(m_dir, String.valueOf(version));
+ return (file.isFile()) ? new FileInputStream(file) : null;
+ }
+
+ public boolean commit(InputStream data, long fromVersion) throws IOException, IllegalArgumentException {
+ if (!m_isMaster) {
+ throw new IllegalStateException("Commit is only permitted on master repositories");
+ }
+ if (fromVersion < 0) {
+ throw new IllegalArgumentException("Version must be greater than or equal to 0.");
+ }
+
+ long[] versions = getVersions();
+
+ if (versions.length == 0) {
+ if (fromVersion == 0) {
+ put(data, 1);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ long lastVersion = versions[versions.length - 1];
+ if (lastVersion == fromVersion) {
+ put(data, fromVersion + 1);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public SortedRangeSet getRange() throws IOException {
+ return new SortedRangeSet(getVersions());
+ }
+
+ /* returns list of all versions present in ascending order */
+ private long[] getVersions() throws IOException {
+ File[] versions = m_dir.listFiles();
+ if (versions == null) {
+ throw new IOException("Unable to list version in the store (failed to get the filelist for directory '" + m_dir.getAbsolutePath() + "'");
+ }
+ long[] results = new long[versions.length];
+ for (int i = 0; i < versions.length; i++) {
+ String name = versions[i].getName();
+ try {
+ results[i] = Long.parseLong(name);
+ }
+ catch (NumberFormatException nfe) {
+ m_log.log(LogService.LOG_WARNING, "Unable to determine version number for '" + name + "', skipping it.");
+ }
+ }
+ Arrays.sort(results);
+ return results;
+ }
+
+ /**
+ * Updates the repository configuration.
+ *
+ * @param isMaster True if the repository is a master repository, false otherwise.
+ *
+ * @throws ConfigurationException If it was impossible to use the new configuration.
+ */
+ public void updated(boolean isMaster) throws ConfigurationException {
+ m_isMaster = isMaster;
+ }
+
+ /**
+ * Renames a given source file to a new destination file.
+ * <p>
+ * This avoids the problem mentioned in ACE-155.<br/>
+ * The moveFile method from Commons-IO is not used, as it would mean that
+ * we need to include this JAR in several placed for only a few lines of
+ * code.
+ * </p>
+ *
+ * @param source the file to rename;
+ * @param dest the file to rename to.
+ */
+ private void renameFile(File source, File dest) throws IOException {
+ boolean renameOK = false;
+ int attempts = 0;
+ while (!renameOK && (attempts++ < 10)) {
+ try {
+ renameOK = source.renameTo(dest);
+ if (!renameOK) {
+ renameOK = moveFile(source, dest);
+ }
+ }
+ catch (IOException e) {
+ // In all other cases, we assume the source file is still locked and cannot be removed;
+ }
+ }
+
+ if (!renameOK) {
+ if (m_log != null) {
+ m_log.log(LogService.LOG_ERROR, "Unable to move new repository file to it's final location.");
+ }
+ throw new IOException("Could not move temporary file (" + source.getAbsolutePath() + ") to it's final location (" + dest.getAbsolutePath() + ")");
+ }
+ }
+
+ /**
+ * Moves a given source file to a destination location, effectively resulting in a rename.
+ *
+ * @param source the source file to move;
+ * @param dest the destination file to move the file to.
+ * @return <code>true</code> if the move succeeded.
+ * @throws IOException in case of I/O problems.
+ */
+ private boolean moveFile(File source, File dest) throws IOException {
+ final int bufferSize = 1024 * 1024; // 1MB
+
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+ FileChannel input = null;
+ FileChannel output = null;
+
+ try {
+ fis = new FileInputStream(source);
+ input = fis.getChannel();
+
+ fos = new FileOutputStream(dest);
+ output = fos.getChannel();
+
+ long size = input.size();
+ long pos = 0;
+ while (pos < size) {
+ pos += output.transferFrom(input, pos, Math.min(size - pos, bufferSize));
+ }
+ }
+ finally {
+ closeQuietly(fos);
+ closeQuietly(fis);
+ closeQuietly(output);
+ closeQuietly(input);
+ }
+
+ if (source.length() != dest.length()) {
+ throw new IOException("Failed to move file! Not all contents from '" + source + "' copied to '" + dest + "'!");
+ }
+
+ dest.setLastModified(source.lastModified());
+
+ if (!source.delete()) {
+ dest.delete();
+ throw new IOException("Failed to move file! Source file (" + source + ") locked?");
+ }
+
+ return true;
+ }
+
+ /**
+ * Safely closes a given resource, ignoring any I/O exceptions that might occur by this.
+ *
+ * @param resource the resource to close, can be <code>null</code>.
+ */
+ private void closeQuietly(Closeable resource) {
+ try {
+ if (resource != null) {
+ resource.close();
+ }
+ }
+ catch (IOException e) {
+ // Ignored...
+ }
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/impl/constants/RepositoryConstants.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,27 @@
+/*
+ * 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.impl.constants;
+
+public interface RepositoryConstants
+{
+ public static final String REPOSITORY_NAME = "name";
+ public static final String REPOSITORY_CUSTOMER = "customer";
+ public static final String REPOSITORY_MASTER = "master";
+ public static final String REPOSITORY_INITIAL_CONTENT = "initial";
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/packageinfo?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/packageinfo (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/packageinfo Tue Apr 9 08:12:33 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/Activator.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/Activator.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/Activator.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,59 @@
+/*
+ * 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.servlet;
+
+import javax.servlet.Servlet;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public static final String REPOSITORY_PID = "org.apache.ace.repository.servlet.RepositoryServlet";
+ public static final String REPOSITORY_REPLICATION_PID = "org.apache.ace.repository.servlet.RepositoryReplicationServlet";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent()
+ .setInterface(Servlet.class.getName(), null)
+ .setImplementation(RepositoryServlet.class)
+ .add(createConfigurationDependency()
+ .setPropagate(true)
+ .setPid(REPOSITORY_PID))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+
+ manager.add(createComponent()
+ .setInterface(Servlet.class.getName(), null)
+ .setImplementation(RepositoryReplicationServlet.class)
+ .add(createConfigurationDependency()
+ .setPropagate(true)
+ .setPid(REPOSITORY_REPLICATION_PID))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryReplicationServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryReplicationServlet.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryReplicationServlet.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryReplicationServlet.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,82 @@
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.ace.repository.RepositoryReplication;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+
+public class RepositoryReplicationServlet extends RepositoryServletBase {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected ServiceReference[] getRepositories(String filter) throws InvalidSyntaxException {
+ return m_context.getServiceReferences(RepositoryReplication.class.getName(), filter);
+ }
+
+ @Override
+ protected SortedRangeSet getRange(ServiceReference ref) throws IOException {
+ RepositoryReplication repository = (RepositoryReplication) m_context.getService(ref);
+ SortedRangeSet range = repository.getRange();
+ m_context.ungetService(ref);
+ return range;
+ }
+
+ @Override
+ protected boolean doCommit(ServiceReference ref, long version, InputStream data) throws IllegalArgumentException, IOException {
+ RepositoryReplication r = (RepositoryReplication) m_context.getService(ref);
+ boolean result = r.put(data, version);
+ m_context.ungetService(ref);
+ return result;
+ }
+
+ @Override
+ protected InputStream doCheckout(ServiceReference ref, long version) throws IllegalArgumentException, IOException {
+ RepositoryReplication r = (RepositoryReplication) m_context.getService(ref);
+ InputStream result = r.get(version);
+ m_context.ungetService(ref);
+ return result;
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "Apache ACE Repository Replication Servlet";
+ }
+
+ @Override
+ public void updated(Dictionary settings) throws ConfigurationException {
+ super.updated(settings);
+ }
+
+ @Override
+ protected String getCheckoutCommand() {
+ return "/get";
+ }
+
+ @Override
+ protected String getCommitCommand() {
+ return "/put";
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServlet.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServlet.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServlet.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,82 @@
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.ace.repository.Repository;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+
+public class RepositoryServlet extends RepositoryServletBase {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected ServiceReference[] getRepositories(String filter) throws InvalidSyntaxException {
+ return m_context.getServiceReferences(Repository.class.getName(), filter);
+ }
+
+ @Override
+ protected SortedRangeSet getRange(ServiceReference ref) throws IOException {
+ Repository repository = (Repository) m_context.getService(ref);
+ SortedRangeSet range = repository.getRange();
+ m_context.ungetService(ref);
+ return range;
+ }
+
+ @Override
+ protected boolean doCommit(ServiceReference ref, long version, InputStream data) throws IllegalArgumentException, IOException {
+ Repository r = (Repository) m_context.getService(ref);
+ boolean result = r.commit(data, version);
+ m_context.ungetService(ref);
+ return result;
+ }
+
+ @Override
+ protected InputStream doCheckout(ServiceReference ref, long version) throws IllegalArgumentException, IOException {
+ Repository r = (Repository) m_context.getService(ref);
+ InputStream result = r.checkout(version);
+ m_context.ungetService(ref);
+ return result;
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "Apache ACE Repository Servlet";
+ }
+
+ @Override
+ public void updated(Dictionary settings) throws ConfigurationException {
+ super.updated(settings);
+ }
+
+ @Override
+ protected String getCheckoutCommand() {
+ return "/checkout";
+ }
+
+ @Override
+ protected String getCommitCommand() {
+ return "/commit";
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServletBase.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServletBase.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServletBase.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/RepositoryServletBase.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,348 @@
+/*
+ * 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.servlet;
+
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Dictionary;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.authentication.api.AuthenticationService;
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Base class for the repository servlets. Both the repository and the repository replication
+ * servlets work in a similar way, so the specifics were factored out of this base class and
+ * put in two subclasses.
+ */
+public abstract class RepositoryServletBase extends HttpServlet implements ManagedService {
+
+ /** A boolean denoting whether or not authentication is enabled. */
+ private static final String KEY_USE_AUTHENTICATION = "authentication.enabled";
+
+ private static final int COPY_BUFFER_SIZE = 1024;
+
+ private static final String QUERY = "/query";
+
+ protected static final String TEXT_MIMETYPE = "text/plain";
+ protected static final String BINARY_MIMETYPE = "application/octet-stream";
+
+ // injected by Dependency Manager
+ private volatile DependencyManager m_dm;
+ private volatile AuthenticationService m_authService;
+
+ private volatile boolean m_useAuth = false;
+
+ protected volatile BundleContext m_context;
+ protected volatile LogService m_log;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String path = request.getPathInfo();
+ String customer = request.getParameter("customer");
+ String name = request.getParameter("name");
+ String filter = request.getParameter("filter");
+ String version = request.getParameter("version");
+
+ if (QUERY.equals(path)) {
+ // both repositories have a query method
+ if (filter != null) {
+ if ((name == null) && (customer == null)) {
+ handleQuery(filter, response);
+ }
+ else {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Specify either a filter or customer and/or name, but not both.");
+ }
+ }
+ else {
+ if ((name != null) && (customer != null)) {
+ handleQuery("(&(customer=" + customer + ")(name=" + name + "))", response);
+ }
+ else if (name != null) {
+ handleQuery("(name=" + name + ")", response);
+ }
+ else if (customer != null) {
+ handleQuery("(customer=" + customer + ")", response);
+ }
+ else {
+ handleQuery(null, response);
+ }
+ }
+ }
+ else if (getCheckoutCommand().equals(path)) {
+ // and both have a checkout, only it's named differently
+ if ((name != null) && (customer != null) && (version != null)) {
+ handleCheckout(customer, name, Long.parseLong(version), response);
+ }
+ }
+ else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+
+ /**
+ * Returns the name of the "checkout" command.
+ */
+ protected abstract String getCheckoutCommand();
+
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String path = request.getPathInfo();
+ String customer = request.getParameter("customer");
+ String name = request.getParameter("name");
+ String version = request.getParameter("version");
+
+ if (getCommitCommand().equals(path)) {
+ // and finally, both have a commit, only it's named differently
+ if ((name != null) && (customer != null) && (version != null)) {
+ handleCommit(customer, name, Long.parseLong(version), request.getInputStream(), response);
+ }
+ else {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Name, customer and version should all be specified.");
+ }
+ }
+ else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+
+ /**
+ * Returns the name of the "commit" command.
+ */
+ protected abstract String getCommitCommand();
+
+ /**
+ * Handles a query command and sends back the response.
+ */
+ private void handleQuery(String filter, HttpServletResponse response) throws IOException {
+ try {
+ ServiceReference[] refs = getRepositories(filter);
+ StringBuffer result = new StringBuffer();
+ if (refs != null) {
+ for (ServiceReference ref : refs) {
+ result.append((String) ref.getProperty("customer"));
+ result.append(',');
+ result.append((String) ref.getProperty("name"));
+ result.append(',');
+ result.append(getRange(ref).toRepresentation());
+ result.append('\n');
+ }
+ }
+ response.setContentType(TEXT_MIMETYPE);
+ response.getWriter().print(result.toString());
+ }
+ catch (IOException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not retrieve version range for repository: " + e.getMessage());
+ }
+ catch (InvalidSyntaxException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid filter syntax: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Implement this by asking the right repository for a range of available versions.
+ *
+ * @param ref reference to the repository service you need to dereference
+ * @return a sorted range set
+ * @throws java.io.IOException if the range cannot be obtained
+ */
+ protected abstract SortedRangeSet getRange(ServiceReference ref) throws IOException;
+
+ /**
+ * Returns a list of repositories that match the specified filter condition.
+ *
+ * @param filter the filter condition
+ * @return an array of service references
+ * @throws InvalidSyntaxException if the filter condition is invalid
+ */
+ protected abstract ServiceReference[] getRepositories(String filter) throws InvalidSyntaxException;
+
+ /**
+ * Called by Dependency Manager upon initialization of this component.
+ *
+ * @param comp the component to initialize, cannot be <code>null</code>.
+ */
+ protected void init(Component comp) {
+ comp.add(m_dm.createServiceDependency()
+ .setService(AuthenticationService.class)
+ .setRequired(m_useAuth)
+ .setInstanceBound(true)
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (!authenticate(req)) {
+ // Authentication failed; don't proceed with the original request...
+ resp.sendError(SC_UNAUTHORIZED);
+ } else {
+ // Authentication successful, proceed with original request...
+ super.service(req, resp);
+ }
+ }
+
+ /**
+ * Authenticates, if needed the user with the information from the given request.
+ *
+ * @param request the request to obtain the credentials from, cannot be <code>null</code>.
+ * @return <code>true</code> if the authentication was successful, <code>false</code> otherwise.
+ */
+ private boolean authenticate(HttpServletRequest request) {
+ if (m_useAuth) {
+ User user = m_authService.authenticate(request);
+ if (user == null) {
+ m_log.log(LogService.LOG_INFO, "Authentication failure!");
+ }
+ return (user != null);
+ }
+ return true;
+ }
+
+ /**
+ * Handles a commit command and sends back the response.
+ */
+ private void handleCommit(String customer, String name, long version, InputStream data, HttpServletResponse response) throws IOException {
+ try {
+ ServiceReference[] refs = getRepositories("(&(customer=" + customer + ")(name=" + name + "))");
+ if ((refs != null) && (refs.length == 1)) {
+ ServiceReference ref = refs[0];
+ try {
+ if (!doCommit(ref, version, data)) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not commit");
+ } else {
+ response.sendError(HttpServletResponse.SC_OK);
+ }
+ }
+ catch (IllegalArgumentException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid version");
+ }
+ catch (IllegalStateException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Cannot commit, not the master repository");
+ }
+ }
+ }
+ catch (IOException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "I/O exception: " + e.getMessage());
+ }
+ catch (InvalidSyntaxException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid filter syntax: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Commit or put the data into the repository.
+ *
+ * @param ref reference to the repository service
+ * @param version the version
+ * @param data the data
+ * @return <code>true</code> if successful
+ * @throws IllegalArgumentException
+ * @throws java.io.IOException
+ */
+ protected abstract boolean doCommit(ServiceReference ref, long version, InputStream data) throws IllegalArgumentException, IOException;
+
+ /**
+ * Handles a checkout command and returns the response.
+ */
+ private void handleCheckout(String customer, String name, long version, HttpServletResponse response) throws IOException {
+ try {
+ ServiceReference[] refs = getRepositories("(&(customer=" + customer + ")(name=" + name + "))");
+ if ((refs != null) && (refs.length == 1)) {
+ ServiceReference ref = refs[0];
+ response.setContentType(BINARY_MIMETYPE);
+ InputStream data = doCheckout(ref, version);
+ if (data == null) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, "Requested version does not exist: " + version);
+ } else {
+ copy(data, response.getOutputStream());
+ }
+ }
+ else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, ((refs == null) ? "Could not find repository " : "Multiple repositories found ") + " for customer " + customer + ", name " + name);
+ }
+ }
+ catch (IOException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "I/O exception: " + e.getMessage());
+ }
+ catch (InvalidSyntaxException e) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid filter syntax: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Checkout or get data from the repository.
+ *
+ * @param ref reference to the repository service
+ * @param version the version
+ * @return the data
+ * @throws IllegalArgumentException
+ * @throws java.io.IOException
+ */
+ protected abstract InputStream doCheckout(ServiceReference ref, long version) throws IllegalArgumentException, IOException;
+
+ /**
+ * Copies data from an input stream to an output stream.
+ * @param in the input
+ * @param out the output
+ * @throws java.io.IOException if copying fails
+ */
+ private void copy(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ int bytes = in.read(buffer);
+ while (bytes != -1) {
+ out.write(buffer, 0, bytes);
+ bytes = in.read(buffer);
+ }
+ }
+
+ public void updated(Dictionary settings) throws ConfigurationException {
+ if (settings != null) {
+ String useAuthString = (String) settings.get(KEY_USE_AUTHENTICATION);
+ if (useAuthString == null
+ || !("true".equalsIgnoreCase(useAuthString) || "false".equalsIgnoreCase(useAuthString))) {
+ throw new ConfigurationException(KEY_USE_AUTHENTICATION, "Missing or invalid value!");
+ }
+ boolean useAuth = Boolean.parseBoolean(useAuthString);
+
+ m_useAuth = useAuth;
+ }
+ else {
+ m_useAuth = false;
+ }
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/packageinfo?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/packageinfo (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/servlet/packageinfo Tue Apr 9 08:12:33 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java?rev=1465922&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java (added)
+++ ace/trunk/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java Tue Apr 9 08:12:33 2013
@@ -0,0 +1,51 @@
+/*
+ * 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.task;
+
+import java.util.Properties;
+
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.discovery.Discovery;
+import org.apache.ace.scheduler.constants.SchedulerConstants;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put(SchedulerConstants.SCHEDULER_NAME_KEY, RepositoryReplicationTask.class.getName());
+ props.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, "Synchronizes repositories.");
+
+ manager.add(createComponent()
+ .setInterface(Runnable.class.getName(), props)
+ .setImplementation(RepositoryReplicationTask.class)
+ .add(createServiceDependency().setService(Discovery.class).setRequired(true))
+ .add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ );
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ }
+}
\ No newline at end of file