Package com.kurento.kmf.content.internal.base

Source Code of com.kurento.kmf.content.internal.base.AbstractHttpContentSession

/*
* (C) Copyright 2013 Kurento (http://kurento.org/)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* 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.
*
*/
package com.kurento.kmf.content.internal.base;

import java.util.concurrent.Future;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;

import com.kurento.kmf.common.exception.Assert;
import com.kurento.kmf.common.exception.KurentoMediaFrameworkException;
import com.kurento.kmf.common.exception.internal.ExceptionUtils;
import com.kurento.kmf.common.exception.internal.ServletUtils;
import com.kurento.kmf.content.ContentHandler;
import com.kurento.kmf.content.ContentSession;
import com.kurento.kmf.content.HttpContentSession;
import com.kurento.kmf.content.internal.ContentSessionManager;
import com.kurento.kmf.content.internal.StreamingProxy;
import com.kurento.kmf.content.internal.StreamingProxyListener;
import com.kurento.kmf.content.jsonrpc.JsonRpcResponse;
import com.kurento.kmf.media.HttpEndpoint;
import com.kurento.kmf.media.events.ErrorEvent;
import com.kurento.kmf.media.events.MediaEventListener;
import com.kurento.kmf.media.events.MediaSessionStartedEvent;
import com.kurento.kmf.media.events.MediaSessionTerminatedEvent;
import com.kurento.kmf.repository.HttpSessionErrorEvent;
import com.kurento.kmf.repository.HttpSessionStartedEvent;
import com.kurento.kmf.repository.HttpSessionTerminatedEvent;
import com.kurento.kmf.repository.RepositoryHttpEndpoint;
import com.kurento.kmf.repository.RepositoryHttpEventListener;
import com.kurento.kmf.repository.RepositoryItem;

/**
*
* Abstract definition for HTTP based content request.
*
* @author Luis López (llopez@gsyc.es)
* @version 1.0.0
*/
public abstract class AbstractHttpContentSession extends AbstractContentSession
    implements HttpContentSession {

  @Autowired
  private StreamingProxy proxy;

  protected boolean useControlProtocol;

  protected boolean redirect;

  protected volatile Future<?> tunnellingProxyFuture;

  private RepositoryHttpEndpoint repositoryHttpEndpoint;

  public AbstractHttpContentSession(
      ContentHandler<? extends ContentSession> handler,
      ContentSessionManager manager, AsyncContext asyncContext,
      String contentId, boolean redirect, boolean useControlProtocol) {
    super(handler, manager, asyncContext, contentId);
    this.useControlProtocol = useControlProtocol;
    this.redirect = redirect;
    if (!useControlProtocol) {
      goToState(STATE.HANDLING, "Cannot go to STATE.HANDLING from state "
          + getState() + ". This condition should never happen", 1); // TODO
    }
  }

  protected abstract RepositoryHttpEndpoint createRepositoryHttpEndpoint(
      RepositoryItem repositoryItem);

  public void start(HttpEndpoint httpEndpoint) {
    try {
      releaseOnTerminate(httpEndpoint);
      Assert.notNull(httpEndpoint, "Illegal null httpEndpoint provided",
          10028);
      goToState(
          STATE.STARTING,
          "Cannot start HttpEndpoint in state "
              + getState()
              + ". This is probably due to an explicit session termination comming from another thread",
          1); // TODO

      getLogger().info(
          "Activating media for " + this.getClass().getSimpleName()
              + " with httpEndpoint provided ");

      activateMedia(httpEndpoint, null);

    } catch (KurentoMediaFrameworkException ke) {
      internalTerminateWithError(null, ke.getCode(), ke.getMessage(),
          null);
      throw ke;
    } catch (Throwable t) {
      KurentoMediaFrameworkException kmfe = new KurentoMediaFrameworkException(
          t.getMessage(), t, 20029);
      internalTerminateWithError(null, kmfe.getCode(), kmfe.getMessage(),
          null);
      throw kmfe;
    }
  }

  @Override
  public void start(RepositoryItem repositoryItem) {
    try {
      Assert.notNull(repositoryItem,
          "Illegal null repositoryItem provided", 10027);

      goToState(
          STATE.STARTING,
          "Cannot start HttpPlayerSession in state "
              + getState()
              + ". This is probably due to an explicit session termination comming from another thread",
          1); // TODO

      getLogger().info(
          "Activating media for " + this.getClass().getSimpleName()
              + " with repositoryItemId "
              + repositoryItem.getId());
      repositoryHttpEndpoint = createRepositoryHttpEndpoint(repositoryItem);

      activateMedia(repositoryHttpEndpoint);
    } catch (KurentoMediaFrameworkException ke) {
      internalTerminateWithError(null, ke.getCode(), ke.getMessage(),
          null);
      throw ke;
    } catch (Throwable t) {
      KurentoMediaFrameworkException kmfe = new KurentoMediaFrameworkException(
          t.getMessage(), t, 20029);
      internalTerminateWithError(null, kmfe.getCode(), kmfe.getMessage(),
          null);
      throw kmfe;
    }
  }

  /*
   * This is an utility method designed for minimizing code replication. For
   * it to work, one and only one of the two parameters must be null;
   */
  protected void activateMedia(HttpEndpoint httpEndpoint,
      final Runnable runOnContentStart) {

    Assert.isTrue(httpEndpoint != null,
        "Internal error. Cannot activate null HttpEndpoint reference",
        1); // TODO

    // Manage fatal errors occurring in the pipeline
    httpEndpoint.getMediaPipeline().addErrorListener(
        new MediaEventListener<ErrorEvent>() {
          @Override
          public void onEvent(ErrorEvent error) {
            getLogger().error(error.getDescription()); // TODO:
                                  // improve
                                  // message
            internalTerminateWithError(null, error.getErrorCode(),
                error.getDescription(), null);
          }
        });

    // Generate appropriate actions when content is started
    httpEndpoint
        .addMediaSessionStartedListener(new MediaEventListener<MediaSessionStartedEvent>() {
          @Override
          public void onEvent(MediaSessionStartedEvent event) {
            callOnContentStartedOnHanlder();
            getLogger().info(
                "Received event with type " + event.getType());
            if (runOnContentStart != null) {
              runOnContentStart.run();
            }
          }
        });

    // Manage end of media session
    httpEndpoint
        .addMediaSessionTerminatedListener(new MediaEventListener<MediaSessionTerminatedEvent>() {

          @Override
          public void onEvent(MediaSessionTerminatedEvent event) {
            internalTerminateWithoutError(null, 1, "TODO", null);// TODO

          }
        });

    // Generate appropriate actions when media session is terminated

    String answerUrl = httpEndpoint.getUrl();
    getLogger().info("HttpEndpoint.getUrl = " + answerUrl);

    Assert.notNull(answerUrl, "Received null url from HttpEndpoint", 20012);
    Assert.isTrue(answerUrl.length() > 0,
        "Received invalid empty url from media server", 20012);

    goToState(
        STATE.ACTIVE,
        "Cannot start session in sate "
            + getState()
            + ". This is probably due to an explicit termination of the session from a different threaad",
        1);

    if (useControlProtocol) {
      answerActivateMediaRequest4JsonControlProtocolConfiguration(answerUrl);
    } else if (redirect) {
      answerActivateMediaRequest4SimpleHttpConfigurationWithRedirect(answerUrl);
    } else {
      answerActivateMediaRequest4SimpleHttpConfigurationWithTunnel(answerUrl);
    }
  }

  protected void activateMedia(RepositoryHttpEndpoint repositoryHttpEndpoint) {

    // Manage fatal errors occurring in the pipeline
    repositoryHttpEndpoint
        .addSessionErrorListener(new RepositoryHttpEventListener<HttpSessionErrorEvent>() {
          @Override
          public void onEvent(HttpSessionErrorEvent event) {
            getLogger().error(event.getDescription()); // TODO:
            internalTerminateWithError(null, 1, // TODO
                event.getDescription(), null);
          }
        });

    // Generate appropriate actions when content is started
    // TODO: addMediaSessionStartListener (symmetry)
    repositoryHttpEndpoint
        .addSessionStartedListener(new RepositoryHttpEventListener<HttpSessionStartedEvent>() {

          @Override
          public void onEvent(HttpSessionStartedEvent event) {
            callOnContentStartedOnHanlder();
            getLogger().info(
                "Received event with type "
                    + event.getClass().getSimpleName());
          }
        });

    repositoryHttpEndpoint
        .addSessionTerminatedListener(new RepositoryHttpEventListener<HttpSessionTerminatedEvent>() {

          @Override
          public void onEvent(HttpSessionTerminatedEvent event) {
            getLogger().info(
                "Received event with type "
                    + event.getClass().getSimpleName());
            internalTerminateWithoutError(null, 1,
                "MediaServer MediaSessionTerminated", null); // TODO
          }
        });

    String answerUrl = repositoryHttpEndpoint.getURL();
    getLogger().info("RepoItemHttpElement.getUrl = " + answerUrl);

    Assert.notNull(answerUrl, "Received null url from RepoItemHttpElement",
        20012);
    Assert.isTrue(answerUrl.length() > 0,
        "Received invalid empty url from RepoItemHttpElement", 20012);

    goToState(
        STATE.ACTIVE,
        "Cannot start session in sate "
            + getState()
            + ". This is probably due to an explicit termination of the session from a different threaad",
        1);

    if (useControlProtocol) {
      answerActivateMediaRequest4JsonControlProtocolConfiguration(answerUrl);
    } else if (redirect) {
      answerActivateMediaRequest4SimpleHttpConfigurationWithRedirect(answerUrl);
    } else {
      answerActivateMediaRequest4SimpleHttpConfigurationWithDispatch(repositoryHttpEndpoint
          .getDispatchURL());
    }

  }

  /**
   * Provide an HTTP response, depending of which redirect strategy is used:
   * it could a redirect (HTTP 307, Temporary Redirect), or a tunneled
   * response using the Streaming Proxy.
   *
   * @param url
   *            Content URL
   * @throws ContentException
   *             Exception in the media server
   */
  private void answerActivateMediaRequest4SimpleHttpConfigurationWithRedirect(
      String url) {
    try {
      HttpServletResponse response = (HttpServletResponse) initialAsyncCtx
          .getResponse();
      getLogger().info("Sending redirect to " + url);
      response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
      response.setHeader("Location", url);

    } catch (Throwable t) {
      throw new KurentoMediaFrameworkException(t.getMessage(), t, 20013);
    } finally {
      initialAsyncCtx.complete();
      initialAsyncCtx = null;
    }
  }

  private void answerActivateMediaRequest4SimpleHttpConfigurationWithTunnel(
      String url) {
    try {
      HttpServletResponse response = (HttpServletResponse) initialAsyncCtx
          .getResponse();
      final HttpServletRequest request = (HttpServletRequest) initialAsyncCtx
          .getRequest();
      getLogger().info("Activating tunneling proxy to " + url);
      tunnellingProxyFuture = proxy.tunnelTransaction(request, response,
          url, new StreamingProxyListener() {

            @Override
            public void onProxySuccess() {
              tunnellingProxyFuture = null;
              // Parameters no matter, no answer will be sent
              // given that we are already in ACTIVE state
              terminate(0, "");
              request.getAsyncContext().complete();
            }

            @Override
            public void onProxyError(String message, int errorCode) {
              tunnellingProxyFuture = null;
              // Parameters no matter, no answer will be sent
              // given that we are already in ACTIVE state
              terminate(errorCode, message);
              request.getAsyncContext().complete();
            }
          });

    } catch (Throwable t) {
      throw new KurentoMediaFrameworkException(t.getMessage(), t, 20013);
    } finally {
      initialAsyncCtx = null;
    }
  }

  private void answerActivateMediaRequest4SimpleHttpConfigurationWithDispatch(
      String path) {
    try {
      initialAsyncCtx.dispatch(path);
    } catch (Throwable t) {
      throw new KurentoMediaFrameworkException(t.getMessage(), t, 20013);
    } finally {
      initialAsyncCtx = null;
    }
  }

  /**
   * Provide an HTTP response, when a JSON signaling protocol strategy is
   * used.
   *
   * @param url
   *            Content URL
   * @throws ContentException
   *             Exception in the media server
   */
  private void answerActivateMediaRequest4JsonControlProtocolConfiguration(
      String url) {
    protocolManager.sendJsonAnswer(initialAsyncCtx,
        JsonRpcResponse.newStartUrlResponse(url, sessionId,
            initialJsonRequest.getId()));
    initialAsyncCtx = null;
    initialJsonRequest = null;
  }

  /**
   * Control protocol accessor (getter).
   *
   * @return Control protocol strategy
   */
  @Override
  public boolean useControlProtocol() {
    return useControlProtocol;
  }

  @Override
  protected void sendErrorAnswerOnInitialContext(int code, String description) {
    if (useControlProtocol) {
      super.sendErrorAnswerOnInitialContext(code, description);
    } else {
      try {
        ServletUtils.sendHttpError(
            (HttpServletRequest) initialAsyncCtx.getRequest(),
            (HttpServletResponse) initialAsyncCtx.getResponse(),
            ExceptionUtils.getHttpErrorCode(code), description);
      } catch (ServletException e) {
        getLogger().error(e.getMessage(), e);
        throw new KurentoMediaFrameworkException(e, 20026);
      }
    }
  }

  @Override
  protected void sendRejectOnInitialContext(int code, String description) {
    if (useControlProtocol) {
      super.sendRejectOnInitialContext(code, description);
    } else {
      try {
        ServletUtils.sendHttpError(
            (HttpServletRequest) initialAsyncCtx.getRequest(),
            (HttpServletResponse) initialAsyncCtx.getResponse(),
            ExceptionUtils.getHttpErrorCode(code), description);
      } catch (ServletException e) {
        getLogger().error(e.getMessage(), e);
        throw new KurentoMediaFrameworkException(e, 20026);
      }
    }
  }

  /**
   * Release Streaming proxy.
   */
  @Override
  protected synchronized void destroy() {
    super.destroy();

    if (repositoryHttpEndpoint != null) {
      repositoryHttpEndpoint.stop();
      repositoryHttpEndpoint = null;
    }

    Future<?> localTunnelingProxyFuture = tunnellingProxyFuture;
    if (localTunnelingProxyFuture != null) {
      localTunnelingProxyFuture.cancel(true);
      tunnellingProxyFuture = null;
    }
  }
}
TOP

Related Classes of com.kurento.kmf.content.internal.base.AbstractHttpContentSession

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.