/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.ee.structure;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.jboss.as.ee.logging.EeLogger;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.MountedDeploymentOverlay;
import org.jboss.as.server.deployment.SubDeploymentMarker;
import org.jboss.as.server.deployment.SubExplodedDeploymentMarker;
import org.jboss.as.server.deployment.module.ModuleRootMarker;
import org.jboss.as.server.deployment.module.MountHandle;
import org.jboss.as.server.deployment.module.ResourceRoot;
import org.jboss.as.server.deployment.module.TempFileProviderService;
import org.jboss.metadata.ear.spec.EarMetaData;
import org.jboss.metadata.ear.spec.ModuleMetaData;
import org.jboss.metadata.ear.spec.ModuleMetaData.ModuleType;
import org.jboss.vfs.VFS;
import org.jboss.vfs.VFSUtils;
import org.jboss.vfs.VirtualFile;
import org.jboss.vfs.VisitorAttributes;
import org.jboss.vfs.util.SuffixMatchFilter;

/**
 * Deployment processor responsible for detecting EAR deployments and putting setting up the basic structure.
 *
 * @author John Bailey
 * @author Stuart Douglas
 */
public class EarStructureProcessor implements DeploymentUnitProcessor {

    private static final String JAR_EXTENSION = ".jar";
    private static final String WAR_EXTENSION = ".war";
    private static final String SAR_EXTENSION = ".sar";
    private static final String RAR_EXTENSION = ".rar";
    private static final List<String> CHILD_ARCHIVE_EXTENSIONS = new ArrayList<String>();

    static {
        CHILD_ARCHIVE_EXTENSIONS.add(JAR_EXTENSION);
        CHILD_ARCHIVE_EXTENSIONS.add(WAR_EXTENSION);
        CHILD_ARCHIVE_EXTENSIONS.add(SAR_EXTENSION);
        CHILD_ARCHIVE_EXTENSIONS.add(RAR_EXTENSION);
    }

    private static final SuffixMatchFilter CHILD_ARCHIVE_FILTER = new SuffixMatchFilter(CHILD_ARCHIVE_EXTENSIONS, new VisitorAttributes() {

        public boolean isLeavesOnly() {
            return false;
        }
    });

    private static final String DEFAULT_LIB_DIR = "lib";

    public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        if (!DeploymentTypeMarker.isType(DeploymentType.EAR, deploymentUnit)) {
            return;
        }

        final ResourceRoot deploymentRoot = phaseContext.getDeploymentUnit().getAttachment(Attachments.DEPLOYMENT_ROOT);
        final VirtualFile virtualFile = deploymentRoot.getRoot();

        //  Make sure we don't index or add this as a module root
        deploymentRoot.putAttachment(Attachments.INDEX_RESOURCE_ROOT, false);
        ModuleRootMarker.mark(deploymentRoot, false);

        String libDirName = DEFAULT_LIB_DIR;
        //its possible that the ear metadata could come for jboss-app.xml
        final boolean appXmlPresent = deploymentRoot.getRoot().getChild("META-INF/application.xml").exists();
        final EarMetaData earMetaData = deploymentUnit.getAttachment(org.jboss.as.ee.structure.Attachments.EAR_METADATA);
        if (earMetaData != null) {
            final String xmlLibDirName = earMetaData.getLibraryDirectory();
            if (xmlLibDirName != null) {
                if (xmlLibDirName.length() == 1 && xmlLibDirName.charAt(0) == '/') {
                    throw EeLogger.ROOT_LOGGER.rootAsLibraryDirectory();
                }
                libDirName = xmlLibDirName;
            }
        }

        // Process all the children
        Map<String, MountedDeploymentOverlay> overlays = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_OVERLAY_LOCATIONS);
        try {
            final VirtualFile libDir;
            // process the lib directory
            if (!libDirName.isEmpty()) {
                libDir = virtualFile.getChild(libDirName);
                if (libDir.exists()) {
                    List<VirtualFile> libArchives = libDir.getChildren(CHILD_ARCHIVE_FILTER);
                    for (final VirtualFile child : libArchives) {
                        String relativeName = child.getPathNameRelativeTo(deploymentRoot.getRoot());
                        MountedDeploymentOverlay overlay = overlays.get(relativeName);
                        final MountHandle mountHandle;
                        if(overlay != null) {
                            overlay.remountAsZip(false);
                            mountHandle = new MountHandle(null);
                        } else {
                            final Closeable closable = child.isFile() ? mount(child, false) : null;
                            mountHandle = new MountHandle(closable);
                        }
                        final ResourceRoot childResource = new ResourceRoot(child, mountHandle);
                        if (child.getName().toLowerCase(Locale.ENGLISH).endsWith(JAR_EXTENSION)) {
                            ModuleRootMarker.mark(childResource);
                            deploymentUnit.addToAttachmentList(Attachments.RESOURCE_ROOTS, childResource);
                        }
                    }
                }
            } else {
                libDir = null;
            }
            // scan the ear looking for wars and jars
            final List<VirtualFile> childArchives = new ArrayList<VirtualFile>(virtualFile.getChildren(new SuffixMatchFilter(
                    CHILD_ARCHIVE_EXTENSIONS, new VisitorAttributes() {
                @Override
                public boolean isLeavesOnly() {
                    return false;
                }

                @Override
                public boolean isRecurse(VirtualFile file) {
                    // don't recurse into /lib
                    if (file.equals(libDir)) {
                        return false;
                    }
                    for (String suffix : CHILD_ARCHIVE_EXTENSIONS) {
                        if (file.getName().endsWith(suffix)) {
                            // don't recurse into sub deployments
                            return false;
                        }
                    }
                    return true;
                }
            })));

            // if there is no application.xml then look in the ear root for modules
            if (!appXmlPresent) {
                for (final VirtualFile child : childArchives) {
                    final boolean isWarFile = child.getName().toLowerCase(Locale.ENGLISH).endsWith(WAR_EXTENSION);
                    final boolean isRarFile = child.getName().toLowerCase(Locale.ENGLISH).endsWith(RAR_EXTENSION);
                    this.createResourceRoot(deploymentUnit, child, isWarFile || isRarFile, isWarFile);
                }
            } else {
                final Set<VirtualFile> subDeploymentFiles = new HashSet<VirtualFile>();
                // otherwise read from application.xml
                for (final ModuleMetaData module : earMetaData.getModules()) {

                    if(module.getFileName().endsWith(".xml")) {
                        throw EeLogger.ROOT_LOGGER.unsupportedModuleType(module.getFileName());
                    }

                    final VirtualFile moduleFile = virtualFile.getChild(module.getFileName());
                    if (!moduleFile.exists()) {
                        throw EeLogger.ROOT_LOGGER.cannotProcessEarModule(virtualFile, module.getFileName());
                    }

                    if (libDir != null) {
                        VirtualFile moduleParentFile = moduleFile.getParent();
                        if (moduleParentFile != null) {
                            if (libDir.equals(moduleParentFile)) {
                                throw EeLogger.ROOT_LOGGER.earModuleChildOfLibraryDirectory(libDirName, module.getFileName());
                            }
                        }
                    }

                    // maintain this in a collection of subdeployment virtual files, to be used later
                    subDeploymentFiles.add(moduleFile);

                    final boolean webArchive = module.getType() == ModuleType.Web;
                    final ResourceRoot childResource = this.createResourceRoot(deploymentUnit, moduleFile, true, webArchive);
                    childResource.putAttachment(org.jboss.as.ee.structure.Attachments.MODULE_META_DATA, module);

                    if (!webArchive) {
                        ModuleRootMarker.mark(childResource);
                    }

                    final String alternativeDD = module.getAlternativeDD();
                    if (alternativeDD != null && alternativeDD.trim().length() > 0) {
                        final VirtualFile alternateDeploymentDescriptor = deploymentRoot.getRoot().getChild(alternativeDD);
                        if (!alternateDeploymentDescriptor.exists()) {
                            throw EeLogger.ROOT_LOGGER.alternateDeploymentDescriptor(alternateDeploymentDescriptor, moduleFile);
                        }
                        switch (module.getType()) {
                            case Client:
                                childResource.putAttachment(org.jboss.as.ee.structure.Attachments.ALTERNATE_CLIENT_DEPLOYMENT_DESCRIPTOR, alternateDeploymentDescriptor);
                                break;
                            case Connector:
                                childResource.putAttachment(org.jboss.as.ee.structure.Attachments.ALTERNATE_CONNECTOR_DEPLOYMENT_DESCRIPTOR, alternateDeploymentDescriptor);
                                break;
                            case Ejb:
                                childResource.putAttachment(org.jboss.as.ee.structure.Attachments.ALTERNATE_EJB_DEPLOYMENT_DESCRIPTOR, alternateDeploymentDescriptor);
                                break;
                            case Web:
                                childResource.putAttachment(org.jboss.as.ee.structure.Attachments.ALTERNATE_WEB_DEPLOYMENT_DESCRIPTOR, alternateDeploymentDescriptor);
                                break;
                            case Service:
                                throw EeLogger.ROOT_LOGGER.unsupportedModuleType(module.getFileName());

                        }
                    }
                }
                // now check the rest of the archive for any other jar/sar files
                for (final VirtualFile child : childArchives) {
                    if (subDeploymentFiles.contains(child)) {
                        continue;
                    }
                    final String fileName = child.getName().toLowerCase(Locale.ENGLISH);
                    if (fileName.endsWith(SAR_EXTENSION) || fileName.endsWith(JAR_EXTENSION)) {
                        this.createResourceRoot(deploymentUnit, child, false, false);
                    }
                }
            }

        } catch (IOException e) {
            throw EeLogger.ROOT_LOGGER.failedToProcessChild(e, virtualFile);
        }
    }

    private static Closeable mount(VirtualFile moduleFile, boolean explode) throws IOException {
        return explode ? VFS.mountZipExpanded(moduleFile, moduleFile, TempFileProviderService.provider())
                : VFS.mountZip(moduleFile, moduleFile, TempFileProviderService.provider());
    }

    /**
     * Creates a {@link ResourceRoot} for the passed {@link VirtualFile file} and adds it to the list of {@link ResourceRoot}s
     * in the {@link DeploymentUnit deploymentUnit}
     *
     * @param deploymentUnit      The deployment unit
     * @param file                The file for which the resource root will be created
     * @param markAsSubDeployment If this is true, then the {@link ResourceRoot} that is created will be marked as a subdeployment
     *                            through a call to {@link SubDeploymentMarker#mark(org.jboss.as.server.deployment.module.ResourceRoot)}
     * @param explodeDuringMount  If this is true then the {@link VirtualFile file} will be exploded during mount,
     *                            while creating the {@link ResourceRoot}
     * @return Returns the created {@link ResourceRoot}
     * @throws IOException
     */
    private ResourceRoot createResourceRoot(final DeploymentUnit deploymentUnit, final VirtualFile file, final boolean markAsSubDeployment, final boolean explodeDuringMount) throws IOException {
        final boolean war = file.getName().toLowerCase(Locale.ENGLISH).endsWith(WAR_EXTENSION);
        final Closeable closable = file.isFile() ? mount(file, explodeDuringMount) : exportExplodedWar(war, file, deploymentUnit);
        final MountHandle mountHandle = new MountHandle(closable);
        final ResourceRoot resourceRoot = new ResourceRoot(file, mountHandle);
        deploymentUnit.addToAttachmentList(Attachments.RESOURCE_ROOTS, resourceRoot);
        if (markAsSubDeployment) {
            SubDeploymentMarker.mark(resourceRoot);
        }
        if (war) {
            resourceRoot.putAttachment(Attachments.INDEX_RESOURCE_ROOT, false);
            SubExplodedDeploymentMarker.mark(resourceRoot);
        }
        return resourceRoot;
    }

    private Closeable exportExplodedWar(final boolean war, final VirtualFile file, final DeploymentUnit deploymentUnit) throws IOException {
        if (isExplodedWarInArchiveEar(war, file, deploymentUnit)) {
            File warContent = file.getPhysicalFile();
            VFSUtils.recursiveCopy(file, warContent.getParentFile());
            return VFS.mountReal(warContent, file);
        }
        return null;
    }

    private boolean isExplodedWarInArchiveEar(final boolean war, final VirtualFile file, final DeploymentUnit deploymentUnit) {
        return war && !file.isFile() && deploymentUnit.hasAttachment(Attachments.DEPLOYMENT_CONTENTS) && deploymentUnit.getAttachment(Attachments.DEPLOYMENT_CONTENTS).isFile();
    }

    public void undeploy(DeploymentUnit context) {
        final List<ResourceRoot> children = context.removeAttachment(Attachments.RESOURCE_ROOTS);
        if (children != null) {
            for (ResourceRoot childRoot : children) {
                VFSUtils.safeClose(childRoot.getMountHandle());
            }
        }
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.wildfly.extension.messaging.activemq;

import static org.wildfly.extension.messaging.activemq.CommonAttributes.FILTER;

import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.operations.validation.StringLengthValidator;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;

/**
 * Helper class to define operations.
 *
 * @author Jeff Mesnil (c) 2014 Red Hat inc.
 */
public class OperationDefinitionHelper {
    public static AttributeDefinition createNonEmptyStringAttribute(String attributeName) {
        return SimpleAttributeDefinitionBuilder.create(attributeName, ModelType.STRING)
                .setRequired(true)
                .setValidator(new StringLengthValidator(1))
                .build();
    }

    /**
     * Create a {@code SimpleOperationDefinitionBuilder} for the given operationName and flag it as
     * read only and runtime only.
     */
    public static SimpleOperationDefinitionBuilder runtimeReadOnlyOperation(String operationName, ResourceDescriptionResolver resolver) {
        return new SimpleOperationDefinitionBuilder(operationName, resolver)
                .setRuntimeOnly()
                .setReadOnly();
    }

    /**
     * Create a {@code SimpleOperationDefinitionBuilder} for the given operationName and flag it as
     * runtime only.
    */
    public static SimpleOperationDefinitionBuilder runtimeOnlyOperation(String operationName, ResourceDescriptionResolver resolver) {
        return new SimpleOperationDefinitionBuilder(operationName, resolver)
                .setRuntimeOnly();
    }

    public static String resolveFilter(OperationContext context, ModelNode operation) throws OperationFailedException {
        ModelNode filter = FILTER.resolveModelAttribute(context, operation);
        return filter.isDefined() ? filter.asString() : null;
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.ejb3.security;

import static java.security.AccessController.doPrivileged;

import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

import javax.security.jacc.EJBMethodPermission;

import org.jboss.as.ee.component.Component;
import org.jboss.as.ee.component.ComponentView;
import org.jboss.as.ejb3.component.EJBComponent;
import org.jboss.as.ejb3.component.MethodIntf;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.invocation.Interceptor;
import org.jboss.invocation.InterceptorContext;
import org.jboss.metadata.ejb.spec.MethodInterfaceType;
import org.wildfly.common.Assert;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * @author Pedro Igor
 */
public class JaccInterceptor implements Interceptor {

    private static final Principal[] NO_PRINCIPALS = new Principal[0];

    private final String viewClassName;
    private final Method viewMethod;

    public JaccInterceptor(String viewClassName, Method viewMethod) {
        this.viewClassName = viewClassName;
        this.viewMethod = viewMethod;
    }

    @Override
    public Object processInvocation(InterceptorContext context) throws Exception {
        Component component = context.getPrivateData(Component.class);
        final SecurityDomain securityDomain = context.getPrivateData(SecurityDomain.class);
        Assert.checkNotNullParam("securityDomain", securityDomain);
        final SecurityIdentity currentIdentity = securityDomain.getCurrentSecurityIdentity();

        if (component instanceof EJBComponent == false) {
            throw EjbLogger.ROOT_LOGGER.unexpectedComponent(component, EJBComponent.class);
        }

        Method invokedMethod = context.getMethod();
        ComponentView componentView = context.getPrivateData(ComponentView.class);
        String viewClassOfInvokedMethod = componentView.getViewClass().getName();

        // shouldn't really happen if the interceptor was setup correctly. But let's be safe and do a check
        if (!viewClassName.equals(viewClassOfInvokedMethod) || !viewMethod.equals(invokedMethod)) {
            throw EjbLogger.ROOT_LOGGER.failProcessInvocation(getClass().getName(), invokedMethod, viewClassOfInvokedMethod, viewMethod, viewClassName);
        }

        EJBComponent ejbComponent = (EJBComponent) component;

        if(WildFlySecurityManager.isChecking()) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    hasPermission(ejbComponent, componentView, invokedMethod, currentIdentity);
                    return null;
                });
            } catch (PrivilegedActionException e) {
                throw e.getException();
            }
        } else {
            hasPermission(ejbComponent, componentView, invokedMethod, currentIdentity);
        }

        // successful authorization, let the invocation proceed
        return context.proceed();
    }

    private EJBMethodPermission createEjbMethodPermission(Method invokedMethod, EJBComponent ejbComponent, MethodInterfaceType methodIntfType) {
        return new EJBMethodPermission(ejbComponent.getComponentName(), methodIntfType.name(), invokedMethod);
    }

    private void hasPermission(EJBComponent ejbComponent, ComponentView componentView, Method method, SecurityIdentity securityIdentity) {
        MethodInterfaceType methodIntfType = getMethodInterfaceType(componentView.getPrivateData(MethodIntf.class));
        EJBMethodPermission permission = createEjbMethodPermission(method, ejbComponent, methodIntfType);
        ProtectionDomain domain = new ProtectionDomain (componentView.getProxyClass().getProtectionDomain().getCodeSource(), null, null, getGrantedRoles(securityIdentity));
        Policy policy = WildFlySecurityManager.isChecking() ? doPrivileged((PrivilegedAction<Policy>) Policy::getPolicy) : Policy.getPolicy();
        if (!policy.implies(domain, permission)) {
            throw EjbLogger.ROOT_LOGGER.invocationOfMethodNotAllowed(method,ejbComponent.getComponentName());
        }
    }

    /**
     *

     * Gets the {@code MethodInterfaceType} that corresponds to the specified {@code MethodIntf}.
     *

     *
     * @param viewType the {@code MethodIntf} type to be converted.
     * @return the converted type or {@code null} if the type cannot be converted.
     */
    protected MethodInterfaceType getMethodInterfaceType(MethodIntf viewType) {
        switch (viewType) {
            case HOME:
                return MethodInterfaceType.Home;
            case LOCAL_HOME:
                return MethodInterfaceType.LocalHome;
            case SERVICE_ENDPOINT:
                return MethodInterfaceType.ServiceEndpoint;
            case LOCAL:
                return MethodInterfaceType.Local;
            case REMOTE:
                return MethodInterfaceType.Remote;
            case TIMER:
                return MethodInterfaceType.Timer;
            case MESSAGE_ENDPOINT:
                return MethodInterfaceType.MessageEndpoint;
            default:
                return null;
        }
    }

    /**
     * Returns an array of {@link Principal} representing the roles associated with the identity
     * invoking the EJB. This method will check performs checks against run as identities in order to
     * resolve the correct set of roles to be granted.
     *
     * @param securityIdentity the identity invoking the EJB
     * @return an array of {@link Principal} representing the roles associated with the identity
     */
    public static Principal[] getGrantedRoles(SecurityIdentity securityIdentity) {
        Set<String> roles = new HashSet<>();

        for (String s : securityIdentity.getRoles("ejb")) {
            roles.add(s);
        }
        List<Principal> list = new ArrayList<>();
        Function<String, Principal> mapper = roleName -> (Principal) () -> roleName;
        for (String role : roles) {
            Principal principal = mapper.apply(role);
            list.add(principal);
        }
        return list.toArray(NO_PRINCIPALS);
    }
}


package org.jboss.as.jdr.util;

import org.jboss.vfs.VirtualFile;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;

/**
 * User: csams
 * Date: 11/4/12
 * Time: 3:40 PM
 */
public final class Utils {

    public static void safeClose(JarFile jf){
        try{
            if(jf != null) {
                jf.close();
            }
        }catch(Exception e){

        }
    }

    public static void safelyClose(InputStream is){
        try{
            if(is != null) {
                is.close();
            }
        }catch(Exception e){

        }
    }

    public static List<String> readLines(InputStream input) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input,StandardCharsets.UTF_8));
        List<String> result = new ArrayList<String>();
        String line = reader.readLine();

        while(line != null){
            result.add(line);
            line = reader.readLine();
        }
        return result;
    }

    public static String toString(VirtualFile r) throws IOException {
        return new String(toBytes(r), StandardCharsets.UTF_8);
    }

    public static byte[] toBytes(VirtualFile r) throws IOException {
        byte [] buffer = new byte[1024];
        InputStream is = r.openStream();

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        int bytesRead = is.read(buffer);
        while( bytesRead > -1 ) {
            os.write(buffer, 0, bytesRead);
            bytesRead = is.read(buffer);
        }

        Utils.safelyClose(is);
        return os.toByteArray();
    }

    /**
     * Ensure InputStream actually skips ahead the required number of bytes
     * @param is
     * @param amount
     * @throws IOException
     */
    public static void skip(InputStream is, long amount) throws IOException {
        long leftToSkip = amount;
        long amountSkipped = 0;
        while(leftToSkip > 0 && amountSkipped >= 0){
            amountSkipped = is.skip(leftToSkip);
            leftToSkip -= amountSkipped;
        }
    }

    public static boolean isSymlink(VirtualFile vFile) throws IOException {

        File file = vFile.getPhysicalFile();

        if(Utils.isWindows()){
            return false;
        }

        File fileInCanonicalDir = null;
        if (file.getParent() == null) {
            fileInCanonicalDir = file;
        } else {
            File canonicalDir = file.getParentFile().getCanonicalFile();
            fileInCanonicalDir = new File(canonicalDir, file.getName());
        }

        if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) {
            return false;
        } else {
            return true;
        }

    }

    public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";

    public static final String LINE_SEP = String.format("%n");
    public static final char WIN_SEP = '\\';
    public static final char SYS_SEP = File.separatorChar;

    public static boolean isWindows() {
        return SYS_SEP == WIN_SEP;
    }

    public static final long ONE_KB = 1024;

    public static final long ONE_MB = ONE_KB * ONE_KB;

    public static final long ONE_GB = ONE_KB * ONE_MB;

    public static final long ONE_TB = ONE_KB * ONE_GB;

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.messaging;

import static java.util.Arrays.asList;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequired;
import static org.jboss.as.controller.parsing.ParseUtils.readStringAttributeElement;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoAttributes;
import static org.jboss.as.controller.parsing.ParseUtils.requireSingleAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedElement;
import static org.jboss.as.messaging.CommonAttributes.DEFAULT;
import static org.jboss.as.messaging.CommonAttributes.JMS_BRIDGE;
import static org.jboss.as.messaging.CommonAttributes.REMOTING_INCOMING_INTERCEPTORS;
import static org.jboss.as.messaging.CommonAttributes.REMOTING_OUTGOING_INTERCEPTORS;
import static org.jboss.as.messaging.CommonAttributes.SELECTOR;
import static org.jboss.as.messaging.Element.DISCOVERY_GROUP_REF;
import static org.jboss.as.messaging.Element.STATIC_CONNECTORS;

import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import javax.xml.stream.XMLStreamException;

import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ListAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.as.messaging.jms.bridge.JMSBridgeDefinition;
import org.jboss.as.messaging.logging.MessagingLogger;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLExtendedStreamReader;

/**
 * Messaging subsystem 1.3 XML parser.
 *
 * @author Jeff Mesnil
 *
 */
public class Messaging13SubsystemParser extends Messaging12SubsystemParser {

    protected Messaging13SubsystemParser() {
    }

    @Override
    protected void checkClusterConnectionConstraints(XMLExtendedStreamReader reader, Set<Element> seen) throws XMLStreamException {
        // AS7-5598 relax constraints on the cluster-connection to accept one without static-connectors or discovery-group-ref
        // however it is still not valid to have both
        checkNotBothElements(reader, seen, STATIC_CONNECTORS, DISCOVERY_GROUP_REF);
    }

    @Override
    protected void checkBroadcastGroupConstraints(XMLExtendedStreamReader reader, Set<Element> seen) throws XMLStreamException {
        checkNotBothElements(reader, seen, Element.SOCKET_BINDING, Element.JGROUPS_STACK);
        checkNotBothElements(reader, seen, Element.JGROUPS_STACK, Element.GROUP_ADDRESS);
        checkNotBothElements(reader, seen, Element.GROUP_ADDRESS, Element.SOCKET_BINDING);
        if (seen.contains(Element.GROUP_ADDRESS) && !seen.contains(Element.GROUP_PORT)) {
            throw missingRequired(reader, EnumSet.of(Element.GROUP_PORT));
        }
    }

    @Override
    protected void checkDiscoveryGroupConstraints(XMLExtendedStreamReader reader, Set<Element> seen) throws XMLStreamException {
        checkNotBothElements(reader, seen, Element.SOCKET_BINDING, Element.JGROUPS_STACK);
        checkNotBothElements(reader, seen, Element.JGROUPS_STACK, Element.GROUP_ADDRESS);
        checkNotBothElements(reader, seen, Element.GROUP_ADDRESS, Element.SOCKET_BINDING);
        if (seen.contains(Element.GROUP_ADDRESS) && !seen.contains(Element.GROUP_PORT)) {
            throw missingRequired(reader, EnumSet.of(Element.GROUP_PORT));
        }
    }

    protected void handleUnknownConnectionFactoryAttribute(XMLExtendedStreamReader reader, Element element, ModelNode connectionFactory, boolean pooled)
            throws XMLStreamException {
        switch (element) {
            case CALL_FAILOVER_TIMEOUT:
            case COMPRESS_LARGE_MESSAGES:
                handleElementText(reader, element, connectionFactory);
                break;
            case USE_AUTO_RECOVERY:
            case INITIAL_MESSAGE_PACKET_SIZE:
                if (!pooled) {
                    throw unexpectedElement(reader);
                }
                handleElementText(reader, element, connectionFactory);
                break;
            case INITIAL_CONNECT_ATTEMPTS:
                if (!pooled) {
                    throw unexpectedElement(reader);
                }
                handleElementText(reader, element, "pooled", connectionFactory);
                break;
            default: {
                super.handleUnknownConnectionFactoryAttribute(reader, element, connectionFactory, pooled);
            }
        }
    }

    @Override
    protected void handleUnknownClusterConnectionAttribute(XMLExtendedStreamReader reader, Element element, ModelNode clusterConnectionAdd)
            throws XMLStreamException {
        switch (element) {
            case CALL_FAILOVER_TIMEOUT:
            case NOTIFICATION_ATTEMPTS:
            case NOTIFICATION_INTERVAL:
                handleElementText(reader, element, clusterConnectionAdd);
                break;
            default: {
                super.handleUnknownClusterConnectionAttribute(reader, element, clusterConnectionAdd);
            }
        }
    }

    @Override
    protected void handleUnknownConfigurationAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation) throws XMLStreamException {
        switch (element) {
            case CHECK_FOR_LIVE_SERVER:
            case BACKUP_GROUP_NAME:
            case REPLICATION_CLUSTERNAME:
                handleElementText(reader, element, operation);
                break;
            default: {
                super.handleUnknownConfigurationAttribute(reader, element, operation);
            }
        }
    }

    @Override
    protected void handleComplexConfigurationAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation) throws XMLStreamException {
        switch (element) {
            case REMOTING_INCOMING_INTERCEPTORS:
                processRemotingIncomingInterceptors(reader, operation);
                break;
            case REMOTING_OUTGOING_INTERCEPTORS:
                processRemotingOutgoingInterceptors(reader, operation);
                break;
            default: {
                super.handleComplexConfigurationAttribute(reader, element, operation);
            }
        }
    }

    @Override
    protected void handleUnknownBroadcastGroupAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation)
            throws XMLStreamException {
        switch (element) {
            case JGROUPS_STACK:
            case JGROUPS_CHANNEL:
                handleElementText(reader, element, operation);
                break;
            default: {
                super.handleUnknownBroadcastGroupAttribute(reader, element, operation);
            }
        }
    }

    @Override
    protected void handleUnknownDiscoveryGroupAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation)
            throws XMLStreamException {
        switch (element) {
            case JGROUPS_STACK:
            case JGROUPS_CHANNEL:
                handleElementText(reader, element, operation);
                break;
            default: {
                super.handleUnknownDiscoveryGroupAttribute(reader, element, operation);
            }
        }
    }

    protected void processHornetQServers(final XMLExtendedStreamReader reader, final ModelNode subsystemAddress, final List<ModelNode> list) throws XMLStreamException {
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Namespace schemaVer = Namespace.forUri(reader.getNamespaceURI());
            switch (schemaVer) {
                case MESSAGING_1_0:
                case UNKNOWN:
                    throw ParseUtils.unexpectedElement(reader);
                default: {
                    final Element element = Element.forName(reader.getLocalName());
                    switch (element) {
                        case HORNETQ_SERVER:
                            processHornetQServer(reader, subsystemAddress, list, schemaVer);
                            break;
                        case JMS_BRIDGE:
                            processJmsBridge(reader, subsystemAddress, list);
                            break;
                        default:
                            throw ParseUtils.unexpectedElement(reader);
                    }
                }
            }
        }
    }

    private void processJmsBridge(XMLExtendedStreamReader reader, ModelNode subsystemAddress, List<ModelNode> list) throws XMLStreamException {
        String bridgeName = null;
        String moduleName = null;

        final int count = reader.getAttributeCount();
        for (int n = 0; n < count; n++) {
            String attrName = reader.getAttributeLocalName(n);
            Attribute attribute = Attribute.forName(attrName);
            switch (attribute) {
                case NAME:
                    bridgeName = reader.getAttributeValue(n);
                    break;
                case MODULE:
                    moduleName = reader.getAttributeValue(n);
                    break;
                default:
                    throw unexpectedAttribute(reader, n);
            }
        }

        if (bridgeName == null || bridgeName.length() == 0) {
            bridgeName = DEFAULT;
        }

        final ModelNode address = subsystemAddress.clone();
        address.add(JMS_BRIDGE, bridgeName);
        address.protect();

        final ModelNode operation = new ModelNode();
        operation.get(OP).set(ADD);
        operation.get(OP_ADDR).set(address);
        list.add(operation);

        if (moduleName != null && moduleName.length() > 0) {
            JMSBridgeDefinition.MODULE.parseAndSetParameter(moduleName, operation, reader);
        }

        while(reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            switch (element) {
                case SOURCE:
                case TARGET:
                    processJmsBridgeResource(reader, operation, element.getLocalName());
                    break;
                case QUALITY_OF_SERVICE:
                case FAILURE_RETRY_INTERVAL:
                case MAX_RETRIES:
                case MAX_BATCH_SIZE:
                case MAX_BATCH_TIME:
                case SUBSCRIPTION_NAME:
                case CLIENT_ID:
                case ADD_MESSAGE_ID_IN_HEADER:
                    handleElementText(reader, element, operation);
                    break;
                case SELECTOR:
                    requireSingleAttribute(reader, CommonAttributes.STRING);
                    final String selector = readStringAttributeElement(reader, CommonAttributes.STRING);
                    SELECTOR.parseAndSetParameter(selector, operation, reader);
                    break;
                default:
                    throw ParseUtils.unexpectedElement(reader);
            }
        }
    }

    private void processJmsBridgeResource(XMLExtendedStreamReader reader, ModelNode operation, String modelName) throws XMLStreamException {
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            switch (element) {
                case USER:
                case PASSWORD:
                    handleElementText(reader, element, modelName, operation);
                    break;
                case CONNECTION_FACTORY:
                case DESTINATION:
                    handleSingleAttribute(reader, element, modelName, CommonAttributes.NAME, operation);
                    break;
                case CONTEXT:
                    ModelNode context = operation.get(element.getDefinition(modelName).getName());
                    processContext(reader, context);
                    break;
                default:
                    throw ParseUtils.unexpectedElement(reader);
            }
        }
    }

    private void processRemotingIncomingInterceptors(XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {
        requireNoAttributes(reader);
        while(reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            if (element == Element.CLASS_NAME) {
                final String value = reader.getElementText();
                REMOTING_INCOMING_INTERCEPTORS.parseAndAddParameterElement(value, operation, reader);
            } else {
                throw unexpectedElement(reader);
            }
        }
    }

    private void processRemotingOutgoingInterceptors(XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {
        requireNoAttributes(reader);
        while(reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            if (element == Element.CLASS_NAME) {
                final String value = reader.getElementText();
                REMOTING_OUTGOING_INTERCEPTORS.parseAndAddParameterElement(value, operation, reader);
            } else {
                throw unexpectedElement(reader);
            }
        }
    }

    private void processContext(XMLExtendedStreamReader reader, ModelNode context) throws XMLStreamException {
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            if (element == Element.PROPERTY) {
                int count = reader.getAttributeCount();
                String key = null;
                String value = null;
                for (int n = 0; n < count; n++) {
                    String attrName = reader.getAttributeLocalName(n);
                    Attribute attribute = Attribute.forName(attrName);
                    switch (attribute) {
                        case KEY:
                            key = reader.getAttributeValue(n);
                            break;
                        case VALUE:
                            value = reader.getAttributeValue(n);
                            break;
                        default:
                            throw unexpectedAttribute(reader, n);
                    }
                }
                context.get(key).set(value);
                ParseUtils.requireNoContent(reader);
            } else {
                throw unexpectedElement(reader);
            }
        }
    }

    /**
     * [AS7-5808] Support space-separated roles names for backwards compatibility and comma-separated ones for compatibility with
     * HornetQ configuration.
     *
     * Roles are persisted using space character delimiter in {@link MessagingXMLWriter}.
     */
    @Override
    protected List<String> parseRolesAttribute(XMLExtendedStreamReader reader, int index) throws XMLStreamException {
        String roles = reader.getAttributeValue(index);
        return asList(roles.split("[,\\s]+"));
    }

    static void handleSingleAttribute(final XMLExtendedStreamReader reader, final Element element, final String modelName, String attributeName, final ModelNode node) throws XMLStreamException {
        AttributeDefinition attributeDefinition = element.getDefinition(modelName);
        final String value = readStringAttributeElement(reader, attributeName);
        if (attributeDefinition instanceof SimpleAttributeDefinition) {
            ((SimpleAttributeDefinition) attributeDefinition).parseAndSetParameter(value, node, reader);
        } else if (attributeDefinition instanceof ListAttributeDefinition) {
            ((ListAttributeDefinition) attributeDefinition).parseAndAddParameterElement(value, node, reader);
        }
    }

    /**
     * Check that not both elements have been defined
     */
    protected static void checkNotBothElements(XMLExtendedStreamReader reader, Set<Element> seen, Element element1, Element element2) throws XMLStreamException {
        if (seen.contains(element1) && seen.contains(element2)) {
            throw new XMLStreamException(MessagingLogger.ROOT_LOGGER.onlyOneRequired(element1.getLocalName(), element2.getLocalName()), reader.getLocation());
        }
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.as.ejb3.iiop.stub;

import static java.security.AccessController.doPrivileged;

import java.security.PrivilegedAction;
import org.jboss.as.ejb3.logging.EjbLogger;
import javax.rmi.CORBA.Util;
import org.jboss.ejb.iiop.HandleImplIIOP;
import org.jboss.ejb.iiop.HomeHandleImplIIOP;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.RemarshalException;
import org.omg.CORBA_2_3.portable.InputStream;
import org.omg.CORBA_2_3.portable.OutputStream;
import org.wildfly.iiop.openjdk.rmi.marshal.strategy.StubStrategy;

/**
 * Dynamically generated IIOP stub classes extend this abstract superclass,
 * which extends javax.rmi.CORBA.Stub.
 *

 * A DynamicIIOPStub is a local proxy of a remote object. It has
 * methods (invoke(), invokeBoolean(),
 * invokeByte(), and so on) that send an IIOP request to the
 * server that implements the remote object, receive the reply from the
 * server, and return the results to the caller. All of these methods take
 * the IDL name of the operation, a StubStrategy instance to
 * be used for marshalling parameters and unmarshalling the result, plus an
 * array of operation parameters.
 *
 * @author Francisco Reverbel
 * @author Stuart Douglas
 */
public abstract class DynamicIIOPStub
        extends javax.rmi.CORBA.Stub {
    /**
     * @since 4.2.0
     */
    static final long serialVersionUID = 3283717238950231589L;

    /**
     * My handle (either a HandleImplIIOP or a HomeHandleImplIIOP).
     */
    private Object handle = null;

    private static void trace(final String msg) {
        if (EjbLogger.EJB3_INVOCATION_LOGGER.isTraceEnabled())
            EjbLogger.EJB3_INVOCATION_LOGGER.trace(msg);
    }

    private static void tracef(final String format, final Object... params) {
        if (EjbLogger.EJB3_INVOCATION_LOGGER.isTraceEnabled())
            EjbLogger.EJB3_INVOCATION_LOGGER.tracef(format, params);
    }

    /**
     * Constructs a DynamicIIOPStub.
     */
    public DynamicIIOPStub() {
        super();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns an Object result to the caller.
     */
    public Object invoke(String operationName, final StubStrategy stubStrategy, Object[] params) throws Throwable {
        if (operationName.equals("_get_handle")
                && this instanceof javax.ejb.EJBObject) {
            if (handle == null) {
                handle = new HandleImplIIOP(this);
            }
            return handle;
        } else if (operationName.equals("_get_homeHandle")
                && this instanceof javax.ejb.EJBHome) {
            if (handle == null) {
                handle = new HomeHandleImplIIOP(this);
            }
            return handle;
        } else {
            //FIXME
            // all invocations are now made using remote invocation
            // local invocations between two different applications cause
            // ClassCastException between Stub and Interface
            // (two different modules are loading the classes)
            // problem was unnoticeable with JacORB because it uses
            // remote invocations to all stubs to which interceptors are
            // registered and a result all that JacORB always used
            // remote invocations

            // remote call path

            // To check whether this is a local stub or not we must call
            // org.omg.CORBA.portable.ObjectImpl._is_local(), and _not_
            // javax.rmi.CORBA.Util.isLocal(Stub s), which in Sun's JDK
            // always return false.

            InputStream in = null;
            try {
                try {
                    OutputStream out =
                            (OutputStream) _request(operationName, true);
                    stubStrategy.writeParams(out, params);
                    tracef("sent request: %s", operationName);
                    in = (InputStream) _invoke(out);
                    if (stubStrategy.isNonVoid()) {
                        trace("received reply");
                        final InputStream finalIn = in;
                        return doPrivileged(new PrivilegedAction<Object>() {
                            public Object run() {
                                return stubStrategy.readRetval(finalIn);
                            }
                        });
                    } else {
                        return null;
                    }
                } catch (final ApplicationException ex) {
                    trace("got application exception");
                    in = (InputStream) ex.getInputStream();
                    final InputStream finalIn1 = in;
                    throw doPrivileged(new PrivilegedAction<Exception>() {
                        public Exception run() {
                            return stubStrategy.readException(ex.getId(), finalIn1);
                        }
                    });
                } catch (RemarshalException ex) {
                    trace("got remarshal exception");
                    return invoke(operationName, stubStrategy, params);
                }
            } catch (SystemException ex) {
                if (EjbLogger.EJB3_INVOCATION_LOGGER.isTraceEnabled()) {
                    EjbLogger.EJB3_INVOCATION_LOGGER.trace("CORBA system exception in IIOP stub", ex);
                }
                throw Util.mapSystemException(ex);
            } finally {
                _releaseReply(in);
            }
        }
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a boolean result to the caller.
     */
    public boolean invokeBoolean(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Boolean) invoke(operationName,
                stubStrategy, params)).booleanValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a byte result to the caller.
     */
    public byte invokeByte(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Number) invoke(operationName,
                stubStrategy, params)).byteValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a char result to the caller.
     */
    public char invokeChar(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Character) invoke(operationName,
                stubStrategy, params)).charValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a short result to the caller.
     */
    public short invokeShort(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Number) invoke(operationName,
                stubStrategy, params)).shortValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns an int result to the caller.
     */
    public int invokeInt(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Number) invoke(operationName, stubStrategy, params)).intValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a long result to the caller.
     */
    public long invokeLong(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Number) invoke(operationName, stubStrategy, params)).longValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a float result to the caller.
     */
    public float invokeFloat(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Number) invoke(operationName, stubStrategy, params)).floatValue();
    }

    /**
     * Sends a request message to the server, receives the reply from the
     * server, and returns a double result to the caller.
     */
    public double invokeDouble(String operationName, StubStrategy stubStrategy, Object[] params) throws Throwable {
        return ((Number) invoke(operationName,
                stubStrategy, params)).doubleValue();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("JBossDynStub[").append(getClass().getName()).append(", ");
        try {
            builder.append(_orb().object_to_string(this));
        } catch (BAD_OPERATION ignored) {
            builder.append("*DISCONNECTED*");
        }
        builder.append("]");
        return builder.toString();
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.wildfly.extension.messaging.activemq;

import static org.wildfly.extension.messaging.activemq.ActiveMQActivationService.getActiveMQServer;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.DEAD_LETTER_ADDRESS;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.EXPIRY_ADDRESS;

import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;

/**
 * {@code OperationStepHandler} adding a new address setting.
 *
 * @author Emanuel Muckenhuber
 */
class AddressSettingAdd extends AbstractAddStepHandler {

    static final OperationStepHandler INSTANCE = new AddressSettingAdd(AddressSettingDefinition.ATTRIBUTES);

    private AddressSettingAdd(AttributeDefinition... attributes) {
        super(attributes);
    }

    @Override
    protected void populateModel(OperationContext context, ModelNode operation, final Resource resource) throws OperationFailedException {
        super.populateModel(context, operation, resource);

        context.addStep(AddressSettingsValidator.ADD_VALIDATOR, OperationContext.Stage.MODEL);
    }

    @Override
    protected void performRuntime(final OperationContext context, final ModelNode operation, final ModelNode model) throws OperationFailedException {
        final ActiveMQServer server = getActiveMQServer(context, operation);
        if(server != null) {
            final AddressSettings settings = createSettings(context, model);
            server.getAddressSettingsRepository().addMatch(context.getCurrentAddressValue(), settings);
        }
    }

    /**
     * Create a setting.
     *
     * @param context the operation context
     * @param config the detyped config
     * @return the address settings
     *
     * @throws OperationFailedException if the model is invalid
     */
    static AddressSettings createSettings(final OperationContext context, final ModelNode config) throws OperationFailedException {
        final AddressSettings settings = new AddressSettings();

        if (config.hasDefined(AddressSettingDefinition.ADDRESS_FULL_MESSAGE_POLICY.getName())) {
            final AddressFullMessagePolicy addressPolicy = AddressFullMessagePolicy.valueOf(AddressSettingDefinition.ADDRESS_FULL_MESSAGE_POLICY.resolveModelAttribute(context, config).asString());
            settings.setAddressFullMessagePolicy(addressPolicy);
        }
        if (config.hasDefined(DEAD_LETTER_ADDRESS.getName())) {
            settings.setDeadLetterAddress(asSimpleString(DEAD_LETTER_ADDRESS.resolveModelAttribute(context, config), null));
        }
        if (config.hasDefined(AddressSettingDefinition.LAST_VALUE_QUEUE.getName())) {
            settings.setDefaultLastValueQueue(AddressSettingDefinition.LAST_VALUE_QUEUE.resolveModelAttribute(context, config).asBoolean());
        }
        if (config.hasDefined(AddressSettingDefinition.MAX_DELIVERY_ATTEMPTS.getName())) {
            settings.setMaxDeliveryAttempts(AddressSettingDefinition.MAX_DELIVERY_ATTEMPTS.resolveModelAttribute(context, config).asInt());
        }
        if (config.hasDefined(AddressSettingDefinition.MAX_SIZE_BYTES.getName())) {
            settings.setMaxSizeBytes(AddressSettingDefinition.MAX_SIZE_BYTES.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.MESSAGE_COUNTER_HISTORY_DAY_LIMIT.getName())) {
            settings.setMessageCounterHistoryDayLimit(AddressSettingDefinition.MESSAGE_COUNTER_HISTORY_DAY_LIMIT.resolveModelAttribute(context, config).asInt());
        }
        if (config.hasDefined(CommonAttributes.EXPIRY_ADDRESS.getName())) {
            settings.setExpiryAddress(asSimpleString(EXPIRY_ADDRESS.resolveModelAttribute(context, config), null));
        }
        if (config.hasDefined(AddressSettingDefinition.EXPIRY_DELAY.getName())) {
            settings.setExpiryDelay(AddressSettingDefinition.EXPIRY_DELAY.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.REDELIVERY_DELAY.getName())) {
            settings.setRedeliveryDelay(AddressSettingDefinition.REDELIVERY_DELAY.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.REDELIVERY_MULTIPLIER.getName())) {
            settings.setRedeliveryMultiplier(AddressSettingDefinition.REDELIVERY_MULTIPLIER.resolveModelAttribute(context, config).asDouble());
        }
        if (config.hasDefined(AddressSettingDefinition.MAX_REDELIVERY_DELAY.getName())) {
            settings.setMaxRedeliveryDelay(AddressSettingDefinition.MAX_REDELIVERY_DELAY.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.REDISTRIBUTION_DELAY.getName())) {
            settings.setRedistributionDelay(AddressSettingDefinition.REDISTRIBUTION_DELAY.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.PAGE_SIZE_BYTES.getName())) {
            settings.setPageSizeBytes(AddressSettingDefinition.PAGE_SIZE_BYTES.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.PAGE_MAX_CACHE_SIZE.getName())) {
            settings.setPageCacheMaxSize(AddressSettingDefinition.PAGE_MAX_CACHE_SIZE.resolveModelAttribute(context, config).asInt());
        }
        if (config.hasDefined(AddressSettingDefinition.SEND_TO_DLA_ON_NO_ROUTE.getName())) {
            settings.setSendToDLAOnNoRoute(AddressSettingDefinition.SEND_TO_DLA_ON_NO_ROUTE.resolveModelAttribute(context, config).asBoolean());
        }
        if (config.hasDefined(AddressSettingDefinition.SLOW_CONSUMER_CHECK_PERIOD.getName())) {
            settings.setSlowConsumerCheckPeriod(AddressSettingDefinition.SLOW_CONSUMER_CHECK_PERIOD.resolveModelAttribute(context, config).asLong());
        }
        if (config.hasDefined(AddressSettingDefinition.SLOW_CONSUMER_POLICY.getName())) {
            final SlowConsumerPolicy slowConsumerPolicy = SlowConsumerPolicy.valueOf(AddressSettingDefinition.SLOW_CONSUMER_POLICY.resolveModelAttribute(context, config).asString());
            settings.setSlowConsumerPolicy(slowConsumerPolicy);
        }
        if (config.hasDefined(AddressSettingDefinition.SLOW_CONSUMER_THRESHOLD.getName())) {
            settings.setSlowConsumerThreshold(AddressSettingDefinition.SLOW_CONSUMER_THRESHOLD.resolveModelAttribute(context, config).asLong());
        }
        // always set the auto-create|delete-jms-queues attributes as their default attribute values differ from Artemis defaults.
        settings.setAutoCreateJmsQueues(AddressSettingDefinition.AUTO_CREATE_JMS_QUEUES.resolveModelAttribute(context, config).asBoolean());
        settings.setAutoDeleteJmsQueues(AddressSettingDefinition.AUTO_DELETE_JMS_QUEUES.resolveModelAttribute(context, config).asBoolean());
        settings.setAutoCreateQueues(AddressSettingDefinition.AUTO_CREATE_QUEUES.resolveModelAttribute(context, config).asBoolean());
        settings.setAutoDeleteQueues(AddressSettingDefinition.AUTO_DELETE_QUEUES.resolveModelAttribute(context, config).asBoolean());
        settings.setAutoCreateAddresses(AddressSettingDefinition.AUTO_CREATE_ADDRESSES.resolveModelAttribute(context, config).asBoolean());
        settings.setAutoDeleteAddresses(AddressSettingDefinition.AUTO_DELETE_ADDRESSES.resolveModelAttribute(context, config).asBoolean());
        return settings;
    }

    static SimpleString asSimpleString(final ModelNode node, final String defVal) {
        return SimpleString.toSimpleString(node.getType() != ModelType.UNDEFINED ? node.asString() : defVal);
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.ee.component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jboss.as.ee.logging.EeLogger;
import org.jboss.as.ee.component.interceptors.InterceptorClassDescription;
import org.jboss.as.ee.concurrent.ConcurrentContext;
import org.jboss.as.ee.naming.InjectedEENamespaceContextSelector;
import org.jboss.msc.service.ServiceName;

/**
 * @author David M. Lloyd
 */
public final class EEModuleDescription implements ResourceInjectionTarget {
    private final String applicationName;
    private volatile String moduleName;
    private final String earApplicationName;
    //distinct name defaults to the empty string
    private volatile String distinctName = "";
    private final Map<String, ComponentDescription> componentsByName = new HashMap<String, ComponentDescription>();
    private final Map<String, List<ComponentDescription>> componentsByClassName = new HashMap<String, List<ComponentDescription>>();
    private final Map<String, EEModuleClassDescription> classDescriptions = new HashMap<String, EEModuleClassDescription>();
    private final Map<String, InterceptorClassDescription> interceptorClassOverrides = new HashMap<String, InterceptorClassDescription>();

    /**
     * Additional interceptor environment that was defined in the deployment descriptor <interceptors/> element.
     */
    private final Map<String, InterceptorEnvironment> interceptorEnvironment = new HashMap<String, InterceptorEnvironment>();

    /**
     * A map of message destinations names to their resolved JNDI name
     */
    private final Map<String, String> messageDestinations = new HashMap<String, String>();

    private InjectedEENamespaceContextSelector namespaceContextSelector;

    // Module Bindings
    private final List<BindingConfiguration> bindingConfigurations = new ArrayList<BindingConfiguration>();
    //injections that have been set in the components deployment descriptor
    private final Map<String, Map<InjectionTarget, ResourceInjectionConfiguration>> resourceInjections = new HashMap<String, Map<InjectionTarget, ResourceInjectionConfiguration>>();

    private final boolean appClient;

    private ServiceName defaultClassIntrospectorServiceName = ReflectiveClassIntrospector.SERVICE_NAME;

    private final ConcurrentContext concurrentContext;

    private final EEDefaultResourceJndiNames defaultResourceJndiNames;

    /**
     * The default security domain for the module.
     */
    private String defaultSecurityDomain;

    /**
     * The number of registered startup beans.
     */
    private int startupBeansCount;

    /**
     * Construct a new instance.
     *
     * @param applicationName    the application name (which is same as the module name if the .ear is absent)
     * @param moduleName         the module name
     * @param earApplicationName The application name (which is null if the .ear is absent)
     * @param appClient          indicates if the process type is an app client
     */
    public EEModuleDescription(final String applicationName, final String moduleName, final String earApplicationName, final boolean appClient) {
        this.applicationName = applicationName;
        this.moduleName = moduleName;
        this.earApplicationName = earApplicationName;
        this.appClient = appClient;
        this.concurrentContext = new ConcurrentContext();
        this.defaultResourceJndiNames = new EEDefaultResourceJndiNames();
    }

    /**
     * Adds or retrieves an existing EEModuleClassDescription for the local module. This method should only be used
     * for classes that reside within the current deployment unit, usually by annotation scanners that are attaching annotation
     * information.
     *

     * This
     *
     * @param className The class name
     * @return The new or existing {@link EEModuleClassDescription}
     */
    public EEModuleClassDescription addOrGetLocalClassDescription(final String className) {
        if (className == null) {
            throw EeLogger.ROOT_LOGGER.nullVar("className", "module", moduleName);
        }
        EEModuleClassDescription ret = classDescriptions.get(className);
        if (ret == null) {
            classDescriptions.put(className, ret = new EEModuleClassDescription(className));
        }
        return ret;
    }

    /**
     * Returns a class that is local to this module
     *
     * @param className The class
     * @return The description, or null if not found
     */
    EEModuleClassDescription getClassDescription(final String className) {
        return classDescriptions.get(className);
    }

    /**
     * Returns all class descriptions in this module
     *
     * @return All class descriptions
     */
    public Collection<EEModuleClassDescription> getClassDescriptions() {
        return classDescriptions.values();
    }

    public ServiceName getDefaultClassIntrospectorServiceName() {
        return defaultClassIntrospectorServiceName;
    }

    public void setDefaultClassIntrospectorServiceName(ServiceName defaultClassIntrospectorServiceName) {
        this.defaultClassIntrospectorServiceName = defaultClassIntrospectorServiceName;
    }

    /**
     * Add a component to this module.
     *
     * @param description the component description
     */
    public void addComponent(ComponentDescription description) {
        final String componentName = description.getComponentName();
        final String componentClassName = description.getComponentClassName();
        if (componentName == null) {
            throw EeLogger.ROOT_LOGGER.nullVar("componentName", "module", moduleName);
        }
        if (componentClassName == null) {
            throw EeLogger.ROOT_LOGGER.nullVar("componentClassName","module", moduleName);
        }
        if (componentsByName.containsKey(componentName)) {
            throw EeLogger.ROOT_LOGGER.componentAlreadyDefined(componentName);
        }
        componentsByName.put(componentName, description);
        List<ComponentDescription> list = componentsByClassName.get(componentClassName);
        if (list == null) {
            componentsByClassName.put(componentClassName, list = new ArrayList<ComponentDescription>(1));
        }
        list.add(description);
    }

    public void removeComponent(final String componentName, final String componentClassName) {
        componentsByName.remove(componentName);
        componentsByClassName.remove(componentClassName);
    }

    /**
     * Returns the application name which can be the same as the module name, in the absence of a .ear top level
     * deployment
     *
     * @return
     * @see {@link #getEarApplicationName()}
     */
    public String getApplicationName() {
        //if no application name is set just return the module name
        //this means that if the module name is changed the application name
        //will change as well
        if(applicationName == null) {
            return moduleName;
        }
        return applicationName;
    }

    public String getModuleName() {
        return moduleName;
    }

    public boolean hasComponent(final String name) {
        return componentsByName.containsKey(name);
    }

    /**
     * @return true if the process type is an app client
     */
    public boolean isAppClient() {
        return appClient;
    }

    public void setModuleName(String moduleName) {
        this.moduleName = moduleName;
    }

    public ComponentDescription getComponentByName(String name) {
        return componentsByName.get(name);
    }

    public List<ComponentDescription> getComponentsByClassName(String className) {
        final List<ComponentDescription> ret = componentsByClassName.get(className);
        return ret == null ? Collections.<ComponentDescription>emptyList() : ret;
    }

    public Collection<ComponentDescription> getComponentDescriptions() {
        return componentsByName.values();
    }

    public InjectedEENamespaceContextSelector getNamespaceContextSelector() {
        return namespaceContextSelector;
    }

    public void setNamespaceContextSelector(InjectedEENamespaceContextSelector namespaceContextSelector) {
        this.namespaceContextSelector = namespaceContextSelector;
    }

    public String getDistinctName() {
        return distinctName;
    }

    public void setDistinctName(String distinctName) {
        if (distinctName == null) {
            throw EeLogger.ROOT_LOGGER.nullVar("distinctName", "module", moduleName);
        }
        this.distinctName = distinctName;
    }

    /**
     * Unlike the {@link #getApplicationName()} which follows the Java EE6 spec semantics i.e. application name is the
     * name of the top level deployment (even if it is just a jar and not an ear), this method returns the
     * application name which follows the EJB spec semantics i.e. the application name is the
     * .ear name or any configured value in application.xml. This method returns null in the absence of a .ear
     *
     * @return
     */
    public String getEarApplicationName() {
        return this.earApplicationName;
    }

    /**
     * Get module level interceptor method overrides that are set up in ejb-jar.xml
     *
     * @param className The class name
     * @return The overrides, or null if no overrides have been set up
     */
    public InterceptorClassDescription getInterceptorClassOverride(final String className) {
        return interceptorClassOverrides.get(className);
    }

    /**
     * Adds a module level interceptor class override, it is merged with any existing overrides if they exist
     *
     * @param className The class name
     * @param override  The override
     */
    public void addInterceptorMethodOverride(final String className, final InterceptorClassDescription override) {
        interceptorClassOverrides.put(className, InterceptorClassDescription.merge(interceptorClassOverrides.get(className), override));
    }

    public List<BindingConfiguration> getBindingConfigurations() {
        return bindingConfigurations;
    }

    public void addResourceInjection(final ResourceInjectionConfiguration injection) {
        String className = injection.getTarget().getClassName();
        Map<InjectionTarget, ResourceInjectionConfiguration> map = resourceInjections.get(className);
        if (map == null) {
            resourceInjections.put(className, map = new HashMap<InjectionTarget, ResourceInjectionConfiguration>());
        }
        map.put(injection.getTarget(), injection);
    }

    public Map<InjectionTarget, ResourceInjectionConfiguration> getResourceInjections(final String className) {
        Map<InjectionTarget, ResourceInjectionConfiguration> injections = resourceInjections.get(className);
        if (injections == null) {
            return Collections.emptyMap();
        } else {
            return Collections.unmodifiableMap(injections);
        }
    }

    public void addMessageDestination(final String name, final String jndiName) {
        messageDestinations.put(name, jndiName);
    }

    public Map<String, String> getMessageDestinations() {
        return Collections.unmodifiableMap(messageDestinations);
    }

    public void addInterceptorEnvironment(final String interceptorClassName, final InterceptorEnvironment env) {
        interceptorEnvironment.put(interceptorClassName, env);
    }

    public Map<String, InterceptorEnvironment> getInterceptorEnvironment() {
        return interceptorEnvironment;
    }

    public ConcurrentContext getConcurrentContext() {
        return concurrentContext;
    }

    public EEDefaultResourceJndiNames getDefaultResourceJndiNames() {
        return defaultResourceJndiNames;
    }

    public String getDefaultSecurityDomain() {
        return defaultSecurityDomain;
    }

    public void setDefaultSecurityDomain(String defaultSecurityDomain) {
        this.defaultSecurityDomain = defaultSecurityDomain;
    }

    public int getStartupBeansCount() {
        return this.startupBeansCount;
    }

    public int registerStartupBean() {
        return ++this.startupBeansCount;
    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.as.weld.services;

import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.jboss.as.weld.logging.WeldLogger;
import org.jboss.modules.ModuleClassLoader;
import org.jboss.weld.bootstrap.api.Singleton;
import org.jboss.weld.bootstrap.api.SingletonProvider;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * Singleton Provider that uses the TCCL to figure out the current application.
 *
 * @author Stuart Douglas
 */
public class ModuleGroupSingletonProvider extends SingletonProvider {

    /**
     * Map of the top level class loader to all class loaders in a deployment
     */
    public static final Map<ClassLoader, Set<ClassLoader>> deploymentClassLoaders = new ConcurrentHashMap<ClassLoader, Set<ClassLoader>>();

    /**
     * Maps a top level class loader to all CL's in the deployment
     */
    public static void addClassLoaders(ClassLoader topLevel, Set<ClassLoader> allClassLoaders) {
        deploymentClassLoaders.put(topLevel, allClassLoaders);
    }

    /**
     * Removes the class loader mapping
     */
    public static void removeClassLoader(ClassLoader topLevel) {
        deploymentClassLoaders.remove(topLevel);
    }

    @Override
    public <T> Singleton<T> create(Class<? extends T> type) {
        return new TCCLSingleton<T>();
    }

    @SuppressWarnings("unused")
    private static class TCCLSingleton<T> implements Singleton<T> {

        private volatile Map<ClassLoader, T> store = Collections.emptyMap();
        private volatile Map<String, T> contextIdStore = Collections.emptyMap();

        public T get() {
            T instance = store.get(findParentModuleCl(WildFlySecurityManager.getCurrentContextClassLoaderPrivileged()));
            if (instance == null) {
                throw WeldLogger.ROOT_LOGGER.singletonNotSet(WildFlySecurityManager.getCurrentContextClassLoaderPrivileged());
            }
            return instance;
        }

        public synchronized void set(T object) {
            final Map<ClassLoader, T> store = new IdentityHashMap<ClassLoader, T>(this.store);
            ClassLoader classLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
            store.put(classLoader, object);
            if (deploymentClassLoaders.containsKey(classLoader)) {
                for (ClassLoader cl : deploymentClassLoaders.get(classLoader)) {
                    store.put(cl, object);
                }
            }
            this.store = store;
        }

        public synchronized void clear() {
            ClassLoader classLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
            final Map<ClassLoader, T> store = new IdentityHashMap<ClassLoader, T>(this.store);
            store.remove(classLoader);
            if (deploymentClassLoaders.containsKey(classLoader)) {
                for (ClassLoader cl : deploymentClassLoaders.get(classLoader)) {
                    store.remove(cl);
                }
            }
            this.store = store;
        }

        public boolean isSet() {
            return store.containsKey(findParentModuleCl(WildFlySecurityManager.getCurrentContextClassLoaderPrivileged()));
        }

        /**
         * If a custom CL is in use we want to get the module CL it delegates to
         * @param classLoader The current CL
         * @returnThe corresponding module CL
         */
        private ClassLoader findParentModuleCl(ClassLoader classLoader) {
            ClassLoader c = classLoader;
            while (c != null && !(c instanceof ModuleClassLoader)) {
                c = c.getParent();
            }
            return c;
        }

        /*
         * Default implementation of the Weld 1.2 API
         *
         * This provides forward binary compatibility with Weld 1.2 (OSGi integration).
         * It is OK to ignore the id parameter as TCCL is still used to identify the singleton in Java EE.
         */
        public T get(String id) {
            T val = contextIdStore.get(id);
            if(val != null) {
                return val;
            }
            return get();
        }

        public boolean isSet(String id) {
            T val = contextIdStore.get(id);
            if(val != null) {
                return true;
            }
            return isSet();
        }

        public synchronized void set(String id, T object) {
            set(object);
            final HashMap<String, T> store = new HashMap<String, T>(this.contextIdStore);
            store.put(id, object);
            this.contextIdStore = store;
        }

        public synchronized void clear(String id) {
            clear();
            final HashMap<String, T> store = new HashMap<String, T>(this.contextIdStore);
            store.remove(id);
            this.contextIdStore = store;
        }
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.clustering.controller.transform;

import org.jboss.as.clustering.controller.Operations;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.transform.OperationResultTransformer;
import org.jboss.as.controller.transform.OperationTransformer;
import org.jboss.as.controller.transform.TransformationContext;
import org.jboss.dmr.ModelNode;

/**
 * @author Paul Ferraro
 */
public class SimpleWriteAttributeOperationTransformer implements OperationTransformer {

    private final PathAddressTransformer addressTransformer;

    public SimpleWriteAttributeOperationTransformer(PathAddressTransformer addressTransformer) {
        this.addressTransformer = addressTransformer;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) {
        String name = Operations.getAttributeName(operation);
        ModelNode value = Operations.getAttributeValue(operation);
        ModelNode legacyOperation = org.jboss.as.controller.client.helpers.Operations.createWriteAttributeOperation(this.addressTransformer.transform(address).toModelNode(), name, value);
        return new TransformedOperation(legacyOperation, OperationResultTransformer.ORIGINAL_RESULT);
    }
}