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

org.glassfish.embed.GlassFish Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2008 Sun Microsystems, Inc. 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.html
 * or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [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.embed;

import com.sun.enterprise.deploy.shared.ArchiveFactory;
import com.sun.enterprise.module.Module;
import com.sun.enterprise.module.bootstrap.BootException;
import com.sun.enterprise.module.bootstrap.Main;
import com.sun.enterprise.module.bootstrap.StartupContext;
import com.sun.enterprise.module.impl.ModulesRegistryImpl;
import com.sun.enterprise.security.SecuritySniffer;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.v3.admin.adapter.AdminConsoleAdapter;
import com.sun.enterprise.v3.data.ApplicationInfo;
import com.sun.enterprise.v3.deployment.DeployCommand;
import com.sun.enterprise.v3.deployment.DeploymentContextImpl;
import com.sun.enterprise.v3.server.ApplicationLifecycle;
import com.sun.enterprise.v3.server.DomainXml;
import com.sun.enterprise.v3.server.DomainXmlPersistence;
import com.sun.enterprise.v3.server.ServerEnvironment;
import com.sun.enterprise.v3.server.SnifferManager;
import com.sun.enterprise.v3.services.impl.LogManagerService;
import com.sun.enterprise.web.WebDeployer;
import com.sun.hk2.component.InhabitantsParser;
import com.sun.web.security.RealmAdapter;
import com.sun.web.server.DecoratorForJ2EEInstanceListener;
import org.glassfish.api.Startup;
import org.glassfish.api.container.Sniffer;
import org.glassfish.api.deployment.archive.ArchiveHandler;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.deployment.autodeploy.AutoDeployService;
import org.glassfish.embed.impl.DomainXml2;
import org.glassfish.embed.impl.EntityResolverImpl;
import org.glassfish.embed.impl.ProxyModuleDefinition;
import org.glassfish.embed.impl.ServerEnvironment2;
import org.glassfish.embed.impl.SilentActionReport;
import org.glassfish.embed.impl.WebDeployer2;
import org.glassfish.embed.impl.DomainXmlHolder;
import org.glassfish.internal.api.Init;
import org.glassfish.web.WebEntityResolver;
import org.jvnet.hk2.component.Habitat;
import org.jvnet.hk2.component.Inhabitant;
import org.jvnet.hk2.component.Inhabitants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Entry point to the embedded GlassFish.
 *
 * 

* TODO: the way this is done today is that the embedded API wraps the ugliness * of the underlying GFv3 internal abstractions, but ideally, it should be the * other way around — this should be the native interface inside GlassFish, * and application server launcher and CLI commands should be the client of this * API. This is how all the other sensible containers do it, like Tomcat and Jetty. * * @author Kohsuke Kawaguchi */ public class GlassFish { /** * As of April 2008, several key configurations like HTTP listener * creation cannot be done once GFv3 starts running. *

* We hide this from the client of this API by laziyl starting * the server, and this flag remembers which state we are in. */ private boolean started; protected /*almost final*/ Habitat habitat; /** * Work around until the live HTTP listener support comes back. */ private final Document domainXml; /** * To navigate around {@link #domainXml}. */ private final XPath xpath = XPathFactory.newInstance().newXPath(); // key components inside GlassFish. We access them all the time, // so we might just as well keep them here for ease of access. protected /*almost final*/ ApplicationLifecycle appLife; protected /*almost final*/ SnifferManager snifMan; protected /*almost final*/ ArchiveFactory archiveFactory; protected /*almost final*/ ServerEnvironment env; /** * Starts an empty do-nothing GlassFish v3. * *

* In particular, no HTTP listener is configured out of the box, so you'd have to add * some programatically via {@link #createHttpListener(int)} and {@link #createVirtualServer(GFHttpListener)}. */ public GlassFish() throws GFException { try { domainXml = parseDefaultDomainXml(); } catch (IOException e) { throw new GFException(e); } catch (SAXException e) { throw new GFException(e); } catch (ParserConfigurationException e) { throw new GFException(e); } } /** * Starts GlassFish v3 with minimalistic configuration that involves * single HTTP listener listening on the given port. */ public GlassFish(int httpPort) throws GFException { this(); createVirtualServer(createHttpListener(httpPort)); start(); } private Document parseDefaultDomainXml() throws ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // dbf.setNamespaceAware(true); // domain.xml doesn't use namespace return dbf.newDocumentBuilder().parse(getClass().getResource("/org/glassfish/embed/domain.xml").toExternalForm()); } /** * Sets the overall logging level for GlassFish. */ public static void setLogLevel(Level level) { Logger.getLogger("javax.enterprise").setLevel(level); } /** * Tweaks the 'recipe' --- for embedded use, we'd like GFv3 to behave a little bit * differently from normal stand-alone use. */ protected InhabitantsParser decorateInhabitantsParser(InhabitantsParser parser) { // we don't want GFv3 to reconfigure all the loggers parser.drop(LogManagerService.class); // we don't need admin CLI support. // TODO: admin CLI should be really moved to a separate class parser.drop(AdminConsoleAdapter.class); // don't care about auto-deploy either parser.drop(AutoDeployService.class); // we don't really parse domain.xml from disk parser.replace(DomainXml.class, DomainXml2.class); parser.habitat.add(Inhabitants.create(new DomainXmlHolder(domainXml))); // ... and we don't persist it either. parser.replace(DomainXmlPersistence.class, DomainXml2.class); // we provide our own ServerEnvironment parser.replace(ServerEnvironment.class, ServerEnvironment2.class); {// adjustment for webtier only bundle parser.drop(DecoratorForJ2EEInstanceListener.class); // in the webtier-only bundle, these components don't exist to begin with. try { // security code needs a whole lot more work to work in the modular environment. // disabling it for now. parser.drop(SecuritySniffer.class); // WebContainer has a bug in how it looks up Realm, but this should work around that. parser.drop(RealmAdapter.class); } catch (LinkageError e) { // maybe we are running in the webtier only bundle } } // override the location of default-web.xml parser.replace(WebDeployer.class, WebDeployer2.class); // override the location of cached DTDs and schemas parser.replace(WebEntityResolver.class, EntityResolverImpl.class); return parser; } protected File createTempDir() throws IOException { File dir = File.createTempFile("glassfish","embedded"); dir.delete(); dir.mkdirs(); return dir; } public GFVirtualServer createVirtualServer(final GFHttpListener listener) { // the following live update code doesn't work yet due to the missing functionality in the webtier. if(started) throw new IllegalStateException(); DomBuilder db = onHttpService(); db.element("virtual-server") .attribute("id","server") .attribute("http-listeners",listener.getId()) .attribute("hosts","${com.sun.aas.hostName}") // ??? .attribute("log-file","") .element("property") .attribute("name","docroot") .attribute("value","."); return new GFVirtualServer(null); // try { // Configs configs = habitat.getComponent(Configs.class); // // HttpService httpService = configs.getConfig().get(0).getHttpService(); // return (GFVirtualServer) ConfigSupport.apply(new SingleConfigCode() { // public Object run(HttpService param) throws PropertyVetoException, TransactionFailure { // VirtualServer vs = ConfigSupport.createChildOf(param, VirtualServer.class); // vs.setId("server"); // vs.setHttpListeners(listener.core.getId()); // vs.setHosts("${com.sun.aas.hostName}"); //// vs.setDefaultWebModule("no-such-module"); // // Property property = // ConfigSupport.createChildOf(vs, Property.class); // property.setName("docroot"); // property.setValue("."); // vs.getProperty().add(property); // // // param.getVirtualServer().add(vs); // return new GFVirtualServer(vs); // } // }, httpService); // } catch(TransactionFailure e) { // throw new GFException(e); // } } public GFHttpListener createHttpListener(final int listenerPort) { // the following live update code doesn't work yet due to the missing functionality in the webtier. if(started) throw new IllegalStateException(); onHttpService().element("http-listener") .attribute("id",listenerPort) .attribute("address","0.0.0.0") .attribute("port",listenerPort) .attribute("default-virtual-server","server") .attribute("server-name","") .attribute("enabled",true); return new GFHttpListener(String.valueOf(listenerPort),null); // try { // Configs configs = habitat.getComponent(Configs.class); // // HttpService httpService = configs.getConfig().get(0).getHttpService(); // return (GFHttpListener)ConfigSupport.apply(new SingleConfigCode() { // public Object run(HttpService param) throws PropertyVetoException, TransactionFailure { // HttpListener newListener = ConfigSupport.createChildOf(param, HttpListener.class); // newListener.setId("http-listener-"+listenerPort); // newListener.setAddress("127.0.0.1"); // newListener.setPort(String.valueOf(listenerPort)); // newListener.setDefaultVirtualServer("server"); // newListener.setEnabled("true"); // // param.getHttpListener().add(newListener); // return new GFHttpListener(newListener); // } // }, httpService); // } catch(TransactionFailure e) { // throw new GFException(e); // } } private DomBuilder onHttpService() { try { return new DomBuilder((Element) xpath.evaluate("//http-service",domainXml, XPathConstants.NODE)); } catch (XPathExpressionException e) { throw new AssertionError(e); // impossible } } /** * Starts the server if hasn't done so already. Necessary to work around the live HTTP listener update. */ private void start() { if(started) return; started = true; try { final Module[] proxyMod = new Module[1]; ModulesRegistryImpl mrs = new ModulesRegistryImpl(null) { public Module find(Class clazz) { Module m = super.find(clazz); if(m==null) return proxyMod[0]; return m; } }; proxyMod[0] = mrs.add(new ProxyModuleDefinition(getClass().getClassLoader())); StartupContext startupContext = new StartupContext(createTempDir(), new String[0]); habitat = new Main() { @Override protected InhabitantsParser createInhabitantsParser(Habitat habitat1) { return decorateInhabitantsParser(super.createInhabitantsParser(habitat1)); } }.launch(mrs,startupContext); appLife = habitat.getComponent(ApplicationLifecycle.class); snifMan = habitat.getComponent(SnifferManager.class); archiveFactory = habitat.getComponent(ArchiveFactory.class); env = habitat.getComponent(ServerEnvironment.class); } catch (IOException e) { throw new GFException(e); } catch (BootException e) { throw new GFException(e); } } /** * Deploys WAR/EAR/RAR/etc to this GlassFish. * * @return * always non-null. Represents the deployed application. */ public GFApplication deploy(File archive) throws IOException { start(); ReadableArchive a = archiveFactory.openArchive(archive); if(!archive.isDirectory()) { // explode (if I don't, WarHandler won't work) ArchiveHandler h = appLife.getArchiveHandler(a); File tmpDir = new File(a.getName()); FileUtils.whack(tmpDir); tmpDir.mkdirs(); h.expand(a, archiveFactory.createArchive(tmpDir)); a.close(); a = archiveFactory.openArchive(tmpDir); } return deploy(a); } public GFApplication deploy(ReadableArchive a) throws IOException { start(); ArchiveHandler h = appLife.getArchiveHandler(a); // now prepare sniffers ClassLoader parentCL = snifMan.createSnifferParentCL(null); ClassLoader cl = h.getClassLoader(parentCL, a); Collection activeSniffers = snifMan.getSniffers(a, cl); // TODO: we need to stop this totally type-unsafe way of passing parameters Properties params = new Properties(); params.put(DeployCommand.NAME,a.getName()); params.put(DeployCommand.ENABLED,"true"); final DeploymentContextImpl deploymentContext = new DeploymentContextImpl(Logger.getAnonymousLogger(), a, params, env); deploymentContext.setClassLoader(cl); SilentActionReport r = new SilentActionReport(); ApplicationInfo appInfo = appLife.deploy(activeSniffers, deploymentContext, r); r.check(); return new GFApplication(this,appInfo,deploymentContext); } /** * Stops the running server. */ public void stop() { if(!started) return; for (Inhabitant svc : habitat.getInhabitants(Startup.class)) { svc.release(); } for (Inhabitant svc : habitat.getInhabitants(Init.class)) { svc.release(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy