Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes classloading of the generated classes for the shared EAR libraries #25301

Merged
merged 3 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023 Eclipse Foundation and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024 Eclipse Foundation and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -239,7 +239,26 @@ private static boolean useMethodHandles(final ClassLoader loader, final Class<?>
if (anchorClass == null || loader.getParent() == null || loader.getClass() == ASURLClassLoader.class) {
return false;
}
// Use MethodHandles.Lookup only if the anchor run-time Package defined by CL.
return Objects.equals(anchorClass.getPackage(), loader.getDefinedPackage(targetPackageName));
// Use MethodHandles.Lookup only if the anchor class has the same package
// and anchor class is visible to the application classloader.
return Objects.equals(anchorClass.getPackageName(), targetPackageName) && isVisible(anchorClass, loader);
}

/**
* Checks that the anchor class {@code anchorClass} is visible to the application
* classloader {@code loader}.
*
* @param anchorClass the anchor class
* @param loader the application classloader
*/
private static boolean isVisible(Class<?> anchorClass, ClassLoader loader) {
ClassLoader anchorLoader = anchorClass.getClassLoader();
while (loader != null) {
if (loader.equals(anchorLoader)) {
return true;
}
loader = loader.getParent();
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class GlassFishTestEnvironment {
private static final File ASADMIN = findAsadmin();
private static final File STARTSERV = findStartServ();
private static final File KEYTOOL = findKeyTool();
private static final File JARSIGNER = findJarSigner();
private static final File PASSWORD_FILE_FOR_UPDATE = findPasswordFile("password_update.txt");
private static final File PASSWORD_FILE = findPasswordFile("password.txt");

Expand Down Expand Up @@ -130,6 +131,10 @@ public static KeyTool getKeyTool() {
}


public static JarSigner getJarSigner() {
return new JarSigner(JARSIGNER);
}

/**
* @return project's target directory.
*/
Expand Down Expand Up @@ -161,6 +166,7 @@ public static KeyStore getDomain1TrustStore() {
public static int getPort(HttpListenerType listenerType) {
return listenerType.getPort();
}

/**
* Creates a {@link Client} instance for the domain administrator.
* Caller is responsible for closing.
Expand Down Expand Up @@ -320,6 +326,9 @@ private static File findKeyTool() {
return new File(System.getProperty("java.home"), isWindows() ? "bin/keytool.exe" : "bin/keytool");
}

private static File findJarSigner() {
return new File(System.getProperty("java.home"), isWindows() ? "bin/jarsigner.exe" : "bin/jarsigner");
}

private static boolean isWindows() {
return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.main.itest.tools;

import com.sun.enterprise.universal.process.ProcessManager;
import com.sun.enterprise.universal.process.ProcessManagerException;
import com.sun.enterprise.universal.process.ProcessManagerTimeoutException;

import java.io.File;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Tool for executing jarsigner/jarsigner.exe commands.
* The tool is stateless.
*/
public class JarSigner {

private static final Logger LOG = System.getLogger(JarSigner.class.getName());

private final File jarsigner;

public JarSigner(File jarsigner) {
this.jarsigner = jarsigner;
}

/**
* Executes the command with arguments.
*
* @param args the arguments
*/
public void exec(String... args) {
final List<String> parameters = Arrays.asList(args);
LOG.log(Level.INFO, "exec(args={0})", parameters);
final List<String> command = new ArrayList<>();
command.add(jarsigner.getAbsolutePath());
command.addAll(parameters);

final ProcessManager processManager = new ProcessManager(command);
processManager.setTimeoutMsec(60_000);
processManager.setEcho(true);

int exitCode;
String errorMessage = "";
try {
exitCode = processManager.execute();
} catch (ProcessManagerTimeoutException e) {
errorMessage = e.getMessage();
exitCode = 1;
} catch (ProcessManagerException e) {
LOG.log(Level.ERROR, "The execution failed.", e);
errorMessage = e.getMessage();
exitCode = 1;
}

final String stdErr = processManager.getStderr() + "\n" + errorMessage;
if (!processManager.getStdout().isEmpty()) {
System.out.println(processManager.getStdout());
}
if (!processManager.getStderr().isEmpty()) {
System.err.println(processManager.getStderr());
}
if (exitCode != 0) {
throw new RuntimeException(stdErr);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.main.test.app.signedear.api;

import jakarta.ejb.Remote;

@Remote
public interface ExampleRemote {

void example();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.main.test.app.signedear.impl;

import jakarta.ejb.Stateless;

import org.glassfish.main.test.app.signedear.api.ExampleRemote;

@Stateless
public class ExampleBean implements ExampleRemote {

@Override
public void example() {
// do nothing
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.main.test.app.signedear;

import java.io.File;
import java.lang.System.Logger.Level;
import java.util.UUID;

import org.glassfish.main.itest.tools.GlassFishTestEnvironment;
import org.glassfish.main.itest.tools.JarSigner;
import org.glassfish.main.itest.tools.KeyTool;
import org.glassfish.main.itest.tools.asadmin.Asadmin;
import org.glassfish.main.test.app.signedear.api.ExampleRemote;
import org.glassfish.main.test.app.signedear.impl.ExampleBean;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.glassfish.main.itest.tools.asadmin.AsadminResultMatcher.asadminOK;
import static org.hamcrest.MatcherAssert.assertThat;

/**
* Integration test for deployment EAR application that contains signed
* shared libraries and modules.
*
* <p>This integration test checks the correctness of the classloading
* of generated classes.
*/
public class SignedEarDeploymentTest {

private static final System.Logger LOG = System.getLogger(SignedEarDeploymentTest.class.getName());

private static final String APP_NAME = "signed";

private static final String KEYSTORE_PASSWORD = UUID.randomUUID().toString();

private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
private static final KeyTool KEYTOOL = GlassFishTestEnvironment.getKeyTool();
private static final JarSigner JARSIGNER = GlassFishTestEnvironment.getJarSigner();

@TempDir
private static File tempDir;
private static File earFile;

@BeforeAll
public static void prepareDeployment() {
File keyStore = new File(tempDir, "signtest.jks");

// Generate a key pair (a public key and associated private key).
KEYTOOL.exec("-genkeypair", "-alias", "signtest", "-keyalg", "RSA", "-dname",
"CN=SIGNTEST, OU=Eclipse Glassfish Tests, O=Eclipse Foundation, L=Brussels, ST=Belgium, C=Belgium",
"-validity", "7", "-keypass", KEYSTORE_PASSWORD, "-keystore", keyStore.getAbsolutePath(),
"-storepass", KEYSTORE_PASSWORD);

// Create shared library.
JavaArchive apiArchive = ShrinkWrap.create(JavaArchive.class)
.addClass(ExampleRemote.class);
File apiFile = new File(tempDir, "api.jar");
apiArchive.as(ZipExporter.class).exportTo(apiFile);
LOG.log(Level.INFO, apiArchive.toString(true));

// Sign shared library.
JARSIGNER.exec("-keystore", keyStore.getAbsolutePath(), "-storepass", KEYSTORE_PASSWORD,
"-keypass", KEYSTORE_PASSWORD, apiFile.getAbsolutePath(), "signtest");

// Create EAR EJB module.
JavaArchive implArchive = ShrinkWrap.create(JavaArchive.class)
.addClass(ExampleBean.class);
File implFile = new File(tempDir, "impl.jar");
implArchive.as(ZipExporter.class).exportTo(implFile);
LOG.log(Level.INFO, implArchive.toString(true));

// Sign EAR EJB module.
JARSIGNER.exec("-keystore", keyStore.getAbsolutePath(), "-storepass", KEYSTORE_PASSWORD,
"-keypass", KEYSTORE_PASSWORD, implFile.getAbsolutePath(), "signtest");

// Create EAR application.
EnterpriseArchive enterpriseArchive = ShrinkWrap.create(EnterpriseArchive.class)
.addAsLibrary(apiFile)
.addAsModule(implFile);
earFile = new File(tempDir, APP_NAME + ".ear");
enterpriseArchive.as(ZipExporter.class).exportTo(earFile);
LOG.log(Level.INFO, enterpriseArchive.toString(true));
}

@AfterAll
public static void cleanup() {
ASADMIN.exec("undeploy", APP_NAME);
}

@Test
public void testDeployment() {
assertThat(ASADMIN.exec("deploy", earFile.getAbsolutePath()), asadminOK());
}
}