org.glassfish.ejb.embedded.EJBContainerProviderImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2016 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.
*/
// Portions Copyright [2019-2021] Payara Foundation and/or affiliates
package org.glassfish.ejb.embedded;
import com.sun.enterprise.deploy.shared.ArchiveFactory;
import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.module.bootstrap.Which;
import com.sun.enterprise.security.EmbeddedSecurity;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.logging.LogDomains;
import org.glassfish.api.container.Sniffer;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.deployment.common.DeploymentUtils;
import org.glassfish.deployment.common.GenericAnnotationDetector;
import org.glassfish.deployment.common.ModuleDescriptor;
import org.glassfish.ejb.deployment.io.EjbDeploymentDescriptorFile;
import org.glassfish.embeddable.BootstrapProperties;
import org.glassfish.embeddable.GlassFish;
import org.glassfish.embeddable.GlassFishProperties;
import org.glassfish.embeddable.GlassFishRuntime;
import org.glassfish.hk2.api.ServiceLocator;
import jakarta.ejb.EJBException;
import jakarta.ejb.embeddable.EJBContainer;
import jakarta.ejb.spi.EJBContainerProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* GlassFish implementation of the EJBContainerProvider.
*
* @author Marina Vatkina
*/
public class EJBContainerProviderImpl implements EJBContainerProvider {
private static final String GF_PROVIDER_NAME = EJBContainerProviderImpl.class.getName();
private static final String GF_EJB_EMBEDDED_PROPERTY_START = "org.glassfish.ejb.embedded.";
protected static final String KEEP_TEMPORARY_FILES = GF_EJB_EMBEDDED_PROPERTY_START + "keep-temporary-files";
private static final String SKIP_CLIENT_MODULES = GF_EJB_EMBEDDED_PROPERTY_START + "skip-client-modules";
private static final String GF_INSTALLATION_ROOT = GF_EJB_EMBEDDED_PROPERTY_START + "glassfish.installation.root";
private static final String GF_INSTANCE_ROOT = GF_EJB_EMBEDDED_PROPERTY_START + "glassfish.instance.root";
private static final String GF_DOMAIN_FILE = GF_EJB_EMBEDDED_PROPERTY_START + "glassfish.configuration.file";
private static final String GF_INSTANCE_REUSE = GF_EJB_EMBEDDED_PROPERTY_START + "glassfish.instance.reuse";
private static final String GF_WEB_HTTP_PORT = GF_EJB_EMBEDDED_PROPERTY_START + "glassfish.web.http.port";
private static final String WEAVING = "org.glassfish.persistence.embedded.weaving.enabled";
private static final Attributes.Name ATTRIBUTE_NAME_SKIP = new Attributes.Name("Bundle-SymbolicName");
private static final String[] KNOWN_PACKAGES =
{"org.glassfish.", "com.sun.enterprise.", "org.eclipse.", "org.jboss.weld."};
private static final String[] ATTRIBUTE_VALUES_OK = {"sample", "test"};
// Use Bundle from another package
private static final Logger _logger =
LogDomains.getLogger(EJBContainerProviderImpl.class, LogDomains.EJB_LOGGER);
private static final StringManager localStrings =
StringManager.getManager(EJBContainerProviderImpl.class);
private static final Object lock = new Object();
private static EJBContainerImpl container;
private static GlassFishRuntime runtime;
private static ArchiveFactory archiveFactory;
private static Class[] ejbAnnotations = null;
public EJBContainerProviderImpl() {}
public EJBContainer createEJBContainer(Map, ?> properties) throws EJBException {
if (properties == null || properties.get(EJBContainer.PROVIDER) == null ||
properties.get(EJBContainer.PROVIDER).equals(GF_PROVIDER_NAME)) {
if (container != null && container.isOpen()) {
throw new EJBException(localStrings.getString(
"ejb.embedded.exception_exists_container"));
}
boolean ok = false;
Locations l = getLocations(properties);
try {
createContainer(properties, l);
Set modules = addModules(properties, l);
if (!DeploymentElement.hasEJBModule(modules)) {
_logger.log(Level.SEVERE, "ejb.embedded.no_modules_found");
} else {
container.deploy(properties, modules);
}
ok = true;
return container;
} catch (EJBException e) {
throw e;
} catch (Throwable t) {
_logger.log(Level.SEVERE, "ejb.embedded.exception_instantiating", t);
throw new EJBException(t.getMessage());
} finally {
if (!ok && container != null) {
try {
_logger.info("[EJBContainerProviderImpl] Cleaning up on failure ...");
container.close();
} catch (Throwable t1) {
_logger.info("[EJBContainerProviderImpl] Error cleaning up..." + t1);
}
container = null;
}
}
}
return null; // not this provider
}
private Locations createContainer(Map, ?> properties, Locations l) throws EJBException {
synchronized(lock) {
// if (container == null || !container.isOpen()) {
try {
if (runtime != null) {
runtime.shutdown(); // dispose of the old one
}
BootstrapProperties bootstrapProperties = new BootstrapProperties();
// Propagate non EJB embeddable container properties into GlassFishProperties
Properties newProps = new Properties();
if (properties != null) {
copyUserProperties(properties, newProps);
}
// Disable weaving if it is not spesified
if (newProps.getProperty(WEAVING) == null) {
newProps.setProperty(WEAVING, "false");
}
GlassFishProperties glassFishProperties = new GlassFishProperties(newProps);
if (Boolean.getBoolean(KEEP_TEMPORARY_FILES)) {
glassFishProperties.setProperty("org.glassfish.embeddable.autoDelete", "false"); // set autodelete to false.
glassFishProperties.setConfigFileReadOnly(false); // make sure the domain.xml is written back.
}
if (l.installed_root != null && l.instance_root != null) {
// Real install
_logger.info("[EJBContainerProviderImpl] Using installation location " + l.installed_root.getCanonicalPath());
bootstrapProperties.setInstallRoot(l.installed_root.getCanonicalPath());
}
if (l.instance_root != null && l.reuse_instance_location) {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("[EJBContainerProviderImpl] Reusing instance location at: " + l.instance_root);
}
_logger.info("[EJBContainerProviderImpl] Using instance location: " + l.instance_root.getCanonicalPath());
glassFishProperties.setInstanceRoot(l.instance_root.getCanonicalPath());
} else if (l.domain_file != null) {
_logger.info("[EJBContainerProviderImpl] Using config file location: " + l.domain_file.toURI().toString());
glassFishProperties.setConfigFileURI(l.domain_file.toURI().toString());
}
addWebContainerIfRequested(properties, glassFishProperties);
runtime = GlassFishRuntime.bootstrap(bootstrapProperties);
_logger.info("[EJBContainerProviderImpl] Using runtime class: " + runtime.getClass());
GlassFish server = runtime.newGlassFish(glassFishProperties);
if (l.instance_root != null && !l.reuse_instance_location) {
// XXX Start the server to get the services
server.start();
EmbeddedSecurity es = server.getService(EmbeddedSecurity.class);
ServiceLocator habitat = server.getService(ServiceLocator.class);
server.stop();
// If we are running from an existing install, copy over security files to the temp instance
if (es != null) {
es.copyConfigFiles(habitat, l.instance_root, l.domain_file);
}
}
// server is started in EJBContainerImpl constructor
container = new EJBContainerImpl(server);
validateInstanceDirectory();
archiveFactory = server.getService(ArchiveFactory.class);
Sniffer sniffer = server.getService(Sniffer.class, "Ejb");
ejbAnnotations = sniffer.getAnnotationTypes();
} catch (Exception e) {
try {
if (container != null) {
container.stop();
}
} catch (Exception e0) {
_logger.log(Level.SEVERE, e0.getMessage(), e0);
}
container = null;
throw new EJBException(e);
}
// }
}
return l;
}
/**
* Adds EJB modules for the property in the properties Map or if such property
* is not specified, from the System classpath. Also adds library references.
*/
private Set addModules(Map, ?> properties, Locations l) {
Set modules = new HashSet();
Object obj = (properties == null)? null : properties.get(EJBContainer.MODULES);
boolean skip_module_with_main_class = getBooleanProperty(properties, SKIP_CLIENT_MODULES);
Map moduleNames = new HashMap();
// Check EJBContainer.MODULES setting first - it can have an explicit set of files
if (obj != null) {
// Check module names first
if (obj instanceof String) {
moduleNames.put((String)obj, false);
} else if (obj instanceof String[]) {
String[] arr = (String[])obj;
for (String s : arr) {
moduleNames.put(s, false);
}
} else if (obj instanceof File) {
addModule(l, modules, moduleNames, (File)obj);
} else if (obj instanceof File[]) {
File[] arr = (File[])obj;
for (File f : arr) {
addModule(l, modules, moduleNames, f);
}
}
}
if (modules.isEmpty()) {
// No file is specified - load from the classpath
String path = System.getProperty("java.class.path");
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("[EJBContainerProviderImpl] Looking for EJB modules in classpath: " + path);
}
String[] entries = path.split(File.pathSeparator);
for (String s0 : entries) {
addModule(l, modules, moduleNames, new File(s0), skip_module_with_main_class);
}
if (!moduleNames.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : moduleNames.entrySet()) {
if (!entry.getValue()) {
sb.append(entry.getKey()).append(", ");
}
}
int ln = sb.length();
if (ln > 0) {
// Errors found. Trim the constructed string
throw new EJBException("Modules: [" + sb.substring(0, ln-2) + "] do not match an entry in the classpath");
}
}
}
return modules;
}
/**
* @returns DeploymentElement if this file represents an EJB module or a library.
* Returns null if it's an EJB module which name is not present in the list of requested
* module names.
*/
private DeploymentElement getRequestedEJBModuleOrLibrary(File file, Map moduleNames)
throws Exception {
DeploymentElement result = null;
String fileName = file.getName();
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("... Testing ... " + fileName);
}
ReadableArchive archive = null;
InputStream is = null;
try {
boolean isEJBModule = false;
String moduleName = DeploymentUtils.getDefaultEEName(fileName);
archive = archiveFactory.openArchive(file);
is = getDeploymentDescriptor(archive);
if (is != null) {
isEJBModule = true;
EjbDeploymentDescriptorFile eddf =
new EjbDeploymentDescriptorFile();
eddf.setXMLValidation(false);
EjbBundleDescriptor bundleDesc = (EjbBundleDescriptor) eddf.read(is);
ModuleDescriptor moduleDesc = bundleDesc.getModuleDescriptor();
moduleDesc.setArchiveUri(fileName);
moduleName = moduleDesc.getModuleName();
} else {
GenericAnnotationDetector detector =
new GenericAnnotationDetector(ejbAnnotations);
isEJBModule = detector.hasAnnotationInArchive(archive);
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("... is EJB module: " + isEJBModule);
if (isEJBModule) {
_logger.fine("... is Requested EJB module [" + moduleName + "]: "
+ (moduleNames.isEmpty() || moduleNames.containsKey(moduleName)));
}
}
if (!isEJBModule || moduleNames.isEmpty()) {
result = new DeploymentElement(file, isEJBModule, moduleName);
} else if (moduleNames.containsKey(moduleName) && !moduleNames.get(moduleName)) {
// Is a requested EJB module and was not found already
result = new DeploymentElement(file, isEJBModule, moduleName);
moduleNames.put(moduleName, true);
}
return result;
} finally {
if (archive != null)
archive.close();
if (is != null)
is.close();
}
}
/**
* Adds an a DeploymentElement to the Set of modules if it represents an EJB module or a library.
*/
private void addModule(Locations l, Set modules, Map moduleNames, File f) {
addModule(l, modules, moduleNames, f, false);
}
/**
* Adds an a DeploymentElement to the Set of modules if it represents an EJB module or a library.
* If skip_module_with_main_class is true, ignore the module that contains Main-Class attribute
* in its manifest file
*/
private void addModule(Locations l, Set modules, Map moduleNames, File f,
boolean skip_module_with_main_class) {
try {
if (f.exists() && !skipJar(f, l, skip_module_with_main_class)) {
DeploymentElement de = getRequestedEJBModuleOrLibrary(f, moduleNames);
if (de != null) {
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("... Added " + ((de.isEJBModule())? "EJB Module" : "library") +
" .... " + de.getElement().getName());
}
modules.add(de);
}
}
} catch (Exception ioe) {
_logger.log(Level.FINE, "ejb.embedded.io_exception", ioe);
// skip it
}
}
/**
* @returns true if this jar is either a GlassFish module jar or one
* of the other known implementation modules.
*/
private boolean skipJar(File file, Locations l, boolean skip_module_with_main_class) throws Exception {
if (file.isDirectory() ) {
if (!skip_module_with_main_class) {
// Nothing to check
return false;
}
File m_file = new File(file, "META-INF/MANIFEST.MF");
if (!m_file.exists()) {
return false;
}
InputStream is = null;
try {
is = new FileInputStream(m_file);
if(containsMainClass(new Manifest(is))) {
// Ignore dirs with a Manifest file with a Main-Class attribute
_logger.info("... skipping entry with a Manifest file with a Main-Class attribute: " + file.getName());
return true;
} else {
return false;
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
_logger.log(Level.FINE, "Exception while closing Manifest file under "
+ file + ": ", ex);
}
}
}
}
// Skip jars in the install modules directory
if (l.modules_dir != null &&
l.modules_dir.equals(file.getAbsoluteFile().getParentFile().getAbsolutePath())) {
_logger.info("... skipping module: " + file.getName());
return true;
}
JarFile jf = null;
try {
jf = new JarFile(file);
Manifest m = jf.getManifest();
if (m != null) {
if (skip_module_with_main_class && containsMainClass(m)) {
// Ignore jars with a Main-Class attribute
_logger.info("... skipping entry with a Manifest file with a Main-Class attribute: " + file.getName());
return true;
}
java.util.jar.Attributes attributes = m.getMainAttributes();
String value = attributes.getValue(ATTRIBUTE_NAME_SKIP);
if (value != null) {
for (String skipValue : KNOWN_PACKAGES) {
if (value.startsWith(skipValue)) {
for (String okValue : ATTRIBUTE_VALUES_OK) {
// value starts with one of the KNOWN_PACKAGES but contains an okValue further down
if (value.indexOf(okValue) > 0) {
// Still OK
return false;
}
}
// Not OK - skip it
_logger.info("... skipping entry with a Manifest file with a special attribute: " + file.getName());
return true;
}
}
}
}
} finally {
if (jf != null) {
try {
jf.close();
} catch (IOException ex) {
_logger.log(Level.FINE, "Exception while closing JarFile "
+ jf.getName() + ": ", ex);
}
}
}
return false;
}
/**
* Create a File object from the location and report an error
* if such file does not exist.
* Returns null if such file does not exist.
*/
private File getValidFile(String location, String msg_key) {
File f = new File(location);
if (!f.exists()) {
_logger.log(Level.WARNING, msg_key, location);
f = null;
}
return f;
}
/**
* Returns true if the Manifest file contains Main-Class attribute
*/
private boolean containsMainClass(Manifest m) {
if (m != null) {
java.util.jar.Attributes attributes = m.getMainAttributes();
String value = attributes.getValue(Attributes.Name.MAIN_CLASS);
return (value != null && value.length() > 0);
}
return false;
}
/**
* Create File objects corresponding to instance root and domain.xml location.
*/
private Locations getLocations(Map, ?> properties) throws EJBException {
String installed_root_location = null;
String instance_root_location = null;
String domain_file_location = null;
File installed_root = null;
File instance_root = null;
File domain_file = null;
boolean reuse_instance_location = false;
if (properties != null) {
// Check if anything is set
installed_root_location = (String) properties.get(GF_INSTALLATION_ROOT);
instance_root_location = (String) properties.get(GF_INSTANCE_ROOT);
domain_file_location = (String) properties.get(GF_DOMAIN_FILE);
reuse_instance_location = getBooleanProperty(properties, GF_INSTANCE_REUSE);
}
if (installed_root_location == null) {
// Try to calculate installation location relative to
// the jar that contains this class
try {
installed_root_location = Which.jarFile(getClass()).
getParentFile().getParentFile().getAbsolutePath();
} catch (Exception e) {
_logger.log(Level.SEVERE, "ejb.embedded.cannot_determine_installation_location");
_logger.log(Level.FINE, e.getMessage(), e);
}
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("[EJBContainerProviderImpl] installed_root_location : " + installed_root_location);
}
if (installed_root_location != null) {
installed_root = getValidFile(installed_root_location, "ejb.embedded.installation_location_not_exists");
if (installed_root != null) {
if (instance_root_location == null) {
// Calculate location for the domain relative to GF install
instance_root_location = installed_root_location
+ File.separatorChar + "domains"
+ File.separatorChar + "domain1";
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("[EJBContainerProviderImpl] instance_root_location: " + instance_root_location);
}
instance_root = getValidFile(instance_root_location, "ejb.embedded.instance_location_not_exists");
}
}
if (instance_root != null && domain_file_location == null) {
// Calculate location for the domain.xml relative to GF instance
domain_file_location = instance_root_location
+ File.separatorChar + "config"
+ File.separatorChar + "domain.xml";
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("[EJBContainerProviderImpl] domain_file_location : " + domain_file_location);
}
if (domain_file_location != null) {
domain_file = getValidFile(domain_file_location, "ejb.embedded.configuration_file_location_not_exists");
if (domain_file != null) {
if (!reuse_instance_location) {
File temp_domain_file = null;
try {
DomainXmlTransformer dxf = new DomainXmlTransformer(domain_file, _logger);
boolean keep_ports = (properties == null)? false : ((properties.get(GF_WEB_HTTP_PORT) == null)? false : true);
temp_domain_file = dxf.transform(keep_ports);
} catch (Exception e) {
throw new EJBException(localStrings.getString(
"ejb.embedded.exception_creating_temporary_domain_xml_file"), e);
}
if (temp_domain_file != null) {
domain_file = temp_domain_file;
} else {
throw new EJBException(localStrings.getString(
"ejb.embedded.failed_create_temporary_domain_xml_file"));
}
}
}
}
return new Locations(installed_root, instance_root, domain_file, reuse_instance_location);
}
private InputStream getDeploymentDescriptor(ReadableArchive archive) throws IOException {
InputStream dd = archive.getEntry("META-INF/ejb-jar.xml");
if (dd == null) {
// Try EJB in a .war file as well
dd = archive.getEntry("WEB-INF/ejb-jar.xml");
}
return dd;
}
private void addWebContainerIfRequested(Map, ?> properties, GlassFishProperties props) throws EJBException {
String http_port = (properties == null)? null : (String)properties.get(GF_WEB_HTTP_PORT);
if (http_port != null) {
int port = 8080;
try {
port = Integer.parseInt(http_port);
} catch (NumberFormatException e) {
System.err.println("Using port 8080");
}
props.setPort("http-listener-1", port);
}
}
/**
* Copy user specified properties into a Proprties object that will be used
* to create GlassFishProperties.
*/
private void copyUserProperties(Map, ?> properties, Properties props) {
for (Map.Entry, ?> entry : properties.entrySet()) {
Object key = entry.getKey();
if (key instanceof String) {
String sk = (String) key;
if (!sk.startsWith(GF_EJB_EMBEDDED_PROPERTY_START)) {
for (String prefix : KNOWN_PACKAGES) {
if (sk.startsWith(prefix)) {
Object value = entry.getValue();
if (value instanceof String) {
props.setProperty(sk, (String)value);
} else {
props.setProperty(sk, value.toString());
}
}
}
}
}
}
}
/**
* Returns boolean value whether the original type is String or Boolean
*/
private boolean getBooleanProperty(Map, ?> properties, String key) {
boolean result = false;
if (properties != null) {
Object value = properties.get(key);
if (value != null) {
if (value instanceof String) {
result = Boolean.valueOf((String)value);
} else {
try {
result = (Boolean) value;
} catch (Exception e) {}
}
}
}
return result;
}
/**
* Verifies that instance directory exists and not empty
*/
private void validateInstanceDirectory() {
// Verify that the instance was created properly
File instance_directory = new File(System.getProperty("com.sun.aas.instanceRoot"));
if (!instance_directory.exists()) {
throw new IllegalStateException("Unexpected ERROR: Instance directory " + instance_directory + " does not exist");
} else if (!instance_directory.isDirectory()) {
throw new IllegalStateException("Unexpected ERROR: Instance directory " + instance_directory + " is not a directory");
}
File[] files = instance_directory.listFiles();
if (files == null || files.length == 0) {
throw new IllegalStateException("Unexpected ERROR: Instance directory " + instance_directory + " is empty");
}
}
private static class Locations {
final File installed_root;
final File instance_root;
final File domain_file;
final String modules_dir;
final boolean reuse_instance_location;
Locations (File installed_root, File instance_root, File domain_file, boolean reuse_instance_location) {
this.installed_root = installed_root;
this.instance_root = instance_root;
this.domain_file = domain_file;
if (installed_root != null) {
modules_dir = (new File(installed_root, "modules")).getAbsolutePath();
} else {
modules_dir = null;
}
this.reuse_instance_location = reuse_instance_location;
}
}
}