/*
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
*
* Copyright 2006-2012 by respective authors (see below). All rights reserved.
*
* 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 org.red5.server.so;
import static junit.framework.Assert.assertTrue;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;
import org.junit.Test;
import org.red5.server.api.IAttributeStore;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.so.ISharedObject;
import org.red5.server.api.so.ISharedObjectBase;
import org.red5.server.api.so.ISharedObjectListener;
import org.red5.server.scope.WebScope;
import org.red5.server.util.ScopeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
/**
* This is for testing SharedObject issues.
*
* http://help.adobe.com/en_US/FlashMediaServer/3.5_SS_ASD/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7e63.html
*
* @author Paul Gregoire (mondain@gmail.com)
*/
@ContextConfiguration(locations = { "SharedObjectTest.xml" })
public class SharedObjectTest extends AbstractJUnit4SpringContextTests {
protected static Logger log = LoggerFactory.getLogger(SharedObjectTest.class);
private static WebScope appScope;
private static TestRunnable[] trs;
@SuppressWarnings("unused")
private String host = "localhost";
@SuppressWarnings("unused")
private String appPath = "junit";
@SuppressWarnings("unused")
private String roomPath = "/junit/room1";
static {
System.setProperty("red5.deployment.type", "junit");
System.setProperty("red5.root", "bin");
System.setProperty("red5.config_root", "bin/conf");
System.setProperty("logback.ContextSelector", "org.red5.logging.LoggingContextSelector");
}
{
log.debug("Property - user.dir: {}", System.getProperty("user.dir"));
log.debug("Property - red5.root: {}", System.getProperty("red5.root"));
log.debug("Property - red5.config_root: {}", System.getProperty("red5.config_root"));
}
@Test
public void testSharedObject() {
log.debug("testSharedObject");
if (appScope == null) {
appScope = (WebScope) applicationContext.getBean("web.scope");
log.debug("Application / web scope: {}", appScope);
assertTrue(appScope.getDepth() == 1);
}
SOApplication app = (SOApplication) applicationContext.getBean("web.handler");
String soName = "foo";
//Room 1
// /default/junit/room1
TestCase.assertTrue(appScope.createChildScope("room1"));
IScope room1 = appScope.getScope("room1");
log.debug("Room 1: {}", room1);
assertTrue(room1.getDepth() == 2);
// get the SO
ISharedObject sharedObject = app.getSharedObject(room1, soName, true);
log.debug("SO: {}", sharedObject);
assertTrue(sharedObject != null);
log.debug("testSharedObject-end");
}
@Test
public void testGetSONames() throws Exception {
log.debug("testGetSONames");
if (appScope == null) {
appScope = (WebScope) applicationContext.getBean("web.scope");
log.debug("Application / web scope: {}", appScope);
assertTrue(appScope.getDepth() == 1);
}
IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room1");
log.debug("Room 1 scope: {}", room1);
Set<String> names = room1.getScopeNames();
log.debug("Names: {}", names);
assertTrue(names.size() > 0);
log.debug("testGetSONames-end");
}
@Test
public void testRemoveSO() throws Exception {
log.debug("testRemoveSO");
if (appScope == null) {
appScope = (WebScope) applicationContext.getBean("web.scope");
log.debug("Application / web scope: {}", appScope);
assertTrue(appScope.getDepth() == 1);
}
String soName = "foo";
IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room1");
room1.removeChildren();
log.debug("Child exists: {}", room1.hasChildScope(soName));
log.debug("testRemoveSO-end");
}
/**
* Test for Issue 209
* http://code.google.com/p/red5/issues/detail?id=209
*/
@Test
public void testPersistentCreation() throws Exception {
log.debug("testPersistentCreation");
if (appScope == null) {
appScope = (WebScope) applicationContext.getBean("web.scope");
log.debug("Application / web scope: {}", appScope);
assertTrue(appScope.getDepth() == 1);
}
SOApplication app = (SOApplication) applicationContext.getBean("web.handler");
String soName = "foo";
// get our room
IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room1");
// create the SO
app.createSharedObject(room1, soName, true);
// get the SO
ISharedObject sharedObject = app.getSharedObject(room1, soName, true);
assertTrue(sharedObject != null);
log.debug("testPersistentCreation-end");
}
@Test
public void testDeepDirty() throws Throwable {
log.debug("testDeepDirty");
SOApplication app = (SOApplication) applicationContext.getBean("web.handler");
// get our room
IScope room = ScopeUtils.resolveScope(appScope, "/junit/room1");
// create the SO
app.createSharedObject(room, "dirtySO", true);
// test runnables represent clients
trs = new TestRunnable[2];
for (int t = 0; t < 2; t++) {
trs[t] = new SOClientWorker(t, app, room);
}
MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);
// fires off threads
long start = System.nanoTime();
mttr.runTestRunnables();
System.out.println("Runtime: " + (System.nanoTime() - start) + "ns");
// go to sleep
try {
Thread.sleep(3000);
} catch (Exception e) {
}
for (TestRunnable r : trs) {
SOClientWorker cl = (SOClientWorker) r;
log.debug("Worker: {} shared object: {}", cl.getId(), cl.getSO().getAttributes());
}
log.debug("testDeepDirty-end");
}
// Used to ensure all the test-runnables are in "runTest" block.
private static boolean allThreadsRunning() {
for (TestRunnable r : trs) {
if (!((SOClientWorker) r).isRunning()) {
return false;
}
}
return true;
}
private class SOClientWorker extends TestRunnable {
private int id;
private ISharedObject so;
private volatile boolean running = false;
public SOClientWorker(int id, SOApplication app, IScope room) {
this.id = id;
this.so = app.getSharedObject(room, "dirtySO", true);
ISharedObjectListener listener = new MySOListener(id);
so.addSharedObjectListener(listener);
}
public void runTest() throws Throwable {
log.debug("runTest#{}", id);
running = true;
do {
Thread.sleep(100);
} while (!allThreadsRunning());
// create complex type object
Complex complex = (Complex) so.getAttribute("complex");
if (complex == null) {
complex = new Complex();
complex.getMap().put("myId", id);
so.setAttribute("complex", complex);
}
Thread.sleep(500);
log.debug("runTest-end#{}", id);
running = false;
}
public int getId() {
return id;
}
public ISharedObject getSO() {
return so;
}
public boolean isRunning() {
return running;
}
}
private class MySOListener implements ISharedObjectListener {
private int id;
public MySOListener(int id) {
this.id = id;
}
@Override
public void onSharedObjectConnect(ISharedObjectBase so) {
log.trace("onSharedObjectConnect");
}
@Override
public void onSharedObjectDisconnect(ISharedObjectBase so) {
log.trace("onSharedObjectDisconnect");
}
@Override
public void onSharedObjectUpdate(ISharedObjectBase so, String key, Object value) {
log.trace("onSharedObjectUpdate - key: {} value: {}", key, value);
}
@Override
public void onSharedObjectUpdate(ISharedObjectBase so, IAttributeStore values) {
log.trace("onSharedObjectUpdate - values: {}", values);
}
@Override
public void onSharedObjectUpdate(ISharedObjectBase so, Map<String, Object> values) {
log.trace("onSharedObjectUpdate - values: {}", values);
}
@Override
public void onSharedObjectDelete(ISharedObjectBase so, String key) {
log.trace("onSharedObjectDelete");
}
@Override
public void onSharedObjectClear(ISharedObjectBase so) {
log.trace("onSharedObjectClear");
}
@Override
public void onSharedObjectSend(ISharedObjectBase so, String method, List<?> params) {
log.trace("onSharedObjectSend");
}
}
private class Complex {
private long x = System.currentTimeMillis();
private String s = "Complex object";
@SuppressWarnings("rawtypes")
private Map map = new HashMap();
public long getX() {
return x;
}
public void setX(long x) {
this.x = x;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
@Override
public String toString() {
return "Complex [x=" + x + ", s=" + s + ", map=" + map + "]";
}
}
}