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

org.glassfish.internal.embedded.Server Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.internal.embedded;

import org.glassfish.api.container.Sniffer;
import org.glassfish.embeddable.*;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.jvnet.hk2.annotations.Contract;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;



/**
 * Instances of server are embedded application servers, capable of attaching various containers
 * (entities running users applications).
 *
 * @author Jerome Dochez
 */
@Contract
public class Server {

    /**
     * Builder for creating embedded server instance. Builder can be used to configure
     * the logger, the verbosity and the embedded file system which acts as a
     * virtual file system to the embedded server instance.
     */
    public static class Builder {
        final String serverName;
        EmbeddedFileSystem fileSystem;

        /**
         * Creates an unconfigured instance. The habitat will be obtained
         * by scanning the inhabitants files using this class's classloader
         *
         * @param id the server name
         */
        public Builder(String id) {
            this.serverName = id;
        }

        /**
         * Enables or disables the logger for this server
         *
         * @param enabled true to enable, false to disable
         * @return this instance
         */
        public Builder logger(boolean enabled) {
            return this;
        }

        /**
         * Sets the log file location
         *
         * @param f a valid file location
         * @return this instance
         */
        public Builder logFile(File f) {
            return this;
        }

        /**
         * Turns on of off the verbose flag.
         *
         * @param b true to turn on, false to turn off
         * @return this instance
         */
        public Builder verbose(boolean b) {
            return this;
        }

        /**
         * Set the jmx port number. Also enables the jmx connector. This applies
         * only when the default configuration is being used.
         * 
         * @param portNumber jmx port number.
         * @return this instance
         */
        public Builder jmxPort(int portNumber) {
            return this;
        }

        /**
         * Sets the embedded file system for the application server, used to locate
         * important files or directories used through the server lifetime.
         *
         * @param fileSystem a virtual filesystem
         * @return this instance
         */
        public Builder embeddedFileSystem(EmbeddedFileSystem fileSystem) {
            this.fileSystem = fileSystem;
            return this;
        }
        /**
         * Uses this builder's name to create or return an existing embedded
         * server instance.
         * The embedded server will be using the configured parameters
         * of this builder. If no embedded file system is used, the embedded instance will use
         * a temporary instance root with a default basic configuration. That temporary instance
         * root will be deleted once the server is shutdown.
         *
         * @return the configured server instance
         */
        public Server build() {
            return build(null);
        }

        /**
         * Uses this builder's name to create or return an existing embedded
         * server instance.
         * The embedded server will be using the configured parameters
         * of this builder. If no embedded file system is used, the embedded instance will use
         * a temporary instance root with a default basic configuration. That temporary instance
         * root will be deleted once the server is shutdown.
         *
         * @param properties extra creation properties
         *
         * @return the configured server instance
         */
        public Server build(Properties properties) {
            synchronized(servers) {
                if (!servers.containsKey(serverName)) {
                    Server s = new Server(this, properties);
                    servers.put(serverName, s);
                    return s;
                }
                throw new IllegalStateException("An embedded server of this name already exists");
            }
        }
    }

    private final static class Container {
        private final EmbeddedContainer container;
        boolean started;

        private Container(EmbeddedContainer container) {
            this.container = container;
        }
        
    }

    private final static Map servers = new HashMap();

    private final String serverName;
    private final ServiceHandle fileSystem;
    private final ServiceLocator habitat;
    private final List containers = new ArrayList();
    private final GlassFish glassfish;
    private static GlassFishRuntime glassfishRuntime;

    private static final Logger logger = Logger.getAnonymousLogger();

    private void setBootstrapProperties(BootstrapProperties props, EmbeddedFileSystem fs) {
        props.setProperty("GlassFish_Platform", "Static");
        if (fs != null) {
            String instanceRoot = fs.instanceRoot != null ? fs.instanceRoot.getAbsolutePath() : null;
            String installRoot = fs.installRoot != null ? fs.installRoot.getAbsolutePath() : instanceRoot;
            if (installRoot != null) {
                props.setInstallRoot(installRoot);
            }
        }
    }

    private void setGlassFishProperties(GlassFishProperties props, EmbeddedFileSystem fs) {
        props.setProperty("-type", "EMBEDDED");
        props.setProperty("org.glassfish.persistence.embedded.weaving.enabled", "false");
        if (fs != null) {
            String instanceRoot = fs.instanceRoot != null ? fs.instanceRoot.getAbsolutePath() : null;
            if (instanceRoot != null) {
                props.setInstanceRoot(fs.instanceRoot.getAbsolutePath());
            }
            if (fs.configFile != null) {
                props.setConfigFileURI(fs.configFile.toURI().toString());
            }
            if (fs.autoDelete) {
                props.setProperty("org.glassfish.embeddable.autoDelete", "true");
            }
        }
        // TODO :: Support modification of jmxPort
    }
    
    private Server(Builder builder, Properties properties) {
        serverName = builder.serverName;
        
        try {
            if(properties == null) {
                properties = new Properties();
            }
            EmbeddedFileSystem fs = builder.fileSystem;
            BootstrapProperties bootstrapProps = new BootstrapProperties(properties);
            setBootstrapProperties(bootstrapProps, fs);
            glassfishRuntime = GlassFishRuntime.bootstrap(bootstrapProps, getClass().getClassLoader());

            GlassFishProperties gfProps = new GlassFishProperties(properties);
            setGlassFishProperties(gfProps, fs);
            glassfish = glassfishRuntime.newGlassFish(gfProps);
            glassfish.start();
            
            if(fs == null) {
                EmbeddedFileSystem.Builder fsBuilder = new EmbeddedFileSystem.Builder();
                fsBuilder.autoDelete(true);
                fs = fsBuilder.build();
            }
            
            // Add the neccessary inhabitants.
            habitat = glassfish.getService(ServiceLocator.class);
            ServiceLocatorUtilities.addOneConstant(habitat, this);
            fileSystem = habitat.getServiceHandle(
                    ServiceLocatorUtilities.addOneConstant(habitat, fs, null, EmbeddedFileSystem.class));
            
            logger.logp(Level.FINER, "Server", "", "Created GlassFish = {0}, " +
                    "GlassFish Status = {1}", new Object[]{glassfish, glassfish.getStatus()});
        } catch (Throwable ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Returns the list of existing embedded instances
     *
     * @return list of the instanciated embedded instances.
     */
    public static List getServerNames() {
        List names = new ArrayList();
        names.addAll(servers.keySet());
        return names;
    }

    /**
     * Returns the embedded server instance of a particular name
     *
     * @param name requested server name
     * @return a server instance if it exists, null otherwise
     */
    public static Server getServer(String name) {
        return servers.get(name);
    }

    // todo : have the same name, and make it clear we use the type string().

    /**
     * Get the embedded container configuration for a container type.
     * @param type the container type (e.g. Type.ejb)
     * @return the embedded configuration for this container
     */
    public ContainerBuilder createConfig(ContainerBuilder.Type type) {
        return createConfig(type.toString());
    }

    /**
     * Get the embedded container builder for a container type identified by its
     * name.
     * @param name the container name, which is the name used on the @Service annotation
     * @return the embedded builder for this container
     */
    @SuppressWarnings("unchecked")
    public ContainerBuilder createConfig(String name) {
        return habitat.getService(ContainerBuilder.class, name);
    }

    /**
     * Get an embedded container configuration. The type of the expected
     * configuration is passed to the method and is not necessarily known to
     * the glassfish embedded API. This type of configuration is used for
     * extensions which are not defined by the core glassfish project.
     *
     * The API stability of the interfaces returned by this method is outside the
     * scope of the glassfish-api stability contract, it's a private contract
     * between the provider of that configuration and the user.
     *
     * @param configType the type of the embedded container configuration
     * @param  type of the embedded container
     * @return the configuration to configure a container of type 
     */
    public > T createConfig(Class configType) {
        return habitat.getService(configType);        
    }

    /**
     * Adds a container of a particular type using the default operating
     * configuration for the container.
     *
     * @param type type of the container to be added (like web, ejb).
     * @throws IllegalStateException if the container is already started.
     */
    public synchronized void addContainer(final ContainerBuilder.Type type) {

        containers.add(new Container(new EmbeddedContainer() {

            final List delegates = new ArrayList();
            final ArrayList sniffers = new ArrayList();

            public List getSniffers() {
                synchronized(sniffers) {
                    if (sniffers.isEmpty()) {
                        if (type == ContainerBuilder.Type.all) {
                            for (final ContainerBuilder.Type t : ContainerBuilder.Type.values()) {
                                if (t!=ContainerBuilder.Type.all) {
                                    delegates.add(getContainerFor(t));
                                }
                            }
                        } else {
                            delegates.add(getContainerFor(type));
                        }
                    }
                    for (Container c : delegates) {
                        sniffers.addAll(c.container.getSniffers());
                    }
                }
                return sniffers;
            }

            public void bind(Port port, String protocol)  {
                for (Container delegate : delegates) {
                    delegate.container.bind(port, protocol);
                }
            }

            private Container getContainerFor(final ContainerBuilder.Type type) {
                ContainerBuilder b = createConfig(type);
                if (b!=null) {
                    return new Container(b.create(Server.this));
                } else {
                    return new Container(new EmbeddedContainer() {

                        public List getSniffers() {
                            List sniffers = new ArrayList();
                            Sniffer s = habitat.getService(Sniffer.class, type.toString());
                            if (s!=null) {
                                sniffers.add(s);
                            }
                            return sniffers;
                        }

                        public void bind(Port port, String protocol) {

                        }

                        public void start() throws LifecycleException {

                        }

                        public void stop() throws LifecycleException {

                        }

                    });
                }
            }

            public void start() throws LifecycleException {
                for (Container c : delegates) {
                    if (!c.started) {
                        c.container.start();
                        c.started=true;
                    }
                }
            }

            public void stop() throws LifecycleException {
                for (Container c : delegates) {
                    if (c.started) {
                        c.container.stop();
                        c.started=false;
                    }
                }

            }

        }));
    }



    // todo : clarify that adding containers after the server is created is illegal
    // todo : makes the return of those APIs return void.

    /**
     * Adds a container to this server.
     *
     * Using the configuration instance for the container of type ,
     * creating the container from that configuration and finally adding the
     * container instance to the list of managed containers
     *
     * @param info the configuration for the container
     * @param  type of the container
     * @return instance of the container 
     * @throws IllegalStateException if the container is already started.
     */
    public synchronized  T addContainer(ContainerBuilder info) {
        T container = info.create(this);
        if (container!=null && containers.add(new Container(container))) {
            return container;
        }
        return null;

    }


    /**
     * Returns a list of the currently managed containers
     *
     * @return the containers list
     */
    public Collection getContainers() {
        ArrayList copy = new ArrayList();
        for (Container c : containers) {
            copy.add(c.container);
        }
        return copy;        
    }

    /**
     * Creates a port to attach to embedded containers. Ports can be attached to many
     * embedded containers and some containers may accept more than one port.
     *
     * @param portNumber port number for this port
     * @return a new port abstraction.
     * @throws IOException if the port cannot be opened.
     */
    public Port createPort(int portNumber) throws IOException {
        Ports ports = habitat.getService(Ports.class);
        return ports.createPort(portNumber);
    }

    /**
     * Returns the server name, as specified in {@link Server.Builder#Builder(String)}
     *
     * @return container name
     */
    public String getName(){
        return serverName;
    }

    /**
     * Returns the embedded file system used to run this embedded instance.
     *
     * @return embedded file system used by this instance
     */
    public EmbeddedFileSystem getFileSystem() {
        return fileSystem.getService();
    }

    /**
     * Starts the embedded server, opening ports, and running the startup
     * services.
     *
     * @throws LifecycleException if the server cannot be started propertly
     */
    public synchronized void start() throws LifecycleException {
        if(glassfish != null) {
            try {
                if (glassfish.getStatus() != GlassFish.Status.STARTED) {
                    glassfish.start();
                }
            } catch (GlassFishException e) {
                throw new LifecycleException(e); // TODO(Sahoo): Proper Exception Handling
            }
            logger.finer("GlassFish has been started");
        }
    }

    /**
     * stops the embedded server instance, any deployed application will be stopped
     * ports will be closed and shutdown services will be ran.
     * EmbeddedFileSystem will be released, meaning that any managed directory will
     * be deleted rendering the EmbeddedFileSystem unusable.
     *
     * @throws LifecycleException if the server cannot shuts down properly
     */
    public synchronized void stop() throws LifecycleException {
        try {
            if (glassfish != null) {
                glassfish.stop();
                logger.finer("GlassFish has been stopped");
            }
            if (glassfishRuntime != null) {
                glassfishRuntime.shutdown();
                logger.finer("GlassFishruntime has been shutdown");
            }
        } catch (Exception ex) {
            logger.log(Level.WARNING, ex.getMessage(), ex);
        } finally {
            synchronized(servers) {
                servers.remove(serverName);
            }
            fileSystem.getService().preDestroy();
        }
    }

    /**
     * Returns the embedded deployer implementation, can be used to
     * generically deploy applications to the embedded server.
     *
     * @return embedded deployer
     */
    public EmbeddedDeployer getDeployer() {
        return habitat.getService(EmbeddedDeployer.class);
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy