Package org.red5.server.so

Source Code of org.red5.server.so.SharedObjectScope

package org.red5.server.so;

/*
* RED5 Open Source Flash Server - http://www.osflash.org/red5
*
* Copyright (c) 2006-2007 by respective authors (see below). All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantLock;

import org.red5.server.BaseConnection;
import org.red5.server.BasicScope;
import org.red5.server.api.IAttributeStore;
import org.red5.server.api.IContext;
import org.red5.server.api.IScope;
import org.red5.server.api.ScopeUtils;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.event.IEventListener;
import org.red5.server.api.persistence.IPersistenceStore;
import org.red5.server.api.so.ISharedObject;
import org.red5.server.api.so.ISharedObjectListener;
import org.red5.server.api.so.ISharedObjectSecurity;
import org.red5.server.api.so.ISharedObjectSecurityService;
import org.red5.server.api.statistics.ISharedObjectStatistics;
import org.red5.server.net.rtmp.status.StatusCodes;
import org.red5.server.service.ServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Special scope for shared objects
*/
public class SharedObjectScope extends BasicScope implements ISharedObject, StatusCodes {
    /**
     * Logger
     */
  private Logger log = LoggerFactory.getLogger(SharedObjectScope.class);
    /**
     * Lock to synchronize shared object updates from multiple threads
     */
  private final ReentrantLock lock = new ReentrantLock();
    /**
     * Server-side listeners
     */
  private Set<ISharedObjectListener> serverListeners = new CopyOnWriteArraySet<ISharedObjectListener>();
    /**
     * Event handlers
     */
  private Map<String, Object> handlers = new ConcurrentHashMap<String, Object>();
  /**
   * Security handlers
   */
  private Set<ISharedObjectSecurity> securityHandlers = new CopyOnWriteArraySet<ISharedObjectSecurity>();
    /**
     * Scoped shared object
     */
  protected SharedObject so;

    /**
     * Creates shared object with given parent scope, name, persistence flag state and store object
     * @param parent                    Parent scope
     * @param name                      Name
     * @param persistent                Persistence flag state
     * @param store                     Persistence store
     */
  public SharedObjectScope(IScope parent, String name, boolean persistent,
      IPersistenceStore store) {
    super(parent, TYPE, name, persistent);

    // Create shared object wrapper around the attributes
    String path = parent.getContextPath();
    if ("".equals(path) || path.charAt(0) != '/') {
      path = '/' + path;
    }
        // Load SO
        so = (SharedObject) store.load(TYPE + path + '/' + name);
    // Create if it doesn't exist
        if (so == null) {
      so = new SharedObject(attributes, name, path, persistent, store);
            // Save
      store.save(so);
    } else {
            // Rename and set path
            so.setName(name);
      so.setPath(path);
    }
  }

  /** {@inheritDoc} */
  public void registerSharedObjectSecurity(ISharedObjectSecurity handler) {
    securityHandlers.add(handler);
  }
 
  /** {@inheritDoc} */
  public void unregisterSharedObjectSecurity(ISharedObjectSecurity handler) {
    securityHandlers.remove(handler);
  }
 
  /** {@inheritDoc} */
  public Set<ISharedObjectSecurity> getSharedObjectSecurity() {
    return Collections.unmodifiableSet(securityHandlers);
  }

  /** {@inheritDoc} */
    @Override
  public IPersistenceStore getStore() {
    return so.getStore();
  }

  /** {@inheritDoc} */
    @Override
  public String getName() {
    return so.getName();
  }

  /** {@inheritDoc} */
    @Override
  public void setName(String name) {
    so.setName(name);
  }

  /** {@inheritDoc} */
    @Override
  public String getPath() {
    return so.getPath();
  }

  /** {@inheritDoc} */
    @Override
  public void setPath(String path) {
    so.setPath(path);
  }

  /** {@inheritDoc} */
    @Override
  public String getType() {
    return so.getType();
  }

  /** {@inheritDoc} */
    public boolean isPersistentObject() {
    return so.isPersistentObject();
  }

  /** {@inheritDoc} */
    public void beginUpdate() {
        // Make sure only one thread can update the SO
    lock.lock();
    so.beginUpdate();
  }

  /** {@inheritDoc} */
    public void beginUpdate(IEventListener listener) {
        // Make sure only one thread can update the SO
    lock.lock();
        so.beginUpdate(listener);
  }

  /** {@inheritDoc} */
    public void endUpdate() {
    // End update of SO
        so.endUpdate();
    lock.unlock();
  }

  /** {@inheritDoc} */
    public int getVersion() {
    return so.getVersion();
  }

  /** {@inheritDoc} */
    public void sendMessage(String handler, List arguments) {
    beginUpdate();
    try {
      so.sendMessage(handler, arguments);
    } finally {
      endUpdate();
    }

    // Invoke method on registered handler
    String serviceName, serviceMethod;
        // Find out last dot position
        int dotPos = handler.lastIndexOf('.');
        // If any, split service name and service method name
        if (dotPos != -1) {
      serviceName = handler.substring(0, dotPos);
      serviceMethod = handler.substring(dotPos + 1);
    } else {
            // Otherwise only service method name is available
            serviceName = "";
      serviceMethod = handler;
    }

        // Get previously registred handler for service
        Object soHandler = getServiceHandler(serviceName);
    if (soHandler == null && hasParent()) {
      // No custom handler, check for service defined in the scope's
      // context
      IContext context = getParent().getContext();
      try {
        // The bean must have a name of
        // "<SharedObjectName>.<DottedServiceName>.soservice"
        soHandler = context.getBean(so.getName() + '.' + serviceName
            + ".soservice");
      } catch (Exception err) {
        log.debug("No such bean");
      }
    }

        // Once handler is found, find matching method
        if (soHandler != null) {
            // With exact params...
            Object[] methodResult = ServiceUtils.findMethodWithExactParameters(
          soHandler, serviceMethod, arguments);
            // Or at least with suitable list params
            if (methodResult.length == 0 || methodResult[0] == null) {
        methodResult = ServiceUtils.findMethodWithListParameters(
            soHandler, serviceMethod, arguments);
      }

            // If method is found...
            if (methodResult.length > 0 && methodResult[0] != null) {
        Method method = (Method) methodResult[0];
        Object[] params = (Object[]) methodResult[1];
                //...try to invoke it and handle exceptions
                try {
          method.invoke(soHandler, params);
        } catch (Exception err) {
          log.error("Error while invoking method " + serviceMethod
              + " on shared object handler " + handler, err);
        }
      }
    }

    // Notify server listeners
      for (ISharedObjectListener listener : serverListeners) {
        listener.onSharedObjectSend(this, handler, arguments);
      }
    }

  /** {@inheritDoc} */
    @Override
  public boolean removeAttribute(String name) {
      boolean success;
        // Begin update of shared object
        beginUpdate();
      try {
        // Try to remove attribute
        success = so.removeAttribute(name);
      } finally {
        // End update of SO
        endUpdate();
      }

        // Notify listeners on success and return true
        if (success) {
      for (ISharedObjectListener listener : serverListeners) {
        listener.onSharedObjectDelete(this, name);
      }
        }
    return success;
  }

  /** {@inheritDoc} */
    @Override
  public void removeAttributes() {
        // Begin update
        beginUpdate();
        try {
          // Remove all attributes
          so.removeAttributes();
        } finally {
          // End update
          endUpdate();
        }

        // Notify listeners on atributes clear
    for (ISharedObjectListener listener : serverListeners) {
      listener.onSharedObjectClear(this);
    }
    }

  /** {@inheritDoc} */
    @Override
  public void addEventListener(IEventListener listener) {
    super.addEventListener(listener);
    so.register(listener);

    for (ISharedObjectListener soListener : serverListeners) {
      soListener.onSharedObjectConnect(this);
    }
    }

  /** {@inheritDoc} */
    @Override
  public void removeEventListener(IEventListener listener) {
    so.unregister(listener);
    super.removeEventListener(listener);
    if (!so.isPersistentObject() && (so.getListeners() == null || so.getListeners().isEmpty())) {
      getParent().removeChildScope(this);
    }

    for (ISharedObjectListener soListener : serverListeners) {
      soListener.onSharedObjectDisconnect(this);
    }
    }

  /** {@inheritDoc} */
    @Override
  public boolean hasAttribute(String name) {
    return so.hasAttribute(name);
  }

  /** {@inheritDoc} */
    @Override
  public Object getAttribute(String name) {
    return so.getAttribute(name);
  }

  /** {@inheritDoc} */
    @Override
  public Object getAttribute(String name, Object value) {
      beginUpdate();
      try {
        return so.getAttribute(name, value);
      } finally {
        endUpdate();
      }
  }

  /** {@inheritDoc} */
    @Override
  public Map<String, Object> getAttributes() {
    return so.getAttributes();
  }

  /** {@inheritDoc} */
    @Override
  public Set<String> getAttributeNames() {
    return so.getAttributeNames();
  }

  /** {@inheritDoc} */
    public Map<String, Object> getData() {
    return so.getData();
  }

    /**
     * Return security handlers for this shared object or <code>null</code> if none are found.
     *
     * @return
     */
    private Set<ISharedObjectSecurity> getSecurityHandlers() {
      ISharedObjectSecurityService security = (ISharedObjectSecurityService) ScopeUtils.getScopeService(getParent(), ISharedObjectSecurityService.class);
      if (security == null)
        return null;
     
      return security.getSharedObjectSecurity();
    }
   
    /**
     * Call handlers and check if connection to the existing SO is allowed.
     *
     * @return
     */
    protected boolean isConnectionAllowed() {
      // Check internal handlers first
    for (ISharedObjectSecurity handler: securityHandlers) {
      if (!handler.isConnectionAllowed(this)) {
        return false;
      }
    }
     
      // Check global SO handlers next
      final Set<ISharedObjectSecurity> handlers = getSecurityHandlers();
      if (handlers == null) {
        return true;
      }
     
      for (ISharedObjectSecurity handler: handlers) {
        if (!handler.isConnectionAllowed(this)) {
          return false;
        }
      }
     
      return true;
    }
   
    /**
     * Call handlers and check if writing to the SO is allowed.
     *
     * @param key
     * @param value
     * @return
     */
    protected boolean isWriteAllowed(String key, Object value) {
      // Check internal handlers first
      for (ISharedObjectSecurity handler: securityHandlers) {
        if (!handler.isWriteAllowed(this, key, value)) {
          return false;
        }
      }
     
      // Check global SO handlers next
      final Set<ISharedObjectSecurity> handlers = getSecurityHandlers();
      if (handlers == null) {
        return true;
      }
     
      for (ISharedObjectSecurity handler: handlers) {
        if (!handler.isWriteAllowed(this, key, value)) {
          return false;
        }
      }
     
      return true;
    }
   
    /**
     * Call handlers and check if deleting a property from the SO is allowed.
     *
     * @param key
     * @return
     */
    protected boolean isDeleteAllowed(String key) {
      // Check internal handlers first
      for (ISharedObjectSecurity handler: securityHandlers) {
        if (!handler.isDeleteAllowed(this, key)) {
          return false;
        }
      }
     
      // Check global SO handlers next
      final Set<ISharedObjectSecurity> handlers = getSecurityHandlers();
      if (handlers == null) {
        return true;
      }
     
      for (ISharedObjectSecurity handler: handlers) {
        if (!handler.isDeleteAllowed(this, key)) {
          return false;
        }
      }
     
      return true;
    }
   
    /**
     * Call handlers and check if sending a message to the clients connected to the
     * SO is allowed.
     *
     * @param message
     * @param arguments
     * @return
     */
    protected boolean isSendAllowed(String message, List<?> arguments) {
      // Check internal handlers first
      for (ISharedObjectSecurity handler: securityHandlers) {
        if (!handler.isSendAllowed(this, message, arguments)) {
          return false;
        }
      }
     
      // Check global SO handlers next
      final Set<ISharedObjectSecurity> handlers = getSecurityHandlers();
      if (handlers == null) {
        return true;
      }
     
      for (ISharedObjectSecurity handler: handlers) {
        if (!handler.isSendAllowed(this, message, arguments)) {
          return false;
        }
      }
     
      return true;
    }
   
  /** {@inheritDoc} */
    @Override
  public void dispatchEvent(IEvent e) {
    if (e.getType() != IEvent.Type.SHARED_OBJECT
        || !(e instanceof ISharedObjectMessage)) {
      // Don't know how to handle this event.
      super.dispatchEvent(e);
      return;
    }

    ISharedObjectMessage msg = (ISharedObjectMessage) e;
    if (msg.hasSource()) {
      beginUpdate(msg.getSource());
    } else {
      beginUpdate();
    }
    try {
      for (ISharedObjectEvent event : msg.getEvents()) {
        switch (event.getType()) {
          case SERVER_CONNECT:
            if (!isConnectionAllowed()) {
              so.returnError(SO_NO_READ_ACCESS);
            } else if (msg.hasSource()) {
              IEventListener source = msg.getSource();
              if (source instanceof BaseConnection) {
                ((BaseConnection) source).registerBasicScope(this);
              } else {
                addEventListener(source);
              }
            }
            break;
          case SERVER_DISCONNECT:
            if (msg.hasSource()) {
              IEventListener source = msg.getSource();
              if (source instanceof BaseConnection) {
                ((BaseConnection) source)
                    .unregisterBasicScope(this);
              } else {
                removeEventListener(source);
              }
            }
            break;
          case SERVER_SET_ATTRIBUTE:
            final String key = event.getKey();
            final Object value = event.getValue();
            if (!isWriteAllowed(key, value)) {
              so.returnAttributeValue(key);
              so.returnError(SO_NO_WRITE_ACCESS);
            } else {
              setAttribute(key, value);
            }
            break;
          case SERVER_DELETE_ATTRIBUTE:
            final String property = event.getKey();
            if (!isDeleteAllowed(property)) {
              so.returnAttributeValue(property);
              so.returnError(SO_NO_WRITE_ACCESS);
            } else {
              removeAttribute(property);
            }
            break;
          case SERVER_SEND_MESSAGE:
            final String message = event.getKey();
            final List<?> arguments = (List<?>) event.getValue();
            // Ignore request silently if not allowed
            if (isSendAllowed(message, arguments)) {
              sendMessage(message, arguments);
            }
            break;
          default:
            log.warn("Unknown SO event: " + event.getType());
        }
      }
    } finally {
      endUpdate();
    }
  }

  /** {@inheritDoc} */
    @Override
  public boolean setAttribute(String name, Object value) {
      boolean success;
    beginUpdate();
    try {
      success = so.setAttribute(name, value);
    } finally {
      endUpdate();
    }

    if (success) {
      for (ISharedObjectListener listener : serverListeners) {
        listener.onSharedObjectUpdate(this, name, value);
      }
        }
    return success;
  }

  /** {@inheritDoc} */
    @Override
  public void setAttributes(IAttributeStore values) {
    beginUpdate();
    try {
      so.setAttributes(values);
    } finally {
      endUpdate();
    }

    for (ISharedObjectListener listener : serverListeners) {
      listener.onSharedObjectUpdate(this, values);
    }
    }

  /** {@inheritDoc} */
    @Override
  public void setAttributes(Map<String, Object> values) {
    beginUpdate();
    try {
      so.setAttributes(values);
    } finally {
      endUpdate();
    }

    for (ISharedObjectListener listener : serverListeners) {
      listener.onSharedObjectUpdate(this, values);
    }
    }

  /** {@inheritDoc} */
    @Override
  public String toString() {
    return "Shared Object: " + getName();
  }

  /** {@inheritDoc} */
    public void addSharedObjectListener(
      ISharedObjectListener listener) {
    serverListeners.add(listener);
  }

  /** {@inheritDoc} */
    public void removeSharedObjectListener(
      ISharedObjectListener listener) {
    serverListeners.remove(listener);
  }

  /** {@inheritDoc} */
    public void registerServiceHandler(Object handler) {
    registerServiceHandler("", handler);
  }

  /** {@inheritDoc} */
    public void registerServiceHandler(String name, Object handler) {
    if (name == null) {
      name = "";
    }
    handlers.put(name, handler);
  }

  public void unregisterServiceHandler() {
    unregisterServiceHandler("");
  }

  /** {@inheritDoc} */
    public void unregisterServiceHandler(String name) {
    if (name == null) {
      name = "";
    }
    handlers.remove(name);
  }

  /** {@inheritDoc} */
    public Object getServiceHandler(String name) {
    if (name == null) {
      name = "";
    }
    return handlers.get(name);
  }

  /** {@inheritDoc} */
    public Set<String> getServiceHandlerNames() {
    return Collections.unmodifiableSet(handlers.keySet());
  }

  /**
   * Locks the shared object instance. Prevents any changes to this object by
   * clients until the SharedObject.unlock() method is called.
   */
  public void lock() {
    lock.lock();
  }

  /**
   * Unlocks a shared object instance that was locked with
   * SharedObject.lock().
   */
  public void unlock() {
    lock.unlock();
  }

  /**
   * Returns the locked state of this SharedObject.
   *
   * @return true if in a locked state; false otherwise
   */
  public boolean isLocked() {
    return lock.isLocked();
  }

  /** {@inheritDoc} */
    public boolean clear() {
      boolean success;
      beginUpdate();
      try {
        success = so.clear();
      } finally {
        endUpdate();
      }
     
    if (success) {
      for (ISharedObjectListener listener : serverListeners) {
        listener.onSharedObjectClear(this);
      }
        }
    return success;
  }

  /** {@inheritDoc} */
    public void close() {
    so.close();
    so = null;
  }
   
  /** {@inheritDoc} */
    public void acquire() {
    so.acquire();
  }

  /** {@inheritDoc} */
    public boolean isAcquired() {
    return so.isAcquired();
  }

  /** {@inheritDoc} */
    public void release() {
    so.release();
  }

  /** {@inheritDoc} */
    public ISharedObjectStatistics getStatistics() {
      return so;
    }
}
TOP

Related Classes of org.red5.server.so.SharedObjectScope

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.