Package org.red5.server.scope

Source Code of org.red5.server.scope.ScopeTest$ScopeClientWorker

/*
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
*
* Copyright 2006-2014 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.scope;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.NoSuchElementException;
import java.util.Set;

import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;

import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.red5.server.ClientRegistry;
import org.red5.server.Context;
import org.red5.server.Server;
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IClientRegistry;
import org.red5.server.api.IConnection;
import org.red5.server.api.IContext;
import org.red5.server.api.Red5;
import org.red5.server.api.TestConnection;
import org.red5.server.api.listeners.IScopeListener;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.service.IServiceCapableConnection;
import org.red5.server.api.so.ISharedObject;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.IClientBroadcastStream;
import org.red5.server.api.stream.IClientStream;
import org.red5.server.api.stream.IPlaylistSubscriberStream;
import org.red5.server.api.stream.ISingleItemSubscriberStream;
import org.red5.server.api.stream.IStreamCapableConnection;
import org.red5.server.scheduling.QuartzSchedulingService;
import org.red5.server.stream.ClientBroadcastStream;
import org.red5.server.stream.IProviderService;
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 Scope issues. First created to address:
* http://jira.red5.org/browse/APPSERVER-278
*
* @author Paul Gregoire (mondain@gmail.com)
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@ContextConfiguration(locations = { "ScopeTest.xml" })
public class ScopeTest extends AbstractJUnit4SpringContextTests {

  protected static Logger log = LoggerFactory.getLogger(ScopeTest.class);

  private static TestRunnable[] trs;

  @SuppressWarnings("unused")
  private QuartzSchedulingService service;

  private ClientRegistry registry;
 
  private Context context;

  private WebScope appScope;

  private String host = "localhost";

  private String appPath = "junit";

  private String roomPath = "/junit/room1";

  static {
    System.setProperty("red5.deployment.type", "junit");
    System.setProperty("red5.root", "target/test-classes");
    System.setProperty("red5.config_root", "src/main/server/conf");
    System.setProperty("logback.ContextSelector", "org.red5.logging.LoggingContextSelector");
  }

  @Before
  public void setUp() throws Exception {
    service = (QuartzSchedulingService) applicationContext.getBean("schedulingService");
    registry = (ClientRegistry) applicationContext.getBean("global.clientRegistry");
    context = (Context) applicationContext.getBean("web.context");
    Server server = (Server) applicationContext.getBean("red5.server");
    server.addListener(new IScopeListener() {
      public void notifyScopeCreated(IScope scope) {
        log.debug("Scope created: {}", scope);
      }

      public void notifyScopeRemoved(IScope scope) {
        log.debug("Scope removed: {}", scope);
      }
    });
    appScope = (WebScope) applicationContext.getBean("web.scope");
    log.debug("Application / web scope: {}", appScope);
    assertTrue(appScope.getDepth() == 1);
  }

  @After
  public void tearDown() throws Exception {
    appScope.getScope("room1").removeChildren();
    appScope = null;
  }

  private void setupScopes() {
    log.debug("-------------------------------------------------------------setupScopes");
    //Room 1
    // /default/junit/room1
    assertFalse(appScope.createChildScope("room1")); // room1 is defined in context xml file, this should fail
    IScope room1 = appScope.getScope("room1");
    log.debug("Room 1: {}", room1);
    assertTrue(room1.getDepth() == 2);
    IContext rmCtx1 = room1.getContext();
    log.debug("Context 1: {}", rmCtx1);
    //Room 2
    // /default/junit/room1/room2
    if (room1.getScope("room2") == null) {
      assertTrue(room1.createChildScope("room2"));
    }
    IScope room2 = room1.getScope("room2");
    log.debug("Room 2: {}", room2);
    assertNotNull(room2);
    assertTrue(room2.getDepth() == 3);
    IContext rmCtx2 = room2.getContext();
    log.debug("Context 2: {}", rmCtx2);
    //Room 3
    // /default/junit/room1/room2/room3
    if (room2.getScope("room3") == null) {
      assertTrue(room2.createChildScope("room3"));
    }
    IScope room3 = room2.getScope("room3");
    log.debug("Room 3: {}", room3);
    assertNotNull(room3);
    assertTrue(room3.getDepth() == 4);
    IContext rmCtx3 = room3.getContext();
    log.debug("Context 3: {}", rmCtx3);
    //Room 4 attaches at Room 1 (per bug example)
    // /default/junit/room1/room4
    if (room1.getScope("room4") == null) {
      assertTrue(room1.createChildScope("room4"));
    }
    IScope room4 = room1.getScope("room4");
    log.debug("Room 4: {}", room4);
    assertNotNull(room4);
    assertTrue(room4.getDepth() == 3);
    IContext rmCtx4 = room4.getContext();
    log.debug("Context 4: {}", rmCtx4);
    //Room 5
    // /default/junit/room1/room4/room5
    if (room4.getScope("room5") == null) {
      assertTrue(room4.createChildScope("room5"));
    }
    IScope room5 = room4.getScope("room5");
    log.debug("Room 5: {}", room5);
    assertNotNull(room5);
    assertTrue(room5.getDepth() == 4);
    IContext rmCtx5 = room5.getContext();
    log.debug("Context 5: {}", rmCtx5);
  }

  @Test
  public void client() {
    log.debug("-----------------------------------------------------------------client");
    IClientRegistry reg = context.getClientRegistry();
    IClient client = reg.newClient(null);
    assertTrue("client should not be null", client != null);
  }

  @Test
  public void connectionHandler() {
    log.debug("-----------------------------------------------------------------connectionHandler");
    TestConnection conn = new TestConnection(host, "/", null);
    // add the connection to thread local
    Red5.setConnectionLocal(conn);
    // resolve root
    IScope scope = context.resolveScope("/");
    IClientRegistry reg = context.getClientRegistry();
    IClient client = reg.newClient(null);
    assertNotNull(client);
    conn.initialize(client);
    if (conn.connect(scope)) {
      assertTrue("should have a scope", conn.getScope() != null);
      conn.close();
      assertTrue("should not be connected", !conn.isConnected());
    } else {
      assertTrue("didnt connect", false);
    }
    Red5.setConnectionLocal(null);
  }

  @Test
  public void context() {
    log.debug("-----------------------------------------------------------------context");
    IScope testRoom = context.resolveScope(roomPath);
    IContext context = testRoom.getContext();
    assertTrue("context should not be null", context != null);
    log.debug("{}", testRoom.getContext().getResource(""));
    log.debug("{}", testRoom.getResource(""));
    log.debug("{}", testRoom.getParent().getResource(""));
  }

  /**
   * TODO figure out why this fails to connect, doesnt make any sense.
   *
   * @throws InterruptedException
   */
  public void handler() throws InterruptedException {
    log.debug("-----------------------------------------------------------------handler");
    IScope testApp = context.resolveScope(appPath);
    assertTrue("should have a handler", testApp.hasHandler());
    log.debug("App: {}", testApp);

    TestConnection conn = new TestConnection(host, "/junit", null);
    Red5.setConnectionLocal(conn);

    IClientRegistry reg = context.getClientRegistry();
    IClient client = reg.newClient(null);
    assertTrue("client should not be null", client != null);
    log.debug("{}", client);
    String key = "key";
    String value = "value";
    client.setAttribute(key, value);
    assertTrue("attributes not working", client.getAttribute(key) == value);

    conn.initialize(client);

    if (conn.connect(testApp)) {
      // give connect a moment to settle
      Thread.sleep(100L);
      assertTrue("Should have a scope", conn.getScope() != null);
      assertTrue("app should have 1 client", ((Scope) testApp).getActiveClients() == 1);
      assertTrue("host should have 1 client", testApp.getParent().getClients().size() == 1);
      conn.close();
      assertTrue("Should not be connected", !conn.isConnected());
      assertTrue("app should have 0 client", ((Scope) testApp).getActiveClients() == 0);
      assertTrue("host should have 0 client", testApp.getParent().getClients().size() == 0);
    } else {
      fail("Didnt connect");
    }
    //client.disconnect();
    Red5.setConnectionLocal(null);
  }

  @Test
  public void scopeResolver() {
    log.debug("-----------------------------------------------------------------scopeResolver");
    // Global
    IScope global = context.getGlobalScope();
    assertNotNull("global scope should be set", global);
    assertTrue("should be global", ScopeUtils.isGlobal(global));
    log.debug("{}", global);
    // Test App
    IScope testApp = context.resolveScope(appPath);
    assertTrue("testApp scope not null", testApp != null);
    log.debug("{}", testApp);
    // Test Room
    IScope testRoom = context.resolveScope(roomPath);
    log.debug("{}", testRoom);
    // Test App Not Found
    try {
      IScope notFoundApp = context.resolveScope(appPath + "notfound");
      log.debug("{}", notFoundApp);
      assertTrue("should have thrown an exception", false);
    } catch (RuntimeException e) {
    }
  }

  @Test
  public void testScopeConnection() {
    log.debug("-----------------------------------------------------------------testScopeConnection");
    setupScopes();
    IScope room5 = ScopeUtils.resolveScope(appScope, "/junit/room1/room4/room5");
    log.debug("Room 5 scope: {}", room5);
    // test section for issue #259
    // a little pre-setup is needed first
    IClientRegistry reg = context.getClientRegistry();
    IClient client = reg.newClient(null);
    TestConnection conn = new TestConnection(host, appPath, client.getId());
    conn.initialize(client);
    Red5.setConnectionLocal(conn);
    assertTrue(conn.connect(room5));
    // their code
    IScope scope = Red5.getConnectionLocal().getScope();
    for (IConnection tempConn : scope.getClientConnections()) {
      if (tempConn instanceof IServiceCapableConnection) {
        try {
          @SuppressWarnings("unused")
          IServiceCapableConnection sc = (IServiceCapableConnection) tempConn;
          //sc.invoke(methodName, objArrays);
        } catch (NoSuchElementException e) {
          log.warn("Previous scope connection is unavailable", e);
        }
      }
    }
  }

  @Test
  public void testGetScopeNames() throws Exception {
    log.debug("-----------------------------------------------------------------testGetScopeNames");
    setupScopes();
    IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room1");
    log.debug("Room 1 scope: {}", room1);
    assertTrue(room1.getDepth() == 2);
    Set<String> names = room1.getScopeNames();
    log.debug("Scope: {}", names);
    IScope room5 = ScopeUtils.resolveScope(appScope, "/junit/room1/room4/room5");
    if (room5 != null) {
      log.debug("Room 5 scope: {}", room5);
      assertTrue(room5.getDepth() == 4);
      names = room1.getScopeNames();
      log.debug("Scope: {}", names);
    } else {
      log.warn("Room 5 scope was not found");
    }
  }

  @Test
  public void testRemoveScope() throws Exception {
    log.debug("-----------------------------------------------------------------testRemoveScope");
    setupScopes();
    IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room1");
    IScope room4 = ScopeUtils.resolveScope(appScope, "/junit/room1/room4");
    log.debug("Room 4 scope: {}", room4);
    assertTrue(room4.getDepth() == 3);
    log.debug("Room 4 child scope exists: {}", room1.hasChildScope("room4"));
    room1.removeChildScope(room4);
    log.debug("Room 4 child scope exists: {}", room1.hasChildScope("room4"));
  }

  /**
   * Test for Issue 73
   * http://code.google.com/p/red5/issues/detail?id=73
   *
   */
  @Test
  public void testGetContextPath() throws Exception {
    log.debug("-----------------------------------------------------------------testGetContextPath");
    log.debug("Context path: {}", appScope.getContextPath());
  }

  /**
   * Test created to address multi-thread adding and removal of scopes.
   * @throws Throwable
   */
  @Test
  public void testScopeMultiThreadHandling() throws Throwable {
    log.debug("-----------------------------------------------------------------testScopeMultiThreadHandling");
    setupScopes();
    boolean persistent = false;
    // create app
    MultiThreadedApplicationAdapter app = new MultiThreadedApplicationAdapter();
    // change the handler
    appScope.setHandler(app);
    // start
    app.start(appScope);
    // get our room
    IScope room = ScopeUtils.resolveScope(appScope, "/junit/mt");
    if (room == null) {
      assertTrue(app.createChildScope("mt"));
      room = ScopeUtils.resolveScope(appScope, "/junit/mt");
    }
    // create the SO
    assertTrue(app.createSharedObject(room, "mtSO", persistent));
    ISharedObject so = app.getSharedObject(room, "mtSO");
    // acquire only works with non-persistent so's
    if (!persistent) {
      so.acquire();
      assertTrue(so.isAcquired());
    }
    // test runnables represent clients
    trs = new TestRunnable[21];
    for (int t = 0; t < trs.length; t++) {
      trs[t] = new ScopeClientWorker(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) {
      ScopeClientWorker cl = (ScopeClientWorker) r;
      log.debug("Worker: {} shared object: {}", cl.getId(), cl.getSharedObject().getAttributes());
    }
    if (!persistent) {
      assertTrue(so.isAcquired());
      so.release();
      assertFalse(so.isAcquired());
    }
    app.stop(appScope);

    //    IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room1");
    //    IScope room4 = ScopeUtils.resolveScope(appScope, "/junit/room1/room4");
    //    log.debug("Room 4 scope: {}", room4);
    //    assertTrue(room4.getDepth() == 3);
    //    log.debug("Room 4 child scope exists: {}", room1.hasChildScope("room4"));
    //    room1.removeChildScope(room4);
    //    log.debug("Room 4 child scope exists: {}", room1.hasChildScope("room4"));
  }

  /**
   * Test created to address missing handler when a subscope child is removed. The handler seems to get
   * removed from other children in the set.
   *
   * @throws Throwable
   */
  @Test
  public void testScopeMissingHandler() throws Throwable {
    log.debug("-----------------------------------------------------------------testScopeMissingHandler");
    // create app
    MultiThreadedApplicationAdapter app = new MultiThreadedApplicationAdapter();
    // change the handler
    appScope.setHandler(app);
    // start
    app.start(appScope);
    // create our additional scopes
    assertTrue(appScope.hasHandler());
    IScope top = ScopeUtils.resolveScope(appScope, "/junit");
    assertTrue(top.hasHandler());
    IScope room = ScopeUtils.resolveScope(appScope, "/junit/room13");
    if (room == null) {
      assertTrue(top.createChildScope("room13"));
      room = ScopeUtils.resolveScope(appScope, "/junit/room13");
      assertNotNull(room);
    }
    assertTrue(room.hasHandler());
    // get rooms
    IScope room1 = ScopeUtils.resolveScope(appScope, "/junit/room13/subroomA");
    if (room1 == null) {
      assertTrue(room.createChildScope("subroomA"));
      room1 = ScopeUtils.resolveScope(appScope, "/junit/room13/subroomA");
      assertNotNull(room1);
    }
    Thread.sleep(10);
    IScope room2 = ScopeUtils.resolveScope(appScope, "/junit/room13/subroomB");
    if (room2 == null) {
      assertTrue(room.createChildScope("subroomB"));
      room2 = ScopeUtils.resolveScope(appScope, "/junit/room13/subroomB");
      assertNotNull(room2);
    }
    // let it settle for a moment
    Thread.sleep(50L);
    // create the SOs
    String soName = "messager";
    if (!app.hasSharedObject(room1, soName)) {
      app.createSharedObject(room1, soName, false);
    }
    assertNotNull(app.getSharedObject(room1, soName, false));
    if (!app.hasSharedObject(room2, soName)) {
      app.createSharedObject(room2, soName, false);
    }
    assertNotNull(app.getSharedObject(room2, soName, false));
    // test runnables represent clients
    trs = new TestRunnable[2];
    trs[0] = new ScopeClientWorkerA(0, app, room1);
    trs[1] = new ScopeClientWorkerB(1, app, room2);
    MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);
    // fires off threads
    long start = System.nanoTime();
    mttr.runTestRunnables();
    System.out.println("Runtime: " + (System.nanoTime() - start) + "ns");
    ScopeClientWorkerA soa = (ScopeClientWorkerA) trs[0];
    log.debug("Worker: {} shared object: {}", soa.getId(), soa.getSharedObject().getAttributes());
    ScopeClientWorkerB sob = (ScopeClientWorkerB) trs[1];
    log.debug("Worker: {} shared object: {}", sob.getId(), sob.getSharedObject().getAttributes());
    Thread.sleep(300L);
    // clean up / stop
    app.stop(appScope);
  }

  // Used to ensure all the test-runnables are in "runTest" block.
  private static boolean allThreadsRunning() {
    for (TestRunnable r : trs) {
      if (!((ScopeClientWorker) r).isRunning()) {
        return false;
      }
    }
    return true;
  }

  private class ScopeClientWorker extends TestRunnable {

    private int id;

    private ISharedObject so;

    private ClientBroadcastStream stream;

    private volatile boolean running = false;

    public ScopeClientWorker(int id, MultiThreadedApplicationAdapter app, IScope room) {
      this.id = id;
      this.so = app.getSharedObject(room, "mtSO", true);
      if (id % 2 == 0) {
        TestStreamConnection conn = new TestStreamConnection("localhost", "/junit/mt", "session" + id);
        conn.setClient(registry.newClient(null));
        // create a few streams
        this.stream = new ClientBroadcastStream();
        String name = "stream" + id;
        stream.setRegisterJMX(false);
        stream.setPublishedName(name);
        stream.setScope(room);
        stream.setConnection(conn);       
        //
        IProviderService providerService = (IProviderService) room.getContext().getBean(IProviderService.BEAN_NAME);
        // publish this server-side stream
        providerService.registerBroadcastStream(room, name, stream);
        // start it
        app.streamBroadcastStart(stream);
        if (room.getBroadcastScope(name) == null) {
          log.warn("Stream scope failed");
        }
      }
    }

    public void runTest() throws Throwable {
      log.debug("runTest#{}", id);
      running = true;
      // start any stream
      if (stream != null) {
        stream.start();
        stream.startPublishing();
      }
      do {
        Thread.sleep(33);
      } while (!allThreadsRunning());
      // set a value
      so.setAttribute("time", System.currentTimeMillis() + Integer.valueOf(RandomStringUtils.randomNumeric(3)));
      Thread.sleep(64);
      // stop any stream
      if (stream != null) {
        stream.close();
      }
      log.debug("runTest-end#{}", id);
      running = false;
    }

    public int getId() {
      return id;
    }

    public ISharedObject getSharedObject() {
      return so;
    }

    @SuppressWarnings("unused")
    public IBroadcastStream getStream() {
      return stream;
    }

    public boolean isRunning() {
      return running;
    }
  }

  private final class TestStreamConnection extends TestConnection implements IStreamCapableConnection {

    public TestStreamConnection(String host, String path, String sessionId) {
      super(host, path, sessionId);
    }

    public int reserveStreamId() {
      return 0;
    }

    public int reserveStreamId(int id) {
      return 0;
    }

    public void unreserveStreamId(int streamId) {
    }

    public void deleteStreamById(int streamId) {
    }

    public IClientStream getStreamById(int streamId) {
      return null;
    }

    public ISingleItemSubscriberStream newSingleItemSubscriberStream(int streamId) {
      return null;
    }

    public IPlaylistSubscriberStream newPlaylistSubscriberStream(int streamId) {
      return null;
    }

    public IClientBroadcastStream newBroadcastStream(int streamId) {
      return null;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
      return "TestStreamConnection [path=" + path + ", sessionId=" + sessionId + ", client=" + client + ", scope=" + scope + ", closed=" + isClosed() + "]";
    }

  }

  private class ScopeClientWorkerA extends TestRunnable {

    private int id;

    private IScope room;

    private IConnection conn;

    private ISharedObject so;

    public ScopeClientWorkerA(int id, MultiThreadedApplicationAdapter app, IScope room) {
      this.id = id;
      this.room = room;
      this.so = app.getSharedObject(room, "messager", false);
      System.out.println("Connect path: " + room.getContextPath());
      conn = new TestStreamConnection("localhost", room.getContextPath(), "session" + id);
      conn.setClient(registry.newClient(null));
    }

    @SuppressWarnings("deprecation")
    public void runTest() throws Throwable {
      log.debug("runTest#{}", id);
      Red5.setConnectionLocal(conn);
      assertTrue(conn.connect(room));
      Thread.sleep(50);
      // set a value
      so.setAttribute("client-id", id);
      so.setAttribute("time", System.currentTimeMillis() + Integer.valueOf(RandomStringUtils.randomNumeric(3)));
      Thread.sleep(50);
      conn.close();
      Red5.setConnectionLocal(null);
      log.debug("runTest-end#{}", id);
    }

    public int getId() {
      return id;
    }

    public ISharedObject getSharedObject() {
      return so;
    }

  }

  private class ScopeClientWorkerB extends TestRunnable {

    private int id;

    private IScope room;

    private IConnection conn;

    private ISharedObject so;

    public ScopeClientWorkerB(int id, MultiThreadedApplicationAdapter app, IScope room) {
      this.id = id;
      this.room = room;
      this.so = app.getSharedObject(room, "messager", false);
      System.out.println("Connect path: " + room.getContextPath());
      conn = new TestStreamConnection("localhost", room.getContextPath(), "session" + id);
      conn.setClient(registry.newClient(null));
    }

    @SuppressWarnings("deprecation")
    public void runTest() throws Throwable {
      log.debug("runTest#{}", id);
      Red5.setConnectionLocal(conn);
      assertTrue(conn.connect(room));
      Thread.sleep(50);
      // set a value
      so.setAttribute("client-id", id);
      so.setAttribute("time", System.currentTimeMillis() + Integer.valueOf(RandomStringUtils.randomNumeric(3)));
      Thread.sleep(100);
      so.sendMessage("sendMessage", null);
      Thread.sleep(50);
      conn.close();
      Red5.setConnectionLocal(null);
      log.debug("runTest-end#{}", id);
    }

    public int getId() {
      return id;
    }

    public ISharedObject getSharedObject() {
      return so;
    }

  }

}
TOP

Related Classes of org.red5.server.scope.ScopeTest$ScopeClientWorker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.