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

org.glassfish.appclient.client.acc.UndeployedLaunchable Maven / Gradle / Ivy

/*
 * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
 * Copyright (c) 1997, 2018 Oracle 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
 * 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.appclient.client.acc;

import com.sun.enterprise.deploy.shared.ArchiveFactory;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.ApplicationClientDescriptor;
import com.sun.enterprise.deployment.BundleDescriptor;
import com.sun.enterprise.deployment.archivist.AppClientArchivist;
import com.sun.enterprise.deployment.archivist.Archivist;
import com.sun.enterprise.deployment.archivist.ArchivistFactory;
import com.sun.enterprise.deployment.util.DOLUtils;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;

import java.io.IOException;
import java.net.URI;
import java.net.URLClassLoader;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.glassfish.api.deployment.archive.ArchiveType;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.deployment.common.ModuleDescriptor;
import org.glassfish.deployment.common.RootDeploymentDescriptor;
import org.glassfish.hk2.api.ServiceLocator;
import org.xml.sax.SAXException;


/**
 *
 * @author tjquinn
 */
public class UndeployedLaunchable implements Launchable {

    private static final LocalStringsImpl localStrings = new LocalStringsImpl(UndeployedLaunchable.class);

    private final String callerSuppliedMainClassName;

    private ApplicationClientDescriptor acDesc;

    private AppClientArchivist archivist;

    private final ReadableArchive clientRA;

    private ClassLoader classLoader;

    static UndeployedLaunchable newUndeployedLaunchable(
            final ServiceLocator habitat,
            final ReadableArchive ra,
            final String callerSuppliedMainClassName,
            final String callerSuppliedAppName,
            final ClassLoader classLoader) throws IOException, SAXException, UserError {

        ArchivistFactory af = Util.getArchivistFactory();

        /*
         * Try letting the factory decide what type of archive this is.  That
         * will often allow an app client or an EAR archive to be detected
         * automatically.
         */
        Archivist archivist = af.getArchivist("car", classLoader);
        if (archivist == null) {
            throw new UserError(localStrings.get("appclient.invalidArchive",
                    ra.getURI().toASCIIString()));
        }

        final ArchiveType moduleType = archivist.getModuleType();
        if (moduleType != null && moduleType.equals(DOLUtils.carType())) {
            return new UndeployedLaunchable(habitat, ra,
                    (AppClientArchivist) archivist, callerSuppliedMainClassName);
        } else if (moduleType != null && moduleType.equals(DOLUtils.earType())) {
            /*
             * Locate the app client submodule that matches the main class name
             * or the app client name.
             */

            Application app = (Application) archivist.open(ra);
            for (ModuleDescriptor md : app.getModules()) {
                if ( ! md.getModuleType().equals(DOLUtils.carType())) {
                    continue;
                }

                ApplicationClientDescriptor acd = (ApplicationClientDescriptor) md.getDescriptor();

                final String displayName = acd.getDisplayName();
                final String appName = acd.getModuleID();

                ArchiveFactory archiveFactory = Util.getArchiveFactory();
                ReadableArchive clientRA = archiveFactory.openArchive(ra.getURI().resolve(md.getArchiveUri()));

                /*
                 * Choose this nested app client if the caller-supplied name
                 * matches, or if the caller-supplied main class matches, or
                 * if neither was provided.
                 */
                final boolean useThisClient =
                        (displayName != null && displayName.equals(callerSuppliedAppName))
                     || (appName != null && appName.equals(callerSuppliedAppName))
                     || (callerSuppliedMainClassName != null && clientRA.exists(classToResource(callerSuppliedMainClassName))
                     || (callerSuppliedAppName == null && callerSuppliedMainClassName == null));

                if (useThisClient) {
                    return new UndeployedLaunchable(habitat, clientRA, acd,
                            callerSuppliedMainClassName);
                }
                clientRA.close();
            }

            throw new UserError(localStrings.get("appclient.noMatchingClientInEAR",
                    ra.getURI(), callerSuppliedMainClassName, callerSuppliedAppName));
        } else {
            /*
             * There is a possibility that the user is trying to launch an
             * archive that is more than one type of archive: such as an EJB
             * but also an app client (because the manifest identifies a main
             * class, for example).
             *
             * Earlier the archivist factory might have returned the other type
             * of archivist - such as the EJB archivist.  Now see if the app
             * client archivist will work when selected directly.
             */
            archivist = af.getArchivist(DOLUtils.carType());

            /*
             * Try to open the archive as an app client archive just to see
             * if it works.
             */
            RootDeploymentDescriptor tempACD = archivist.open(ra);
            if (tempACD != null && tempACD instanceof ApplicationClientDescriptor) {
                /*
                 * Start with a fresh archivist - unopened - so we can request
                 * anno processing, etc. before opening it for real.
                 */
                archivist = af.getArchivist(DOLUtils.carType());
                return new UndeployedLaunchable(habitat, ra, (AppClientArchivist) archivist,
                                callerSuppliedMainClassName);
            }
            throw new UserError(
                    localStrings.get("appclient.unexpectedArchive", ra.getURI()));
        }
    }

    @Override
    public URI getURI() {
        return clientRA.getURI();
    }

    @Override
    public String getAnchorDir() {
        return null;
    }

    private static String classToResource(final String className) {
        return className.replace('.', '/') + ".class";
    }

    private UndeployedLaunchable(final ServiceLocator habitat,
            final ReadableArchive clientRA,
            final String callerSuppliedMainClass) throws IOException {
        this.callerSuppliedMainClassName = callerSuppliedMainClass;
        this.clientRA = clientRA;
    }
    private UndeployedLaunchable(final ServiceLocator habitat,
            final ReadableArchive clientRA,
            final ApplicationClientDescriptor acd,
            final String callerSuppliedMainClass) throws IOException {
        this(habitat, clientRA, callerSuppliedMainClass);
        this.acDesc = acd;
    }

    private UndeployedLaunchable(final ServiceLocator habitat,
            final ReadableArchive clientRA,
            final AppClientArchivist archivist,
            final String callerSuppliedMainClass) throws IOException {
        this(habitat, clientRA, callerSuppliedMainClass);
        this.archivist = completeInit(archivist);
    }

    @Override
    public Class getMainClass() throws ClassNotFoundException {
        try {
            String mainClassName = mainClassNameToLaunch();
            return Class.forName(mainClassName, true, getClassLoader());
        } catch (Exception e) {
            throw new ClassNotFoundException("");
        }
    }

    private ClassLoader getClassLoader() {
        if (classLoader == null) {
            classLoader = Thread.currentThread().getContextClassLoader();
        }
        return classLoader;
    }

    private String mainClassNameToLaunch() throws IOException {
        return (callerSuppliedMainClassName != null ? callerSuppliedMainClassName :
            extractMainClassFromArchive(clientRA));
    }

    private String extractMainClassFromArchive(final ReadableArchive clientRA) throws IOException  {
        final Manifest mf = clientRA.getManifest();
        if (mf == null) {
            return null;
        }
        return mf.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
    }

    @Override
    public ApplicationClientDescriptor getDescriptor(final URLClassLoader loader) throws IOException, SAXException {
        this.classLoader = loader;
        if (acDesc == null) {
            final AppClientArchivist _archivist = getArchivist(new ACCClassLoader(loader.getURLs(), loader.getParent()));

            _archivist.setAnnotationProcessingRequested(true);
            acDesc = _archivist.open(clientRA);

            ModuleDescriptor moduleDescriptor = acDesc.getModuleDescriptor();
            Application.createVirtualApplication(null, moduleDescriptor);
            acDesc.getApplication().setAppName(getDefaultApplicationName(clientRA));
        }
        return acDesc;
    }

    public String getDefaultApplicationName(ReadableArchive archive) {
        String appName = archive.getName();
        int lastDot = appName.lastIndexOf('.');
        if (lastDot != -1) {
            if (appName.substring(lastDot).equalsIgnoreCase(".ear") ||
                appName.substring(lastDot).equalsIgnoreCase(".jar")) {
                appName = appName.substring(0, lastDot);
            }
        }
        return appName;
    }


    private AppClientArchivist completeInit(final AppClientArchivist arch) {
        arch.setDescriptor(acDesc);
        arch.setAnnotationProcessingRequested(true);
        return arch;
    }

    private AppClientArchivist getArchivist(final ClassLoader classLoader) throws IOException {
        if (archivist == null) {
            ArchivistFactory af = Util.getArchivistFactory();
            archivist = completeInit(af.getArchivist("car"));
        }
        archivist.setClassLoader(classLoader);
        return archivist;
    }

    @Override
    public void validateDescriptor() {
        try {
            getArchivist(classLoader).validate(classLoader);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy