package com.softcomputer.softweb.infl.commons.clustering.stabilitytest.lockservice.jgrp1660; import java.io.PrintWriter; import java.io.StringWriter; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.Lock; import junit.framework.AssertionFailedError; import org.apache.log4j.Logger; import org.jgroups.JChannel; import org.jgroups.blocks.locking.LockService; import org.jgroups.logging.Log; import org.jgroups.logging.LogFactory; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.internal.runners.JUnit4ClassRunner; import org.junit.runner.RunWith; @RunWith(JUnit4ClassRunner.class) public class LockServiceStabilityTestCase { private int clientImitationThreadsAmount=0; private Thread[] clientImitationThreads=null; private final BlockingQueue descr_queue=new LinkedBlockingQueue(); private final LockService lockService=new LockService(); private final Log logger=LogFactory.getLog(LockServiceStabilityTestCase.class); private static final int DEFAULT_CLIENT_IMITATION_THREADS_AMOUNT=30; private static final int DEFAULT_TEST_TIME_IN_SECONDS=5*60; enum LOCK_TYPE { LOCK, TRY_LOCK } /** Configures test */ @Before public void configureTest() throws Exception { clientImitationThreadsAmount=DEFAULT_CLIENT_IMITATION_THREADS_AMOUNT; final JChannel channel=new JChannel( LockServiceStabilityTestCase.class.getResourceAsStream( "lock.xml" ) ); channel.setName("A"); channel.setDiscardOwnMessages(true); channel.connect("Test"); lockService.setChannel(channel); } /** Tests concurrent usage of {@link LockService} */ @Test public void test() { // Create client imitation threads clientImitationThreads=createClientImitationThreads(clientImitationThreadsAmount); // Run client imitation threads runClientImitationThreads(clientImitationThreads); // Wait for client imitation thread statuses boolean atLeastOneThreadFailed=false; for(int i=0; i < clientImitationThreadsAmount; i++) { try { // Read from results queue final ClientResult clientImitationResultDescription=descr_queue.take(); if(clientImitationResultDescription.getStatus().equals(ClientResult.STATUS.FINISHED)) // Imitation started successfully { getLogger().info("Client imitation successfully finished. threadName=" + clientImitationResultDescription.getThreadName()); } else // Imitation failed { final Error error=clientImitationResultDescription.getError(); final StringWriter stackTraceStringWriter=new StringWriter(); final PrintWriter stackTracePrintWriter=new PrintWriter(stackTraceStringWriter); error.printStackTrace(stackTracePrintWriter); getLogger().info("Client imitation failed. threadName=" + clientImitationResultDescription.getThreadName() + ", error message=" + error.getMessage() + ", stack trace=" + stackTraceStringWriter.getBuffer().toString()); atLeastOneThreadFailed=true; } } catch(final InterruptedException e) { final StringWriter stackTraceStringWriter=new StringWriter(); final PrintWriter stackTracePrintWriter=new PrintWriter(stackTraceStringWriter); e.printStackTrace(stackTracePrintWriter); Assert.fail("Client imitation failed. Stack trace=" + stackTraceStringWriter.getBuffer().toString()); } } if(atLeastOneThreadFailed) { Assert.fail("Client imitation failed. See traces for details."); } } /** Creates array of client imitation threads */ private Thread[] createClientImitationThreads(final int amount) { final Thread[] threads=new Thread[amount]; final int threadsAmount=threads.length; for(int i=0; i < threadsAmount; i++) { final Runnable runnable=new ClientImitation(); threads[i]=new Thread(runnable,"Thread-" + runnable.getClass().getSimpleName() + "-" + i); } return threads; } /** Run created earlier client imitation threads */ private static void runClientImitationThreads(final Thread[] threads) { final int threadsAmount=threads.length; for(int i=0; i < threadsAmount; i++) { final Thread thread=threads[i]; thread.start(); } } /** Getter of {@link Logger} */ private Log getLogger() { return logger; } /** * Imitates client of {@link LockService} * Just locks and then unlocks a lock id */ private class ClientImitation implements Runnable { private int lockType = -1; /* * @see java.lang.Runnable#run() */ @Override public void run() { long startTime=System.currentTimeMillis(); long testTimeInMilliseconds=DEFAULT_TEST_TIME_IN_SECONDS * 1000; try { for(int count=1; (System.currentTimeMillis() - startTime) < testTimeInMilliseconds; ++count, lockType *= -1) { getLogger().trace(Thread.currentThread().getName() + ": cycle #" + count); String lockId="scct"; final Lock threadScopeLock=lockService.getLock(lockId); // boolean result=threadScopeLock.tryLock(); // boolean result=threadScopeLock.tryLock(2000, TimeUnit.MILLISECONDS); if( lockType < 0 ) { getLogger().trace(Thread.currentThread().getName() + ": LOCK" + count); threadScopeLock.lock(); try { Thread.sleep( 100 ); } finally { threadScopeLock.unlock(); } } else if( lockType > 0 ) { getLogger().trace(Thread.currentThread().getName() + ": TRY LOCK" + count); if(threadScopeLock.tryLock()) { try { Thread.sleep( 100 ); } finally { threadScopeLock.unlock(); } } } } // Notify main test flow about successful processing final ClientResult clientImitationResultDescription=new ClientResult(ClientResult.STATUS.FINISHED,Thread.currentThread().getName()); putClientImitationResultDescription(clientImitationResultDescription); } catch(final Error e) { // Notify main test flow about fail final ClientResult clientImitationResultDescription=new ClientResult(ClientResult.STATUS.FAILED,Thread.currentThread().getName(),e); putClientImitationResultDescription(clientImitationResultDescription); } catch(Exception e) { // Notify main test flow about fail final StringWriter stackTraceStringWriter=new StringWriter(); final PrintWriter stackTracePrintWriter=new PrintWriter(stackTraceStringWriter); e.printStackTrace(stackTracePrintWriter); final Error assertionFailedError=new Error(e.getMessage() + ". Stack trace: " + stackTraceStringWriter.getBuffer().toString()); final ClientResult clientImitationResultDescription=new ClientResult(ClientResult.STATUS.FAILED,Thread.currentThread().getName(),assertionFailedError); putClientImitationResultDescription(clientImitationResultDescription); } } /** Puts {@link org.jgroups.tests.LockServiceStabilityTestCase.ClientResult} to notify test main flow */ private void putClientImitationResultDescription(final ClientResult clientImitationResultDescription) { try { descr_queue.put(clientImitationResultDescription); } catch(InterruptedException e1) { getLogger().error(ClientResult.class.getSimpleName() + " can not be transfered to main thread. clientImitationResultDescription='" + clientImitationResultDescription + "'",e1); } } } /** Description of result of {@link ClientImitation} thread work */ private static class ClientResult { private STATUS status; private final String threadName; private final Error error; /** Constructor */ public ClientResult(final STATUS status, final String threadName) { this(status,threadName,null); } /** Constructor */ public ClientResult(final STATUS status, final String threadName, final Error error) { super(); this.status=status; this.threadName=threadName; this.error=error; } /** Getter of {@link STATUS} */ public STATUS getStatus() { return status; } /** Getter of name of client imitation thread */ public String getThreadName() { return threadName; } /** Getter of {@link AssertionFailedError} if any */ public Error getError() { return error; } /* * @see java.lang.Object#toString() */ @Override public String toString() { final StringWriter stackTraceStringWriter=new StringWriter(); final PrintWriter stackTracePrintWriter=new PrintWriter(stackTraceStringWriter); error.printStackTrace(stackTracePrintWriter); return new StringBuilder(ClientResult.class.getSimpleName()) .append("[threadId=").append(threadName) .append(", error=").append("'Message:").append(error.getMessage()).append("; Stack trace:").append(stackTraceStringWriter.getBuffer().toString()) .append("]") .toString(); } /** Status of {@link ClientImitation} work */ enum STATUS { FINISHED, FAILED } } }