All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.exhibitor.application.ExhibitorMain Maven / Gradle / Ivy

The newest version!
/*
 *
 *  Copyright 2011 Netflix, Inc.
 *
 *     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 com.netflix.exhibitor.application;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.netflix.exhibitor.servlet.ExhibitorServletFilter;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import com.netflix.exhibitor.core.Exhibitor;
import com.netflix.exhibitor.core.ExhibitorArguments;
import com.netflix.exhibitor.core.RemoteConnectionConfiguration;
import com.netflix.exhibitor.core.backup.BackupProvider;
import com.netflix.exhibitor.core.config.ConfigProvider;
import com.netflix.exhibitor.core.rest.UIContext;
import com.netflix.exhibitor.core.rest.jersey.JerseySupport;
import com.netflix.exhibitor.standalone.ExhibitorCreator;
import com.netflix.exhibitor.standalone.ExhibitorCreatorExit;
import com.netflix.exhibitor.standalone.SecurityArguments;
import com.sun.jersey.api.client.filter.ClientFilter;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter;
import com.sun.jersey.api.core.DefaultResourceConfig;
import org.apache.curator.utils.CloseableUtils;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.ContextHandler;
import org.mortbay.jetty.security.HashUserRealm;
import org.mortbay.jetty.security.SecurityHandler;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.jetty.webapp.WebXmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

public class ExhibitorMain implements Closeable
{
    private final Logger log = LoggerFactory.getLogger(getClass());
    private final Server server;
    private final AtomicBoolean isClosed = new AtomicBoolean(false);
    private final Exhibitor exhibitor;
    private final AtomicBoolean shutdownSignaled = new AtomicBoolean(false);
    private final Map users = Maps.newHashMap();

    public static void main(String[] args) throws Exception
    {
        ExhibitorCreator creator;
        try
        {
            creator = new ExhibitorCreator(args);
        }
        catch ( ExhibitorCreatorExit exit )
        {
            if ( exit.getError() != null )
            {
                System.err.println(exit.getError());
            }

            exit.getCli().printHelp();
            return;
        }

        SecurityArguments securityArguments = new SecurityArguments(creator.getSecurityFile(), creator.getRealmSpec(), creator.getRemoteAuthSpec());
        ExhibitorMain exhibitorMain = new ExhibitorMain
        (
            creator.getBackupProvider(),
            creator.getConfigProvider(),
            creator.getBuilder(),
            creator.getHttpPort(),
            creator.getListenAddress(),
            creator.getSecurityHandler(),
            securityArguments
        );
        setShutdown(exhibitorMain);

        try {
            exhibitorMain.start();
        }
        catch (Exception ex) {
            ex.printStackTrace(System.err);
            System.err.println(String.format("Failed to start HTTP server on address %s, port %d. Exiting", creator.getListenAddress(), creator.getHttpPort()));
            Runtime.getRuntime().exit(1);
        }

        try {
            exhibitorMain.join();
        }
        finally
        {
            exhibitorMain.close();

            for ( Closeable closeable : creator.getCloseables() )
            {
                CloseableUtils.closeQuietly(closeable);
            }
        }
    }

    public ExhibitorMain(BackupProvider backupProvider, ConfigProvider configProvider, ExhibitorArguments.Builder builder, int httpPort, String listenAddress, SecurityHandler security, SecurityArguments securityArguments) throws Exception
    {
        HashUserRealm realm = makeRealm(securityArguments);
        if ( securityArguments.getRemoteAuthSpec() != null )
        {
            addRemoteAuth(builder, securityArguments.getRemoteAuthSpec());
        }

        builder.shutdownProc(makeShutdownProc(this));
        exhibitor = new Exhibitor(configProvider, null, backupProvider, builder.build());
        exhibitor.start();

        DefaultResourceConfig   application = JerseySupport.newApplicationConfig(new UIContext(exhibitor));
        ServletContainer        container = new ServletContainer(application);
        server = new Server();
        SocketConnector http = new SocketConnector();
        http.setHost(listenAddress);
        http.setPort(httpPort);
        server.addConnector(http);

        Context root = new Context(server, "/", Context.SESSIONS);
        root.addFilter(ExhibitorServletFilter.class, "/", Handler.ALL);
        root.addServlet(new ServletHolder(container), "/*");
        if ( security != null )
        {
            root.setSecurityHandler(security);
        }
        else if ( securityArguments.getSecurityFile() != null )
        {
            addSecurityFile(realm, securityArguments.getSecurityFile(), root);
        }
    }

    private void addRemoteAuth(ExhibitorArguments.Builder builder, String remoteAuthSpec)
    {
        String[] parts = remoteAuthSpec.split(":");
        Preconditions.checkArgument(parts.length == 2, "Badly formed remote client authorization: " + remoteAuthSpec);

        String type = parts[0].trim();
        String userName = parts[1].trim();

        String password = Preconditions.checkNotNull(users.get(userName), "Realm user not found: " + userName);

        ClientFilter filter;
        if ( type.equals("basic") )
        {
            filter = new HTTPBasicAuthFilter(userName, password);
        }
        else if ( type.equals("digest") )
        {
            filter = new HTTPDigestAuthFilter(userName, password);
        }
        else
        {
            throw new IllegalStateException("Unknown remote client authorization type: " + type);
        }

        builder.remoteConnectionConfiguration(new RemoteConnectionConfiguration(Arrays.asList(filter)));
    }

    public void start() throws Exception
    {
        server.start();
    }

    public void join()
    {
        try
        {
            while ( !shutdownSignaled.get() && !Thread.currentThread().isInterrupted() )
            {
                Thread.sleep(5000);
            }
        }
        catch ( InterruptedException e )
        {
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public void close() throws IOException
    {
        if ( isClosed.compareAndSet(false, true) )
        {
            log.info("Shutting down");

            CloseableUtils.closeQuietly(exhibitor);
            try
            {
                server.stop();
            }
            catch ( Exception e )
            {
                log.error("Error shutting down Jetty", e);
            }
            server.destroy();
        }
    }

    private static void setShutdown(final ExhibitorMain exhibitorMain)
    {
        Runtime.getRuntime().addShutdownHook
        (
            new Thread
            (
                makeShutdownProc(exhibitorMain)
            )
        );
    }

    private static Runnable makeShutdownProc(final ExhibitorMain exhibitorMain)
    {
        return new Runnable()
        {
            @Override
            public void run()
            {
                exhibitorMain.shutdownSignaled.set(true);
            }
        };
    }

    private void addSecurityFile(HashUserRealm realm, String securityFile, Context root) throws Exception
    {
        // create a temp Jetty context to parse the security portion of the web.xml file

        /*
            TODO

            This code assumes far too much internal knowledge of Jetty. I don't know
            of simple way to parse the web.xml though and don't want to write it myself.
         */

        final URL url = new URL("file", null, securityFile);
        final WebXmlConfiguration webXmlConfiguration = new WebXmlConfiguration();
        WebAppContext context = new WebAppContext();
        context.setServer(server);
        webXmlConfiguration.setWebAppContext(context);
        ContextHandler contextHandler = new ContextHandler("/")
        {
            @Override
            protected void startContext() throws Exception
            {
                super.startContext();
                setServer(server);
                webXmlConfiguration.configure(url.toString());
            }
        };
        try
        {
            SecurityHandler securityHandler = webXmlConfiguration.getWebAppContext().getSecurityHandler();

            if ( realm != null )
            {
                securityHandler.setUserRealm(realm);
            }

            root.setSecurityHandler(securityHandler);
            contextHandler.start();
        }
        finally
        {
            contextHandler.stop();
        }
    }

    private HashUserRealm makeRealm(SecurityArguments securityArguments) throws Exception
    {
        if ( securityArguments.getRealmSpec() == null )
        {
            return null;
        }

        String[] parts = securityArguments.getRealmSpec().split(":");
        if ( parts.length != 2 )
        {
            throw new Exception("Bad realm spec: " + securityArguments.getRealmSpec());
        }

        return new HashUserRealm(parts[0].trim(), parts[1].trim())
        {
            @Override
            public Object put(Object name, Object credentials)
            {
                users.put(String.valueOf(name), String.valueOf(credentials));

                return super.put(name, credentials);
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy