Package org.ugate.service.web

Source Code of org.ugate.service.web.WebServer

package org.ugate.service.web;

import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.EnumSet;

import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.ugate.UGateEvent;
import org.ugate.UGateEvent.Type;
import org.ugate.UGateKeeper;
import org.ugate.service.entity.ActorType;
import org.ugate.service.entity.EntityExtractor;
import org.ugate.service.entity.RoleType;
import org.ugate.service.entity.jpa.Actor;
import org.ugate.service.web.ui.WebApplication;
import org.ugate.service.web.ui.WebFilter;

/**
* Embedded web {@linkplain Server}
*/
public class WebServer {

  private static final Logger log = LoggerFactory.getLogger(WebServer.class);
  public static final String VAR_REMOTE_USER = "remoteUser";
  public static final String VAR_CONTENT_NAME = "content";
  public static final String VAR_TITLE_NAME = "title";
  public static final String VAR_HEADER_NAME = "header";
  public static final String VAR_FOOTER_NAME = "footer";
  public static final String VAR_URL_NAME = "url";
  public static final String VAR_URL_AJAX_UPDATE_NAME = "ajaxUpdateUrl";
  public static final String VAR_URI_WEB_SOCKET_NAME = "webSocketUri";
  public static final String VAR_REMOTE_NODES_NAME = "remoteNodes";
  public static final String VAR_REMOTE_NODE_READING_NAME = "rnr";
  public static final String VAR_REMOTE_NODE_NAME = "rn";
  public static final String VAR_COMMAND_NAME = "command";
  public static final String VAR_ACTION_NAME = "action";
  public static final String VAR_ACTION_CONNECT_NAME = "connect";
  public static final String[] PROTOCOL_INCLUDE = new String[] { "TLSv1",
      "TLSv1.1", "TLSv1.2" };
  private Server server;
  private HostKeyStore hostKeyStore;
  private final SignatureAlgorithm sa;
  private final EntityExtractor<Actor> actorExtractor;

  /**
   * Constructor
   *
   * @param actorExtractor
   *            the {@link EntityExtractor} for the {@link Actor}
   * @param sa
   *            the {@linkplain SignatureAlgorithm} to use when the
   *            {@linkplain X509Certificate} needs to be created/signed
   * @return the started {@linkplain WebServer}
   */
  private WebServer(final EntityExtractor<Actor> actorExtractor,
      final SignatureAlgorithm sa) {
    this.actorExtractor = actorExtractor;
    this.sa = sa;
  }

  /**
   * Starts the {@linkplain WebServer} in a new {@linkplain Thread}
   *
   * @param actorExtractor
   *            the {@link EntityExtractor} for the {@link Actor} (owner) of
   *            the {@link WebServer}
   * @param sa
   *            the {@linkplain SignatureAlgorithm} to use when the
   *            {@linkplain X509Certificate} needs to be created/signed
   * @return the started {@linkplain WebServer}
   */
  public static final WebServer start(
      final EntityExtractor<Actor> actorExtractor,
      final SignatureAlgorithm sa) {
    final WebServer webServer = new WebServer(actorExtractor, sa);
    final Thread webServerAgent = new Thread(Thread.currentThread()
        .getThreadGroup(), new Runnable() {
      @Override
      public void run() {
        try {
          webServer.startServer();
        } catch (final Throwable t) {
          log.error("Failed to start web server", t);
        }
      }
    }, WebServer.class.getSimpleName() + '-' + System.currentTimeMillis());
    webServerAgent.setDaemon(true);
    webServerAgent.start();
    return webServer;
  }

  /**
   * Starts the {@linkplain WebServer}
   */
  protected final void startServer() {
    // Additional reference examples can be found at:
    // http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/examples/embedded/src/main/java/org/eclipse/jetty/embedded
    try {
      UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
          WebServer.this, Type.WEB_INITIALIZE, false));
      // Get server from configuration file
      // final Resource serverXml =
      // Resource.newSystemResource("META-INF/jetty.xml");
      // final XmlConfiguration configuration = new
      // XmlConfiguration(serverXml.getInputStream());
      // server = (Server) configuration.configure();
      if (actorExtractor.extract() == null
          || actorExtractor.extract().getId() <= 0) {
        throw new NullPointerException(Actor.class.getName()
            + " cannot be null");
      } else if (actorExtractor.extract().getId() <= 0) {
        throw new IllegalArgumentException(
            String.format("%1$s#%2$s cannot be %3$s",
                Actor.class.getName(), ActorType.ID.getKey(),
                actorExtractor.extract().getId()));
      }
      server = new Server();
      server.addLifeCycleListener(new LifeCycle.Listener() {
        @Override
        public void lifeCycleStopping(final LifeCycle lifeCycle) {
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
              WebServer.this, Type.WEB_DISCONNECTING, false));
        }

        @Override
        public void lifeCycleStopped(final LifeCycle lifeCycle) {
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
              WebServer.this, Type.WEB_DISCONNECTED, false));
        }

        @Override
        public void lifeCycleStarting(final LifeCycle lifeCycle) {
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
              WebServer.this, Type.WEB_CONNECTING, false));
        }

        @Override
        public void lifeCycleStarted(final LifeCycle lifeCycle) {
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
              WebServer.this, Type.WEB_CONNECTED, false));
        }

        @Override
        public void lifeCycleFailure(final LifeCycle lifeCycle,
            final Throwable t) {
          log.error(
              String.format("%1$s failure",
                  LifeCycle.class.getName()), t);
          UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
              WebServer.this, Type.WEB_CONNECT_FAILED, false, t
                  .getMessage()));
        }
      });

      // SSL for HTTPS and SPDY (X.509 Setup)
      // TODO : Add password control to the key store
      hostKeyStore = HostKeyStore.loadOrCreate(actorExtractor.extract()
          .getHost(), "", sa);
      final SslContextFactory sslCnxt = new SslContextFactory();
      // sslCnxt.setCertAlias(hostKeyStore.getAlias());
      sslCnxt.setKeyStore(hostKeyStore.getKeyStore());
      sslCnxt.setKeyStoreType(hostKeyStore.getKeyStore().getType());
      sslCnxt.setKeyStoreProvider(hostKeyStore.getKeyStore()
          .getProvider().getName());
      sslCnxt.setIncludeProtocols(PROTOCOL_INCLUDE);
      sslCnxt.setTrustAll(false);

      // HTTP Configuration
      final HttpConfiguration httpConf = new HttpConfiguration();
      httpConf.setSecureScheme(HttpScheme.HTTPS.asString());
      httpConf.setSecurePort(actorExtractor.extract().getHost()
          .getWebPort());
      // httpConf.setOutputBufferSize(32768);

      // HTTP connector
      final ServerConnector http = new ServerConnector(server,
          new HttpConnectionFactory(httpConf));
      http.setHost(actorExtractor.extract().getHost().getWebHostLocal());
      http.setPort(actorExtractor.extract().getHost().getWebPortLocal());
      // http.setIdleTimeout(30000);

      // HTTPS Configuration
      final HttpConfiguration httpsConf = new HttpConfiguration(httpConf);
      httpsConf.addCustomizer(new SecureRequestCustomizer());

      // HTTPS connector
      final ServerConnector https = new ServerConnector(server,
          new SslConnectionFactory(sslCnxt, "http/1.1"),
          new HttpConnectionFactory(httpsConf));
      https.setHost(actorExtractor.extract().getHost().getWebHost());
      https.setPort(actorExtractor.extract().getHost().getWebPort());

      server.setConnectors(new Connector[] { http, https });

      // Serve via servlet
      final EnumSet<DispatcherType> dispatchers = EnumSet.range(
          DispatcherType.FORWARD, DispatcherType.ERROR);
      final ServletContextHandler context = new ServletContextHandler(
          ServletContextHandler.SESSIONS
              | ServletContextHandler.SECURITY);

      // final SessionIdManager idManager = new HashSessionIdManager();
      // final SessionManager sessionManager = new HashSessionManager();
      // SessionHandler sessionHandler = new
      // SessionHandler(sessionManager);
      // sessionManager.setSessionIdManager(idManager);
      // sessionManager.setSessionHandler(sessionHandler);
      // handler.setSessionHandler(sessionHandler);

      context.setContextPath("/");
      final FilterHolder fh = new FilterHolder(WebFilter.class);
      // let the filter know who to use to auto authenticate against when
      // accessed by host machine
      fh.setInitParameter(ActorType.ID.name(),
          String.valueOf(actorExtractor.extract().getId()));
      context.addFilter(fh, "/*", dispatchers);
      final ServletHolder sh = new ServletHolder(
          UGateWebSocketServlet.class);
      context.addServlet(sh,
          "/" + UGateWebSocketServlet.class.getSimpleName());
      final ServletHolder sh2 = new ServletHolder(
          UGateAjaxUpdaterServlet.class);
      context.addServlet(sh2, "/*");
      context.setErrorHandler(new ErrorHandler() {
        @Override
        public void handle(String target, Request baseRequest,
            HttpServletRequest request, HttpServletResponse response)
            throws IOException {
          // delegate errors to the filter
          final String reason = (response instanceof Response) ? ((Response) response)
              .getReason() : null;
          WebFilter.handleErrorPage(request, response,
              baseRequest.getServletContext(),
              response.getStatus(), reason);
        }
      });

      // Serve files from internal resource location
      // final org.eclipse.jetty.webapp.WebAppContext handler = new
      // org.eclipse.jetty.webapp.WebAppContext();
      // final String webappDirInsideJar =
      // this.getClass().getResource("webapp").toExternalForm();
      // handler.setWar(webappDirInsideJar);

      addSecurityHandler(context);

      // server.setDumpAfterStart(true);
      server.start();
      server.join();
    } catch (final Throwable e) {
      log.error("Unable to start web server", e);
      UGateKeeper.DEFAULT.notifyListeners(new UGateEvent<>(
          WebServer.this, Type.WEB_INITIALIZE_FAILED, false));
    } finally {

    }
  }

  /**
   * Programmatic approach to <a
   * href="http://wiki.eclipse.org/Jetty/Howto/Secure_Passwords">Jetty's
   * command line password generation</a>
   *
   * @param username
   *            the login ID
   * @param password
   *            the password
   * @return a {@linkplain String} array of {@linkplain Password}, Obfuscated,
   *         {@linkplain Credential.MD5#digest(String)} and
   *         {@linkplain Credential.Crypt#crypt(String, String)} versions of
   *         the user name and password
   */
  public static String[] createRealmProperty(final String username,
      final String password) {
    // http://wiki.eclipse.org/Jetty/Howto/Secure_Passwords
    final Password pw = new Password(password);
    final String obf = Password.obfuscate(pw.toString());
    final String digest = Credential.MD5.digest(pw.toString());
    final String crypt = Credential.Crypt.crypt(username, pw.toString());
    log.info(String.format(
        "Password: %1$s, Obfuscated: %2$s, Digest: %3$s, Crypt: %4$s",
        pw.toString(), obf, digest, crypt));
    return new String[] { pw.toString(), obf, digest, crypt };
  }

  /**
   * Adds a {@linkplain FormAuthenticator} via {@linkplain JPALoginService}.
   *
   * @param context
   *            the {@linkplain HandlerWrapper} to add authentication to
   */
  protected void addSecurityHandler(final ServletContextHandler context) {
    // final JDBCLoginService loginService = new JDBCLoginService();
    // loginService.setConfig("META-INF/realm.properties");
    // final HashLoginService loginService = new HashLoginService("MyRealm",
    // "META-INF/realm.properties");
    final JPALoginService loginService = new JPALoginService();
    // server.addBean(loginService);

    final ConstraintSecurityHandler security = new ConstraintSecurityHandler();

    final Constraint constraint = new Constraint();
    constraint.setName(Constraint.__FORM_AUTH);
    constraint.setAuthenticate(true);
    constraint.setRoles(RoleType.names());

    final ConstraintMapping cm = new ConstraintMapping();
    cm.setPathSpec("/*");
    cm.setConstraint(constraint);

    security.setConstraintMappings(Arrays
        .asList(new ConstraintMapping[] { cm }));
    final FormAuthenticator fa = new FormAuthenticator(
        WebApplication.ControllerResource.LOGIN.path(),
        WebApplication.ControllerResource.ERROR.path(), false);
    security.setAuthenticator(fa);
    // security.setAuthenticator(new DigestAuthenticator());
    security.setLoginService(loginService);

    context.setSecurityHandler(security);
    server.setHandler(context);
  }

  /**
   * Stops the {@linkplain WebServer}
   */
  public final void stop() {
    try {
      if (server != null && !server.isStopped() && !server.isStopping()) {
        server.stop();
      }
      server.destroy();
      hostKeyStore = null;
    } catch (final Throwable t) {
      log.error("Unable to shutdown web server", t);
    }
  }

  /**
   * @return {@linkplain Server#isRunning()}
   */
  public final boolean isRunning() {
    return server != null && server.isRunning();
  }

  /**
   * @return the {@link EntityExtractor} for the {@link Actor} (owner) of the
   *         {@link WebServer}
   */
  public EntityExtractor<Actor> getActorExtractor() {
    return actorExtractor;
  }
}
TOP

Related Classes of org.ugate.service.web.WebServer

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.