blob: 1ddcb8837f62ffdb358528311bceba25f964390c [file] [log] [blame]
/*
* 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.test.repository;
import static org.apache.ace.test.utils.TestUtils.INTEGRATION;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.apache.ace.http.listener.constants.HttpConstants;
import org.apache.ace.repository.Repository;
import org.apache.ace.repository.impl.constants.RepositoryConstants;
import org.apache.ace.test.constants.TestConstants;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.util.tracker.ServiceTracker;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
public class RepositoryTest {
private static Object m_instance;
private static final int COPY_BUFFER_SIZE = 4096;
private static final String MIME_APPLICATION_OCTET_STREAM = "application/octet-stream";
private static final String HOST = "http://localhost:" + TestConstants.PORT;
private volatile ConfigurationAdmin m_configAdmin; // Injected by dependency manager
private volatile BundleContext m_context; // Injected by dependency manager
public RepositoryTest() {
synchronized (RepositoryTest.class) {
if (m_instance == null) {
m_instance = this;
}
}
}
@Factory
public Object[] createInstances() {
synchronized (RepositoryTest.class) {
return new Object[] { m_instance };
}
}
protected void start() throws IOException {
// configure the (replication)repository servlets
setProperty("org.apache.ace.repository.servlet.RepositoryReplicationServlet", new Object[][] { { HttpConstants.ENDPOINT, "/replication" } });
setProperty("org.apache.ace.repository.servlet.RepositoryServlet", new Object[][] { { HttpConstants.ENDPOINT, "/repository" } });
}
@AfterMethod(alwaysRun = true)
public void tearDown() throws Exception {
// remove all repositories, in case a test case does not reach it's cleanup section due to an exception
removeAllRepositories();
}
private void addRepository(String instanceName, String customer, String name, boolean isMaster) throws IOException, InterruptedException, InvalidSyntaxException {
addRepository(instanceName, customer, name, null, isMaster);
}
/* Configure a new repository instance */
private void addRepository(String instanceName, String customer, String name, String initial, boolean isMaster) throws IOException, InterruptedException, InvalidSyntaxException {
// Publish configuration for a repository instance
Properties props = new Properties();
props.put(RepositoryConstants.REPOSITORY_CUSTOMER, customer);
props.put(RepositoryConstants.REPOSITORY_NAME, name);
props.put(RepositoryConstants.REPOSITORY_MASTER, String.valueOf(isMaster));
if (initial != null) {
props.put(RepositoryConstants.REPOSITORY_INITIAL_CONTENT, initial);
}
props.put("factory.instance.pid", instanceName);
Configuration config = m_configAdmin.createFactoryConfiguration("org.apache.ace.server.repository.factory", null);
ServiceTracker tracker = new ServiceTracker(m_context, m_context.createFilter("(factory.instance.pid=" + instanceName + ")"), null);
tracker.open();
config.update(props);
if (tracker.waitForService(1000) == null) {
throw new IOException("Did not get notified about new repository becoming available in time.");
}
tracker.close();
}
private void removeAllRepositories() throws IOException, InvalidSyntaxException, InterruptedException {
final Configuration[] configs = m_configAdmin.listConfigurations("(factory.instance.pid=*)");
if ((configs != null) && (configs.length > 0)) {
final Semaphore sem = new Semaphore(0);
ServiceTracker tracker = new ServiceTracker(m_context, m_context.createFilter("(" + Constants.OBJECTCLASS + "=" + Repository.class.getName() + ")"), null) {
@Override
public void removedService(ServiceReference reference, Object service) {
super.removedService(reference, service);
// config.length times two because the service tracker also sees added events for each instance
if (size() == 0) {
sem.release();
}
}
};
tracker.open();
for (int i = 0; i < configs.length; i++) {
configs[i].delete();
}
if (!sem.tryAcquire(1, TimeUnit.SECONDS)) {
throw new IOException("Not all instances were removed in time.");
}
tracker.close();
}
}
private void removeRepository(String instanceName) throws IOException, InterruptedException, InvalidSyntaxException {
Configuration[] configs = null;
try {
configs = m_configAdmin.listConfigurations("(factory.instance.pid=" + instanceName + ")");
}
catch (InvalidSyntaxException e) {
// should not happen
}
if ((configs != null) && (configs.length > 0)) {
final Semaphore sem = new Semaphore(0);
ServiceTracker tracker = new ServiceTracker(m_context, m_context.createFilter("(factory.instance.pid=" + instanceName + ")"), null) {
@Override
public void removedService(ServiceReference reference, Object service) {
super.removedService(reference, service);
sem.release();
}
};
tracker.open();
configs[0].delete();
if (!sem.tryAcquire(1, TimeUnit.SECONDS)) {
throw new IOException("Instance did not get removed in time.");
}
}
}
/* Configure properties for the specified service PID */
@SuppressWarnings("unchecked")
private void setProperty(String pid, Object[][] props) throws IOException {
Configuration configuration = m_configAdmin.getConfiguration(pid, null);
Dictionary dictionary = configuration.getProperties();
if (dictionary == null) {
dictionary = new Hashtable();
}
for (Object[] pair : props) {
dictionary.put(pair[0], pair[1]);
}
configuration.update(dictionary);
}
@Test(groups = { INTEGRATION })
public void testCreation() throws Exception {
addRepository("testInstance", "apache", "test", true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int responseCode = query(HOST, "replication/query", null, null, out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
assert "apache,test,\n".equals(out.toString()) : "Expected one repository without any versions as query result instead of : " + out.toString();
addRepository("testInstance2", "apache", "test2", true);
out.reset();
responseCode = query(HOST, "replication/query", null, null, out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
assert "apache,test,\napache,test2,\n".equals(out.toString()) ||
"apache,test2,\napache,test,\n".equals(out.toString()) : "Expected two repositories without any versions as query result instead of : " + out.toString();
removeRepository("testInstance2");
out.reset();
responseCode = query(HOST, "replication/query", null, null, out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
assert "apache,test,\n".equals(out.toString()) : "Expected one repository without any versions as query result instead of : " + out.toString();
removeRepository("testInstance");
out.reset();
responseCode = query(HOST, "replication/query", null, null, out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
assert "".equals(out.toString()) : "Expected one repository without any versions as query result instead of : " + out.toString();
}
@Test(groups = { INTEGRATION })
public void testGetAndPut() throws Exception {
addRepository("testInstance", "apache", "test", true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int responseCode = get(HOST, "replication/get", "apache", "test", "1", out);
assert responseCode == HttpURLConnection.HTTP_NOT_FOUND : "Expected responsecode " + HttpURLConnection.HTTP_NOT_FOUND + " instead of " + responseCode;
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream("test".getBytes());
responseCode = put("replication/put", HOST, "apache", "test", "1", byteArrayInputStream);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
out.reset();
responseCode = get(HOST, "replication/get", "apache", "test", "1", out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
assert "test".equals(out.toString()) : "Expected 'test' as a result of the get operation, not: " + out.toString();
removeRepository("testInstance");
}
@Test(groups = { INTEGRATION })
public void testCheckoutAndCommit() throws Exception {
addRepository("testInstance", "apache", "test", true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int responseCode = get(HOST, "repository/checkout", "apache", "test", "1", out);
assert responseCode == HttpURLConnection.HTTP_NOT_FOUND : "Expected responsecode " + HttpURLConnection.HTTP_NOT_FOUND + " instead of " + responseCode;
ByteArrayInputStream input = new ByteArrayInputStream("test".getBytes());
responseCode = put("repository/commit", HOST, "apache", "test", "1", input);
assert responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR : "Expected responsecode " + HttpURLConnection.HTTP_INTERNAL_ERROR + " instead of " + responseCode;
input.reset();
responseCode = put("repository/commit", HOST, "apache", "test", "0", input);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
out.reset();
responseCode = get(HOST, "repository/checkout", "apache", "test", "1", out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
removeRepository("testInstance");
}
@Test(groups = { INTEGRATION })
public void testMaster() throws Exception {
addRepository("testInstance", "apache", "test", false);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream("test".getBytes());
int responseCode = put("replication/put", HOST, "apache", "test", "1", byteArrayInputStream);
assert responseCode == HttpURLConnection.HTTP_OK;
byteArrayInputStream.reset();
responseCode = put("repository/commit", HOST, "apache", "test", "0", byteArrayInputStream);
assert responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
removeRepository("testInstance");
}
@Test(groups = { INTEGRATION })
public void testBadRequests() throws Exception {
addRepository("testInstance", "apache", "test", false);
URL url = new URL(new URL(HOST), "replication/query?customer=apache&name=test&filter=test");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();
assert responseCode == HttpURLConnection.HTTP_BAD_REQUEST : "Expected responsecode " + HttpURLConnection.HTTP_BAD_REQUEST + " instead of " + responseCode;
url = new URL(new URL(HOST), "repository/query?customer=apache&name=test&filter=test");
connection = (HttpURLConnection) url.openConnection();
responseCode = connection.getResponseCode();
assert responseCode == HttpURLConnection.HTTP_BAD_REQUEST : "Expected responsecode " + HttpURLConnection.HTTP_BAD_REQUEST + " instead of " + responseCode;
removeRepository("testInstance");
}
@Test(groups = { INTEGRATION })
public void testInitialContent() throws Exception {
addRepository("testInstance", "apache", "test", "somecontent", true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int responseCode = get(HOST, "repository/checkout", "apache", "test", "1", out);
assert responseCode == HttpURLConnection.HTTP_OK : "Expected responsecode " + HttpURLConnection.HTTP_OK + " instead of " + responseCode;
assert Arrays.equals(out.toByteArray(), "somecontent".getBytes());
removeRepository("testInstance");
}
private static int query(String host, String endpoint, String customer, String name, OutputStream out) throws MalformedURLException, IOException {
String f1 = (customer == null) ? null : "customer=" + customer;
String f2 = (name == null) ? null : "name=" + name;
String filter = ((f1 == null) ? "?" : "?" + f1 + "&") + ((f2 == null) ? "" : f2);
URL url = new URL(new URL(host), endpoint + filter);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream input = connection.getInputStream();
copy(input, out);
out.flush();
}
return responseCode;
}
private static int get(String host, String endpoint, String customer, String name, String version, OutputStream out) throws MalformedURLException, IOException, FileNotFoundException {
URL url = new URL(new URL(host), endpoint + "?customer=" + customer + "&name=" + name + "&version=" + version);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream input = connection.getInputStream();
copy(input, out);
out.flush();
}
return responseCode;
}
private static int put(String endpoint, String host, String customer, String name, String version, InputStream in) throws MalformedURLException, IOException, FileNotFoundException {
URL url = new URL(new URL(host), endpoint + "?customer=" + customer + "&name=" + name + "&version=" + version);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", MIME_APPLICATION_OCTET_STREAM);
OutputStream out = connection.getOutputStream();
copy(in, out);
out.flush();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream is = (InputStream) connection.getContent();
is.close();
}
return responseCode;
}
/* copy in to out */
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);
}
}
}