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

com.sun.enterprise.deployment.util.ResourceValidator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation
 * Copyright (c) 2017, 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 com.sun.enterprise.deployment.util;

import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.config.serverbeans.ServerTags;
import com.sun.enterprise.deployment.AbstractConnectorResourceDescriptor;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.ApplicationClientDescriptor;
import com.sun.enterprise.deployment.BundleDescriptor;
import com.sun.enterprise.deployment.ConnectorDescriptor;
import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.EjbDescriptor;
import com.sun.enterprise.deployment.EjbReferenceDescriptor;
import com.sun.enterprise.deployment.EntityManagerFactoryReferenceDescriptor;
import com.sun.enterprise.deployment.EntityManagerReferenceDescriptor;
import com.sun.enterprise.deployment.EnvironmentProperty;
import com.sun.enterprise.deployment.JndiNameEnvironment;
import com.sun.enterprise.deployment.ManagedBeanDescriptor;
import com.sun.enterprise.deployment.MessageDestinationReferenceDescriptor;
import com.sun.enterprise.deployment.PersistenceUnitDescriptor;
import com.sun.enterprise.deployment.PersistenceUnitsDescriptor;
import com.sun.enterprise.deployment.ResourceDescriptor;
import com.sun.enterprise.deployment.ResourceEnvReferenceDescriptor;
import com.sun.enterprise.deployment.ResourceReferenceDescriptor;
import com.sun.enterprise.deployment.ServiceReferenceDescriptor;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.util.LocalStringManagerImpl;

import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;

import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.Events;
import org.glassfish.api.naming.SimpleJndiName;
import org.glassfish.deployment.common.DeploymentException;
import org.glassfish.deployment.common.DeploymentProperties;
import org.glassfish.deployment.common.JavaEEResourceType;
import org.glassfish.internal.deployment.Deployment;
import org.glassfish.logging.annotation.LogMessageInfo;
import org.glassfish.resourcebase.resources.api.ResourceConstants;
import org.jvnet.hk2.annotations.Service;

import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_APP;
import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_APP_ENV;
import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_COMPONENT;
import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_COMPONENT_ENV;
import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_GLOBAL;
import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_MODULE;

/**
 * Created by Krishna Deepak on 6/9/17.
 */
@Service
public class ResourceValidator implements EventListener, ResourceValidatorVisitor {

    private static final Logger LOG = com.sun.enterprise.deployment.util.DOLUtils.deplLogger;

    @LogMessageInfo(
            message = "JNDI lookup failed for the resource: Name: {0}, Lookup: {1}, Type: {2}.",
            level = "SEVERE",
            cause = "JNDI lookup for the specified resource failed.",
            action = "Configure the required resources before deploying the application.",
            comment = "For the method validateJNDIRefs of com.sun.enterprise.deployment.util.ResourceValidator."
    )
    private static final String RESOURCE_REF_JNDI_LOOKUP_FAILED = "AS-DEPLOYMENT-00026";

    @LogMessageInfo(
            message = "Resource Adapter not present: RA Name: {0}, Type: {1}.",
            level = "SEVERE",
            cause = "Resource apapter specified is invalid.",
            action = "Configure the required resource adapter."
    )
    private static final String RESOURCE_REF_INVALID_RA = "AS-DEPLOYMENT-00027";

    @LogMessageInfo(message = "Skipping resource validation")
    private static final String SKIP_RESOURCE_VALIDATION = "AS-DEPLOYMENT-00028";

    private static final Set DEFAULT_JNDI_NAMES = Set
        .of(JNDI_CTX_JAVA_COMPONENT + "DefaultDataSource",
            JNDI_CTX_JAVA_COMPONENT + "DefaultJMSConnectionFactory",
            JNDI_CTX_JAVA_COMPONENT + "ORB",
            JNDI_CTX_JAVA_COMPONENT + "DefaultManagedExecutorService",
            JNDI_CTX_JAVA_COMPONENT + "DefaultManagedScheduledExecutorService",
            JNDI_CTX_JAVA_COMPONENT + "DefaultManagedThreadFactory",
            JNDI_CTX_JAVA_COMPONENT + "DefaultContextService",
            JNDI_CTX_JAVA_COMPONENT + "UserTransaction",
            JNDI_CTX_JAVA_COMPONENT + "TransactionSynchronizationRegistry",
            JNDI_CTX_JAVA_COMPONENT + "BeanManager",
            JNDI_CTX_JAVA_COMPONENT + "ValidatorFactory",
            JNDI_CTX_JAVA_COMPONENT + "Validator",
            JNDI_CTX_JAVA_COMPONENT + "InAppClientContainer",
            JNDI_CTX_JAVA_MODULE + "ModuleName",
            JNDI_CTX_JAVA_APP + "AppName")
        .stream().map(SimpleJndiName::new).collect(Collectors.toUnmodifiableSet());

    private String target;

    private DeploymentContext dc;

    private Application application;

    @Inject
    private Events events;

    @Inject
    private Domain domain;

    private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(ResourceValidator.class);

    @Inject
    @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
    private Server server;

    @PostConstruct
    public void postConstruct() {
        events.register(this);
    }

    @Override
    public void event(Event event) {
        if (event.is(Deployment.AFTER_APPLICATION_CLASSLOADER_CREATION)) {
            dc = (DeploymentContext) event.hook();
            application = dc.getModuleMetaData(Application.class);
            DeployCommandParameters commandParams = dc.getCommandParameters(DeployCommandParameters.class);
            target = commandParams.target;
            if (System.getProperty("deployment.resource.validation", "true").equals("false")) {
                LOG.log(Level.INFO, SKIP_RESOURCE_VALIDATION);
                return;
            }
            if (application == null) {
                return;
            }
            AppResources appResources = new AppResources();
            parseResources(appResources);
            validateResources(appResources);
        }
    }

    /**
     * Store all the resources before starting the validation.
     */
    private void parseResources(AppResources appResources) {
        parseResources(application, appResources);
        for (BundleDescriptor bd : application.getBundleDescriptors()) {
            if (bd instanceof WebBundleDescriptor || bd instanceof ApplicationClientDescriptor) {
                parseResources(bd, appResources);
            }
            if (bd instanceof EjbBundleDescriptor) {
                // Resources from Java files in the ejb.jar which are neither an EJB nor a managed bean are stored here.
                // Skip validation for them, validate only Managed Beans.
                for (ManagedBeanDescriptor mbd: bd.getManagedBeans()) {
                    parseResources(mbd, (JndiNameEnvironment) bd, appResources);
                }
                EjbBundleDescriptor ebd = (EjbBundleDescriptor) bd;
                for (EjbDescriptor ejb : ebd.getEjbs()) {
                    parseEJB(ejb, appResources);
                }
            }
        }

        parseManagedBeans(appResources);

        // Parse AppScoped resources
        String appName = DOLUtils.getApplicationName(application);
        @SuppressWarnings("unchecked")
        Map> resourcesList = (Map>) dc
            .getTransientAppMetadata().get(ResourceConstants.APP_SCOPED_RESOURCES_JNDI_NAMES);
        appResources.storeAppScopedResources(resourcesList, appName);
    }

    /**
     * Code logic from BaseContainer.java. Store portable and non-portable JNDI names in our namespace.
     * Internal JNDI names not processed as they will not be called from an application.
     *
     * @param ejb
     */
    private void parseEJB(EjbDescriptor ejb, AppResources appResources) {
        SimpleJndiName javaGlobalName = getJavaGlobalJndiNamePrefix(ejb);

        boolean disableNonPortableJndiName = false;
        // TODO: Need to get the value of system-property
        // server.ejb-container.property.disable-nonportable-jndi-names
        Boolean disableInDD = ejb.getEjbBundleDescriptor().getDisableNonportableJndiNames();
        if (disableInDD != null) {
            // explicitly set in glassfish-ejb-jar.xml
            disableNonPortableJndiName = disableInDD;
        }

        SimpleJndiName glassfishSpecificJndiName;
        if (disableNonPortableJndiName) {
            glassfishSpecificJndiName = null;
        } else {
            glassfishSpecificJndiName = ejb.getJndiName();
        }
        if (glassfishSpecificJndiName != null
            && (glassfishSpecificJndiName.isEmpty() || glassfishSpecificJndiName.equals(javaGlobalName))) {
            glassfishSpecificJndiName = null;
        }

        // used to decide whether the javaGlobalName needs to be stored
        int countPortableJndiNames = 0;

        // interfaces now
        if (ejb.isRemoteInterfacesSupported()) {
            String intf = ejb.getHomeClassName();
            SimpleJndiName fullyQualifiedJavaGlobalName = new SimpleJndiName(javaGlobalName + "!" + intf);
            appResources.storeInNamespace(fullyQualifiedJavaGlobalName, ejb);
            countPortableJndiNames++;
            // non-portable
            if(glassfishSpecificJndiName != null) {
                appResources.storeInNamespace(glassfishSpecificJndiName, ejb);
            }
        }

        if (ejb.isRemoteBusinessInterfacesSupported()) {
            int count = 0;
            for (String intf : ejb.getRemoteBusinessClassNames()) {
                count++;
                SimpleJndiName fullyQualifiedJavaGlobalName = new SimpleJndiName(javaGlobalName + "!" + intf);
                appResources.storeInNamespace(fullyQualifiedJavaGlobalName, ejb);
                countPortableJndiNames++;
                // non-portable - interface specific
                if(glassfishSpecificJndiName != null) {
                    SimpleJndiName remoteJndiName = getRemoteEjbJndiName(true, intf, glassfishSpecificJndiName);
                    appResources.storeInNamespace(remoteJndiName, ejb);
                }
            }
            // non-portable - if only one remote business interface exists and no remote home interfaces exist,
            // then by default this can be used to lookup the remote interface.
            if(glassfishSpecificJndiName != null && !ejb.isRemoteInterfacesSupported() && count == 1) {
                appResources.storeInNamespace(glassfishSpecificJndiName, ejb);
            }
        }

        if (ejb.isLocalInterfacesSupported()) {
            String intf = ejb.getLocalHomeClassName();
            SimpleJndiName fullyQualifiedJavaGlobalName = new SimpleJndiName(javaGlobalName + "!" + intf);
            appResources.storeInNamespace(fullyQualifiedJavaGlobalName, ejb);
            countPortableJndiNames++;
        }

        if (ejb.isLocalBusinessInterfacesSupported()) {
            for (String intf : ejb.getLocalBusinessClassNames()) {
                SimpleJndiName fullyQualifiedJavaGlobalName = new SimpleJndiName(javaGlobalName + "!" + intf);
                appResources.storeInNamespace(fullyQualifiedJavaGlobalName, ejb);
                countPortableJndiNames++;
            }
        }

        if (ejb.isLocalBean()) {
            String intf = ejb.getEjbClassName();
            SimpleJndiName fullyQualifiedJavaGlobalName = new SimpleJndiName(javaGlobalName + "!" + intf);
            appResources.storeInNamespace(fullyQualifiedJavaGlobalName, ejb);
            countPortableJndiNames++;
        }

        if (countPortableJndiNames == 1) {
            appResources.storeInNamespace(javaGlobalName, ejb);
        }
        parseResources(ejb, appResources);
    }

    private SimpleJndiName getJavaGlobalJndiNamePrefix(EjbDescriptor ejbDescriptor) {
        final Application app = ejbDescriptor.getApplication();
        final String appName = app.isVirtual() ? null : app.getAppName();
        StringBuilder javaGlobalPrefix = new StringBuilder(JNDI_CTX_JAVA_GLOBAL);
        if (appName != null) {
            javaGlobalPrefix.append(appName);
            javaGlobalPrefix.append('/');
        }
        javaGlobalPrefix.append(ejbDescriptor.getEjbBundleDescriptor().getModuleDescriptor().getModuleName());
        javaGlobalPrefix.append('/');
        javaGlobalPrefix.append(ejbDescriptor.getName());

        return new SimpleJndiName(javaGlobalPrefix.toString());
    }


    private SimpleJndiName getRemoteEjbJndiName(EjbReferenceDescriptor refDesc) {
        String intf = refDesc.isEJB30ClientView() ? refDesc.getEjbInterface() : refDesc.getHomeClassName();
        return getRemoteEjbJndiName(refDesc.isEJB30ClientView(), intf, refDesc.getJndiName());
    }


    private SimpleJndiName getRemoteEjbJndiName(boolean businessView, String interfaceName, SimpleJndiName jndiName) {
        String portableFullyQualifiedPortion = "!" + interfaceName;
        String glassfishFullyQualifiedPortion = "#" + interfaceName;

        if (businessView) {
            if (!jndiName.hasCorbaPrefix()) {
                if (jndiName.isJavaGlobal()) {
                    return checkFullyQualifiedJndiName(jndiName, portableFullyQualifiedPortion);
                }
                return checkFullyQualifiedJndiName(jndiName, glassfishFullyQualifiedPortion);
            }
        } else {
            // Only in the portable global case, convert to a fully-qualified name
            if (jndiName.isJavaGlobal()) {
                return checkFullyQualifiedJndiName(jndiName, portableFullyQualifiedPortion);
            }
        }

        return jndiName;
    }


    private static SimpleJndiName checkFullyQualifiedJndiName(SimpleJndiName origJndiName, String fullyQualifiedPortion) {
        if (origJndiName.hasSuffix(fullyQualifiedPortion)) {
            return origJndiName;
        }
        return new SimpleJndiName(origJndiName + fullyQualifiedPortion);
    }


    private void parseManagedBeans(AppResources appResources) {
        for (BundleDescriptor bd : application.getBundleDescriptors()) {
            for (ManagedBeanDescriptor managedBean : bd.getManagedBeans()) {
                appResources.storeInNamespace(managedBean.getGlobalJndiName(), (JndiNameEnvironment) bd);
            }
        }
    }


    private void parseResources(BundleDescriptor bd, AppResources appResources) {
        if (!(bd instanceof JndiNameEnvironment)) {
            return;
        }
        JndiNameEnvironment env = (JndiNameEnvironment) bd;
        for (ResourceReferenceDescriptor next : env.getResourceReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (ResourceEnvReferenceDescriptor next : env.getResourceEnvReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (MessageDestinationReferenceDescriptor next : env.getMessageDestinationReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (EnvironmentProperty next : env.getEnvironmentProperties()) {
            parseResources(next, env, appResources);
        }

        for (ResourceDescriptor next : env.getAllResourcesDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (EntityManagerReferenceDescriptor next : env.getEntityManagerReferenceDescriptors()) {
            storeInNamespace(SimpleJndiName.of(next.getName()), env, appResources);
        }

        for (EntityManagerFactoryReferenceDescriptor next : env.getEntityManagerFactoryReferenceDescriptors()) {
            storeInNamespace(SimpleJndiName.of(next.getName()), env, appResources);
        }

        for (EjbReferenceDescriptor next : env.getEjbReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (ServiceReferenceDescriptor next : env.getServiceReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (PersistenceUnitsDescriptor pus : bd.getExtensionsDescriptors(PersistenceUnitsDescriptor.class)) {
            for (PersistenceUnitDescriptor pu : pus.getPersistenceUnitDescriptors()) {
                parseResources(pu, env, appResources);
            }
        }

        for (ManagedBeanDescriptor mbd : bd.getManagedBeans()) {
            parseResources(mbd, env, appResources);
        }
    }


    /**
     * Store resources in ResourceRefDescriptor.
     */
    private void parseResources(ResourceReferenceDescriptor resRef, JndiNameEnvironment env, AppResources appResources) {
        resRef.checkType();
        SimpleJndiName name = getLogicalJNDIName(SimpleJndiName.of(resRef.getName()), env);
        String type = resRef.getType();
        SimpleJndiName jndiName = resRef.getJndiName();
        AppResource resRefResource = new AppResource(name, jndiName, type, env, true);

        if (resRef.isURLResource()) {
            if (jndiName != null && !jndiName.hasJavaPrefix()) {
                try {
                    // for jndi-name like "http://localhost:8080/index.html"
                    new URL(jndiName.toString());
                    resRefResource.noValidation();
                } catch (MalformedURLException e) {
                    // If jndi-name is not an actual url, we might want to lookup the name
                }
            }
        }
        if (resRef.isWebServiceContext()) {
            resRefResource.noValidation();
        }

        appResources.store(resRefResource);
    }

    /**
     * Store resources in ResourceEnvRefDescriptor.
     */
    private void parseResources(ResourceEnvReferenceDescriptor resEnvRef, JndiNameEnvironment env, AppResources appResources) {
        resEnvRef.checkType();
        SimpleJndiName name = getLogicalJNDIName(SimpleJndiName.of(resEnvRef.getName()), env);
        String type = resEnvRef.getType();
        SimpleJndiName jndiName = resEnvRef.getJndiName();
        AppResource resEnvRefResource = new AppResource(name, jndiName, type, env, true);

        if (resEnvRef.isEJBContext() || resEnvRef.isValidator() || resEnvRef.isValidatorFactory() || resEnvRef.isCDIBeanManager()) {
            resEnvRefResource.noValidation();
        }

        appResources.store(resEnvRefResource);
    }

    /**
     * If the message destination ref is linked to a message destination, fetch the linked destination and validate it.
     * We might be duplicating our validation efforts since we are already validating message destination separately.
     */
    private void parseResources(MessageDestinationReferenceDescriptor msgDestRef, JndiNameEnvironment env, AppResources appResources) {
        SimpleJndiName name = getLogicalJNDIName(SimpleJndiName.of(msgDestRef.getName()), env);
        SimpleJndiName jndiName;
        if (msgDestRef.isLinkedToMessageDestination()) {
            jndiName = msgDestRef.getMessageDestination().getJndiName();
        } else {
            jndiName = msgDestRef.getJndiName();
        }
        appResources.store(new AppResource(name, jndiName, msgDestRef.getType(), env, true));
    }

    /**
     * Store references to environment entries.
     * Also validate custom resources of primitive data types.
     */
    private void parseResources(EnvironmentProperty envProp, JndiNameEnvironment env, AppResources appResources) {
        SimpleJndiName name = getLogicalJNDIName(SimpleJndiName.of(envProp.getName()), env);
        SimpleJndiName jndiName;
        if (envProp.hasLookupName()) {
            jndiName = envProp.getLookupName();
        } else if (!envProp.getMappedName().isEmpty()) {
            jndiName = envProp.getMappedName();
        } else {
            jndiName = new SimpleJndiName("");
        }

        AppResource envPropResource = new AppResource(name, jndiName, envProp.getType(), env, true);
        // If lookup/mapped name is not present, then we do not need to validate.
        if (jndiName.isEmpty()) {
            envPropResource.noValidation();
        }

        appResources.store(envPropResource);

        // Store EnvProps even if they do not have a valid lookup element
        appResources.storeInNamespace(name, env);
    }


    /**
     * Logic from EjbNamingReferenceManagerImpl.java - Here EJB references get resolved
     */
    private void parseResources(EjbReferenceDescriptor ejbRef, JndiNameEnvironment env, AppResources appResources) {
        SimpleJndiName name = getLogicalJNDIName(SimpleJndiName.of(ejbRef.getName()), env);
        // we only need to worry about those references which are not linked yet
        if (ejbRef.getEjbDescriptor() != null) {
            appResources.storeInNamespace(name, env);
            return;
        }

        SimpleJndiName jndiName = new SimpleJndiName("");
        // Should we use an inverse approach i.e., skip validation only in special cases?
        // Not sure if that is required as the below approach works fine while resolving EJB references
        boolean validationRequired = false;

        // local
        if (ejbRef.isLocal()) {
            // mapped name has no meaning for local ejb-ref as non-portable JNDI names don't have any meaning in this case?
            if (ejbRef.hasLookupName()) {
                jndiName = ejbRef.getLookupName();
                validationRequired = true;
            }
        } else {
            // remote
            // mapped-name takes precedence over lookup name
            SimpleJndiName ejbRefJndiName = getJndiName(ejbRef);
            if (ejbRefJndiName == null && ejbRef.hasLookupName()) {
                jndiName = ejbRef.getLookupName();
                validationRequired = true;
            } else if (ejbRefJndiName != null && ejbRefJndiName.isJavaApp()
                && !ejbRefJndiName.hasPrefix(JNDI_CTX_JAVA_APP_ENV)) {
                // TODO: A case skipped from EjbNamingRefManager
                // Why does the below logic exist in the EjbNamingRefMan code?
                // Intentionally or not, this resolves the java:app mapped names
                // Seems suspicious as the corresponding java:global case is handled in the getRemoteEjbJndiName function call
                String appName = DOLUtils.getApplicationName(application);
                jndiName = ejbRefJndiName.changePrefix(JNDI_CTX_JAVA_GLOBAL + appName + '/');
                validationRequired = true;
            } else {
                SimpleJndiName remoteJndiName = getRemoteEjbJndiName(ejbRef);
                // TODO: CORBA case
                if (!remoteJndiName.hasCorbaPrefix()) {
                    validationRequired = true;
                    jndiName = remoteJndiName;
                }
            }
        }

        appResources.store(new AppResource(name, jndiName, ejbRef.getType(), env, validationRequired));
    }


    private SimpleJndiName getJndiName(EjbReferenceDescriptor descriptor) {
        if (!descriptor.hasJndiName()) {
            return null;
        }
        return descriptor.getJndiName();
    }


    private void parseResources(ServiceReferenceDescriptor serviceRef, JndiNameEnvironment env,
        AppResources appResources) {
        SimpleJndiName name = getLogicalJNDIName(SimpleJndiName.of(serviceRef.getName()), env);
        if (serviceRef.hasLookupName()) {
            SimpleJndiName lookupName = serviceRef.getLookupName();
            appResources.store(new AppResource(name, lookupName, serviceRef.getType(), env, true));
        } else {
            appResources.storeInNamespace(name, env);
        }
    }


    /**
     * Store the resource definitions in our namespace.
     * CFD and AODD are not valid in an AppClient. O/w need to validate the ra-name in them.
     */
    private void parseResources(ResourceDescriptor resourceDescriptor, JndiNameEnvironment env, AppResources appResources) {
        JavaEEResourceType type = resourceDescriptor.getResourceType();
        if (type == JavaEEResourceType.CFD || type == JavaEEResourceType.AODD) {
            if (env instanceof ApplicationClientDescriptor) {
                return;
            }
            // No need to type check as CFD and AODD extend from AbstractConnectorResourceDescriptor
            AbstractConnectorResourceDescriptor acrd = (AbstractConnectorResourceDescriptor) resourceDescriptor;
            SimpleJndiName raJndiName = SimpleJndiName.of(acrd.getResourceAdapter());
            appResources
                .store(new AppResource(resourceDescriptor.getJndiName(), raJndiName, type.toString(), env, true));
        } else {
            // nothing to validate here. store the definitions in our namespace.
            storeInNamespace(resourceDescriptor.getJndiName(), env, appResources);
        }
    }


    /**
     * Record the Data Source specified in PUD.
     */
    private void parseResources(PersistenceUnitDescriptor pu, JndiNameEnvironment env, AppResources appResources) {
        SimpleJndiName jtaDataSourceName = pu.getJtaDataSource();
        SimpleJndiName nonJtaDataSourceName = pu.getNonJtaDataSource();
        if (jtaDataSourceName != null && !jtaDataSourceName.isEmpty()) {
            appResources.store(new AppResource(pu.getJndiName(), jtaDataSourceName, "javax.sql.DataSource", env, true));
        }
        if (nonJtaDataSourceName != null && !nonJtaDataSourceName.isEmpty()) {
            appResources.store(new AppResource(pu.getJndiName(), nonJtaDataSourceName, "javax.sql.DataSource", env, true));
        }
    }


    private void parseResources(ManagedBeanDescriptor managedBean, JndiNameEnvironment env, AppResources appResources) {
        for (ResourceReferenceDescriptor next : managedBean.getResourceReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (ResourceEnvReferenceDescriptor next : managedBean.getResourceEnvReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (MessageDestinationReferenceDescriptor next : managedBean.getMessageDestinationReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (EjbReferenceDescriptor next : managedBean.getEjbReferenceDescriptors()) {
            parseResources(next, env, appResources);
        }

        for (EnvironmentProperty next : managedBean.getEnvironmentProperties()) {
            parseResources(next, env, appResources);
        }

        for (ResourceDescriptor next : env.getAllResourcesDescriptors()) {
            parseResources(next, env, appResources);
        }
    }


    private void parseResources(EjbDescriptor ejb, AppResources appResources) {
        for (ResourceReferenceDescriptor next : ejb.getResourceReferenceDescriptors()) {
            parseResources(next, ejb, appResources);
        }

        for (ResourceEnvReferenceDescriptor next : ejb.getResourceEnvReferenceDescriptors()) {
            parseResources(next, ejb, appResources);
        }

        for (MessageDestinationReferenceDescriptor next : ejb.getMessageDestinationReferenceDescriptors()) {
            parseResources(next, ejb, appResources);
        }

        for (EnvironmentProperty next : ejb.getEnvironmentProperties()) {
            parseResources(next, ejb, appResources);
        }

        for (EjbReferenceDescriptor next : ejb.getEjbReferenceDescriptors()) {
            parseResources(next, ejb, appResources);
        }

        for (ResourceDescriptor next : ejb.getAllResourcesDescriptors()) {
            parseResources(next, ejb, appResources);
        }
    }


    private void storeInNamespace(SimpleJndiName name, JndiNameEnvironment env, AppResources appResources) {
        SimpleJndiName logicalJNDIName = getLogicalJNDIName(name, env);
        appResources.storeInNamespace(logicalJNDIName, env);
    }


    /**
     * @param rawName to be converted
     * @return The logical JNDI name which has a java: prefix
     */
    private SimpleJndiName getLogicalJNDIName(SimpleJndiName rawName, JndiNameEnvironment env) {
        SimpleJndiName jndiName = rawName.hasJavaPrefix() ? rawName : rawName.changePrefix(JNDI_CTX_JAVA_COMPONENT_ENV);
        boolean treatComponentAsModule = DOLUtils.getTreatComponentAsModule(env);
        if (treatComponentAsModule && jndiName.isJavaComponent()) {
            return jndiName.changePrefix(JNDI_CTX_JAVA_MODULE);
        }
        return jndiName;
    }


    /**
     * Convert JNDI names beginning with java:module and java:app to their corresponding java:global
     * names.
     *
     * @return the converted name with java:global JNDI prefix.
     */
    private SimpleJndiName convertModuleOrAppJNDIName(SimpleJndiName jndiName, JndiNameEnvironment env) {

        if (jndiName == null) {
            return null;
        }

        final BundleDescriptor bd;
        if (env instanceof EjbDescriptor) {
            bd = ((EjbDescriptor) env).getEjbBundleDescriptor();
        } else if (env instanceof BundleDescriptor) {
            bd = (BundleDescriptor) env;
        } else {
            bd = null;
        }

        if (bd == null) {
            return new SimpleJndiName("");
        }
        final String appName = application.isVirtual() ? null : application.getAppName();
        final String moduleName = bd.getModuleDescriptor().getModuleName();
        final StringBuilder javaGlobalName = new StringBuilder(JNDI_CTX_JAVA_GLOBAL);
        if (jndiName.isJavaApp()) {
            if (appName != null) {
                javaGlobalName.append(appName);
                javaGlobalName.append('/');
            }
            javaGlobalName.append(jndiName.removePrefix(JNDI_CTX_JAVA_APP));
        } else if (jndiName.isJavaModule()) {
            if (appName != null) {
                javaGlobalName.append(appName);
                javaGlobalName.append('/');
            }

            javaGlobalName.append(moduleName);
            javaGlobalName.append('/');
            javaGlobalName.append(jndiName.removePrefix(JNDI_CTX_JAVA_MODULE));
        } else {
            return new SimpleJndiName("");
        }
        return new SimpleJndiName(javaGlobalName.toString());
    }


    /**
     * Start of validation logic.
     */
    private void validateResources(AppResources appResources) {
        for (AppResource resource : appResources.myResources) {
            if (!resource.validate) {
                continue;
            }
            if (JavaEEResourceType.CFD.name().equals(resource.getType())
                || JavaEEResourceType.AODD.name().equals(resource.getType())) {
                validateRAName(resource);
            } else {
                validateJNDIRefs(resource, appResources.myNamespace);
            }
        }
        // Validate the ra-names of app scoped resources
        // RA-name and the type of this resource are stored
        List> raNames = (List>) dc.getTransientAppMetadata()
            .get(ResourceConstants.APP_SCOPED_RESOURCES_RA_NAMES);
        if (raNames == null) {
            return;
        }
        for (Map.Entry entry : raNames) {
            validateRAName(entry.getKey(), entry.getValue());
        }
    }


    /**
     * Validate the resource adapter names of @CFD, @AODD.
     */
    private void validateRAName(AppResource resource) {
        String raName = resource.getJndiName() == null ? null : resource.getJndiName().toString();
        validateRAName(raName, resource.getType());
    }


    /**
     * Strategy to validate the resource adapter name:
     * 1) In case of stand-alone RA, look in the domain.xml and for default system RA's
     * 2) In case of embedded RA, compare it with names of RAR descriptors
     * In case of null ra name, we fail the deployment.
     */
    private void validateRAName(String raName, String type) {
        // No ra-name specified
        if (raName == null || raName.isEmpty()) {
            LOG.log(Level.SEVERE, RESOURCE_REF_INVALID_RA, new Object[] {null, type});
            throw new DeploymentException(localStrings.getLocalString("enterprise.deployment.util.ra.validation",
                "Resource Adapter not present: RA Name: {0}, Type: {1}.", null, type));
        }
        int poundIndex = raName.indexOf("#");

        // Pound not present: check for app named raname in domain.xml, check for system ra's
        if (poundIndex < 0) {
            if (domain.getApplications().getApplication(raName) != null) {
                return;
            }
            // System RA's - Copied from ConnectorConstants.java
            if (raName.equals("jmsra") || raName.equals("__ds_jdbc_ra") || raName.equals("jaxr-ra") ||
                    raName.equals("__cp_jdbc_ra") || raName.equals("__xa_jdbc_ra") || raName.equals("__dm_jdbc_ra")) {
                return;
            }
            if (isEmbedded(raName)) {
                return;
            }
        } else if (raName.substring(0, poundIndex).equals(application.getAppName())) {
            // Embedded RA
            // In case the app name does not match, we fail the deployment
            raName = raName.substring(poundIndex + 1);
            if (isEmbedded(raName)) {
                return;
            }
        }
        LOG.log(Level.SEVERE, RESOURCE_REF_INVALID_RA, new Object[] {raName, type});
        throw new DeploymentException(localStrings.getLocalString(
                "enterprise.deployment.util.ra.validation",
                "Resource Adapter not present: RA Name: {0}, Type: {1}.",
                raName, type));
    }


    private boolean isEmbedded(String raName) {
        String ranameWithRAR = raName + ".rar";
        // check for rar named this
        for (BundleDescriptor bd : application.getBundleDescriptors(ConnectorDescriptor.class)) {
            if (raName.equals(bd.getModuleName()) || ranameWithRAR.equals(bd.getModuleName())) {
                return true;
            }
        }
        return false;
    }


    /**
     * Strategy for validating a given jndi name
     * 1) Check in domain.xml
     * 2) Check in the resources defined within the app. These have not been binded to the namespace yet.
     * 3) Check for resources defined by an earlier application.
     * In case a null jndi name is passed, we fail the deployment.
     *
     * @param resource to be validated.
     */
    private void validateJNDIRefs(AppResource resource, JNDINamespace namespace) {
        // In case lookup is not present, check if another resource with the same name exists
        if (!resource.hasLookup()) {
            if (namespace.find(resource.getName(), resource.getEnv())) {
                return;
            }
            LOG.log(Level.SEVERE, RESOURCE_REF_JNDI_LOOKUP_FAILED,
                new Object[] {resource.getName(), null, resource.getType()});
            throw new DeploymentException(
                MessageFormat.format("JNDI lookup failed for the resource: Name: {0}, Lookup: {1}, Type: {2}",
                    resource.getName(), null, resource.getType()));
        }

        // hasLookup returned true, so it cannot be null.
        SimpleJndiName jndiName = resource.getJndiName();
        JndiNameEnvironment env = resource.getEnv();

        if (isResourceInDomainXML(jndiName) || DEFAULT_JNDI_NAMES.contains(jndiName)) {
            return;
        }

        // Managed Bean & EJB portable JNDI names
        if (jndiName.isJavaModule() || jndiName.isJavaApp()) {
            SimpleJndiName newName = convertModuleOrAppJNDIName(jndiName, resource.getEnv());
            if (namespace.find(newName, env)) {
                return;
            }
        }

        // EJB Non-portable JNDI names
        if (!jndiName.hasJavaPrefix()) {
            if (namespace.find(jndiName, env)) {
                return;
            }
        }

        // convert comp to module if req
        SimpleJndiName convertedJndiName = getLogicalJNDIName(jndiName, env);
        if (namespace.find(convertedJndiName, env)) {
            return;
        }

        try {
            if (loadOnCurrentInstance()) {
                InitialContext.doLookup(jndiName.toString());
            }
        } catch (NamingException e) {
            throw new DeploymentException(
                MessageFormat.format("JNDI lookup failed for the resource: Name: {0}, Lookup: {1}, Type: {2}",
                    resource.getName(), jndiName, resource.getType()),
                e);
        }
    }


    /**
     * Validate the given resource in the corresponding target using domain.xml server beans.
     * For resources defined outside the application.
     *
     * @param jndiName to be validated
     * @return True if resource is present in domain.xml in the corresponding target. False otherwise.
     */
    private boolean isResourceInDomainXML(SimpleJndiName jndiName) {
        if (jndiName == null) {
            return false;
        }

        Server svr = domain.getServerNamed(target);
        if (svr != null) {
            return svr.isResourceRefExists(jndiName);
        }

        Cluster cluster = domain.getClusterNamed(target);
        return cluster != null && cluster.isResourceRefExists(jndiName);
    }

    private static class AppResource {
        private final SimpleJndiName name;

        private final SimpleJndiName lookup;

        private final String type;

        private final JndiNameEnvironment env;

        boolean validate;

        private AppResource(SimpleJndiName name, SimpleJndiName lookup, String type, JndiNameEnvironment env, boolean validate) {
            this.name = name;
            this.lookup = lookup;
            this.type = type;
            this.env = env;
            this.validate = validate;
        }

        private SimpleJndiName getJndiName() {
            return lookup;
        }

        private JndiNameEnvironment getEnv() {
            return env;
        }

        private SimpleJndiName getName() {
            return name;
        }

        private String getType() {
            return type;
        }

        private boolean hasLookup() {
            return lookup != null && !lookup.isEmpty();
        }

        private void noValidation() {
            validate = false;
        }
    }

    private static class AppResources {
        List myResources;
        JNDINamespace myNamespace;

        private AppResources() {
            myResources = new ArrayList<>();
            myNamespace = new JNDINamespace();
        }

        /**
         * Store in namespace only if it has a valid lookup value. This is because we do not want to store invalid
         * resources in our namespace.
         */
        private void store(AppResource resource) {
            myResources.add(resource);
            if (resource.hasLookup()) {
                myNamespace.store(resource.name, resource.env);
            }
        }

        /**
         * If we know that the name points to a valid resource, directly store in namespace.
         */
        private void storeInNamespace(SimpleJndiName name, JndiNameEnvironment env) {
            myNamespace.store(name, env);
        }

        private void storeAppScopedResources(Map> resourcesList, String appName) {
            myNamespace.storeAppScopedResources(resourcesList, appName);
        }
    }

    /**
     * A class to record all the logical JNDI names of resources defined in the application in the appropriate scopes.
     * App scoped resources, Resource Definitions are also stored in this data structure.
     */
    private static class JNDINamespace {
        private final Map> componentNamespaces;
        private final Map> moduleNamespaces;
        private final List appNamespace;
        private final List globalNameSpace;
        private final List nonPortableJndiNames;

        private JNDINamespace() {
            componentNamespaces = new HashMap<>();
            moduleNamespaces = new HashMap<>();
            appNamespace = new ArrayList<>();
            globalNameSpace = new ArrayList<>();
            nonPortableJndiNames = new ArrayList<>();
        }

        /**
         * Store app scoped resources in this namespace to facilitate lookup during validation.
         *
         * @param resources - App scoped resources
         * @param appName - Application name
         */
        private void storeAppScopedResources(Map> resources, String appName) {
            if (resources == null) {
                return;
            }
            List appLevelResources = resources.get(appName);
            if (appLevelResources != null) {
                appNamespace.addAll(appLevelResources);
            }
            for (Map.Entry> resource: resources.entrySet()) {
                if (!resource.getKey().equals(appName)) {
                    String moduleName = getActualModuleName(resource.getKey());
                    List jndiNames = moduleNamespaces.get(moduleName);
                    if (jndiNames == null) {
                        jndiNames = new ArrayList<>();
                        jndiNames.addAll(resource.getValue());
                        moduleNamespaces.put(moduleName, jndiNames);
                    } else {
                        jndiNames.addAll(resource.getValue());
                    }
                }
            }
        }


        /**
         * Store the jndi name in the correct scope. Will be stored only if jndi name is javaURL.
         */
        public void store(SimpleJndiName jndiName, JndiNameEnvironment env) {
            LOG.log(Level.FINEST, "store(jndiName={0}, env)", jndiName);
            if (jndiName.isJavaComponent()) {
                String componentId = DOLUtils.getComponentEnvId(env);
                List jndiNames = componentNamespaces.get(componentId);
                if (jndiNames == null) {
                    jndiNames = new ArrayList<>();
                    jndiNames.add(jndiName);
                    componentNamespaces.put(componentId, jndiNames);
                } else {
                    jndiNames.add(jndiName);
                }
            } else if (jndiName.isJavaModule()) {
                String moduleName = getActualModuleName(DOLUtils.getModuleName(env));
                List jndiNames = moduleNamespaces.get(moduleName);
                if (jndiNames == null) {
                    jndiNames = new ArrayList<>();
                    jndiNames.add(jndiName);
                    moduleNamespaces.put(moduleName, jndiNames);
                } else {
                    jndiNames.add(jndiName);
                }
            } else if (jndiName.isJavaApp()) {
                appNamespace.add(jndiName);
            } else if (jndiName.isJavaGlobal()) {
                globalNameSpace.add(jndiName);
            } else {
                nonPortableJndiNames.add(jndiName);
            }
        }


        /**
         * Find the jndi name in our namespace.
         *
         * @return True if the jndi name is found in the namespace. False otherwise.
         */
        public boolean find(SimpleJndiName jndiName, JndiNameEnvironment env) {
            LOG.log(Level.FINE, "find(jndiName={0}, env)", jndiName);
            if (jndiName == null) {
                return false;
            }
            if (jndiName.isJavaComponent()) {
                String componentId = DOLUtils.getComponentEnvId(env);
                List jndiNames = componentNamespaces.get(componentId);
                return jndiNames != null && jndiNames.contains(jndiName);
            } else if (jndiName.isJavaModule()) {
                String moduleName = getActualModuleName(DOLUtils.getModuleName(env));
                List jndiNames = moduleNamespaces.get(moduleName);
                return jndiNames != null && jndiNames.contains(jndiName);
            } else if (jndiName.isJavaApp()) {
                return appNamespace.contains(jndiName);
            } else if (jndiName.isJavaGlobal()) {
                return globalNameSpace.contains(jndiName);
            } else {
                return nonPortableJndiNames.contains(jndiName);
            }
        }


        /**
         * Remove suffix from the module name.
         */
        private String getActualModuleName(String moduleName) {
            if (moduleName != null) {
                if (moduleName.endsWith(".jar") || moduleName.endsWith(".war") || moduleName.endsWith(".rar")) {
                    moduleName = moduleName.substring(0, moduleName.length() - 4);
                }
            }
            return moduleName;
        }
    }

    /**
     * Copy from ApplicationLifeCycle.java
     */
    private boolean loadOnCurrentInstance() {
        final DeployCommandParameters commandParams = dc.getCommandParameters(DeployCommandParameters.class);
        final Properties appProps = dc.getAppProps();
        if (commandParams.enabled) {
            // if the current instance match with the target
            if (domain.isCurrentInstanceMatchingTarget(commandParams.target, commandParams.name(), server.getName(),
                    dc.getTransientAppMetaData(DeploymentProperties.PREVIOUS_TARGETS, List.class))) {
                return true;
            }
            if (server.isDas()) {
                String objectType = appProps.getProperty(ServerTags.OBJECT_TYPE);
                if (objectType != null) {
                    // if it's a system application needs to be loaded on DAS
                    if (objectType.equals(DeploymentProperties.SYSTEM_ADMIN)
                        || objectType.equals(DeploymentProperties.SYSTEM_ALL)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy