/*
* Copyright 2011 Martin Grotzke
*
* Licensed 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 de.javakaffee.web.msm;
import static de.javakaffee.web.msm.integration.TestUtils.createSession;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import de.javakaffee.web.msm.storage.MemcachedStorageClient;
import de.javakaffee.web.msm.storage.StorageClient;
import net.spy.memcached.MemcachedClient;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.couchbase.mock.CouchbaseMock;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.couchbase.client.CouchbaseClient;
import de.javakaffee.web.msm.BackupSessionTask.BackupResult;
import de.javakaffee.web.msm.integration.TestUtils;
import de.javakaffee.web.msm.integration.TomcatBuilder;
import de.javakaffee.web.msm.storage.MemcachedStorageClient.ByteArrayTranscoder;
/**
* @author @author <a href="mailto:martin.grotzke@javakaffee.de">Martin Grotzke</a>
*/
public abstract class CouchbaseIntegrationTest {
private static final Log LOG = LogFactory.getLog(CouchbaseIntegrationTest.class);
private final List<Pair<CouchbaseMock, Thread>> cluster = new ArrayList<Pair<CouchbaseMock,Thread>>(2);
private MemcachedClient mc;
private TomcatBuilder<?> _tomcat1;
private final int _portTomcat1 = 18888;
private boolean couchbaseProvided;
private TranscoderService transcoderService;
abstract TestUtils<?> getTestUtils();
@BeforeMethod
public void setUp(final Method testMethod) throws Throwable {
couchbaseProvided = Boolean.parseBoolean(System.getProperty("couchbase.provided", "false"));
final int couchbasePort = Integer.parseInt(System.getProperty("couchbase.port", "18091"));
if(!couchbaseProvided) {
cluster.add(setupCouchbase(couchbasePort));
}
try {
System.setProperty( "org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true" );
_tomcat1 = getTestUtils().tomcatBuilder().port(_portTomcat1).memcachedNodes("http://localhost:"+ couchbasePort +"/pools")
.sticky(true).memcachedProtocol("binary").username("default").buildAndStart();
} catch ( final Throwable e ) {
LOG.error( "could not start tomcat.", e );
throw e;
}
setupCouchbaseClient();
transcoderService = new TranscoderService(new JavaSerializationTranscoder(_tomcat1.getManager()));
}
@AfterMethod
public void tearDown() throws Exception {
mc.shutdown();
mc = null;
if(!couchbaseProvided) {
tearDownCouchbase();
}
_tomcat1.stop();
}
@Test
public void testBackupSessionInCouchbase() throws InterruptedException, ExecutionException {
final MemcachedSessionService service = _tomcat1.getService();
final MemcachedBackupSession session = createSession( service );
final String sessionId = "12345";
session.setId(sessionId);
session.setAttribute( "foo", "bar" );
final BackupResult backupResult = service.backupSession( session.getIdInternal(), false, null ).get();
assertEquals(backupResult.getStatus(), BackupResultStatus.SUCCESS);
final MemcachedBackupSession loadedSession = transcoderService.deserialize(mc.get(sessionId, ByteArrayTranscoder.INSTANCE), _tomcat1.getManager());
checkSession(loadedSession, session);
}
@Test(enabled = false) // spurious failures
public void testBackupSessionInCouchbaseCluster() throws Exception {
final MemcachedSessionService service = _tomcat1.getService();
cluster.add(setupCouchbase(getMaxCouchbasePort() + 1));
service.setMemcachedNodes(getMemcachedNodesConfig(getURIs()));
setupCouchbaseClient();
waitForReconnect(service.getStorageClient(), cluster.size(), 1000);
waitForReconnect(mc, cluster.size(), 1000);
final MemcachedBackupSession session = createSession( service );
final String sessionId = "12345";
session.setId(sessionId);
session.setAttribute( "foo", "bar" );
final BackupResult backupResult = service.backupSession( session.getIdInternal(), false, null ).get();
assertEquals(backupResult.getStatus(), BackupResultStatus.SUCCESS);
final MemcachedBackupSession loadedSession = transcoderService.deserialize(mc.get(sessionId, ByteArrayTranscoder.INSTANCE), _tomcat1.getManager());
checkSession(loadedSession, session);
}
private void checkSession(final MemcachedBackupSession actual, final MemcachedBackupSession expected) {
assertNotNull(actual);
assertEquals(actual.getId(), expected.getId());
assertEquals(actual.getAttributesInternal(), expected.getAttributesInternal());
}
private void waitForReconnect(final StorageClient client, final int expectedServers, final long timeToWait )
throws InterruptedException, RuntimeException {
waitForReconnect(((MemcachedStorageClient)client).getMemcachedClient(), expectedServers, timeToWait);
}
private void waitForReconnect( final MemcachedClient client, final int expectedServers, final long timeToWait )
throws InterruptedException, RuntimeException {
final long start = System.currentTimeMillis();
while( System.currentTimeMillis() < start + timeToWait ) {
if(client.getAvailableServers().size() == expectedServers) {
return;
}
Thread.sleep( 20 );
}
throw new RuntimeException( "MemcachedClient did not reconnect after " + timeToWait + " millis." );
}
private void setupCouchbaseClient() throws URISyntaxException, IOException {
if(mc != null) {
LOG.info("Closing existing couchbase client.");
mc.shutdown();
}
final List<URI> uris = getURIs();
LOG.info("Creating new couchbase client with uris " + uris);
mc = new CouchbaseClient(uris, "default", "");
}
private List<URI> getURIs() throws URISyntaxException {
final List<URI> uris = new ArrayList<URI>(cluster.size());
for (final Pair<CouchbaseMock, Thread> server : cluster) {
uris.add(new URI("http://localhost:"+ server.getFirst().getHttpPort() +"/pools"));
}
return uris;
}
private Pair<CouchbaseMock, Thread> setupCouchbase(final int couchbasePort) throws IOException {
final CouchbaseMock couchbase = new CouchbaseMock("localhost", couchbasePort, 1, 1);
couchbase.setRequiredHttpAuthorization(null);
final Thread thread = new Thread(couchbase);
thread.start();
return Pair.of(couchbase, thread);
}
private void tearDownCouchbase() throws InterruptedException {
for (final Pair<CouchbaseMock, Thread> server : cluster) {
server.getSecond().interrupt();
server.getSecond().join(1000);
server.getFirst().close();
}
cluster.clear();
}
private String getMemcachedNodesConfig(final List<URI> urIs) {
final StringBuilder sb = new StringBuilder();
for (final URI uri : urIs) {
if(sb.length() > 1) sb.append(",");
sb.append(uri.toString());
}
final String couchbaseNodes = sb.toString();
return couchbaseNodes;
}
private int getMaxCouchbasePort() {
return cluster.get(cluster.size() - 1).getFirst().getHttpPort();
}
}