/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, 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.webservices.security;

import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Set;
import java.util.concurrent.Callable;

import javax.security.auth.Subject;

import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.security.SecurityContext;
import org.jboss.security.SecurityContextAssociation;
import org.jboss.security.SecurityContextFactory;

/**
 * Adaptor of org.jboss.as.security.plugins.SecurityDomainContext to org.jboss.wsf.spi.security.SecurityDomainContext
 *
 * @author alessio.soldano@jboss.com
 * @since 13-May-2011
 */
public final class SecurityDomainContextImpl implements org.jboss.wsf.spi.security.SecurityDomainContext {

    private final SecurityDomainContext context;

    public SecurityDomainContextImpl(SecurityDomainContext context) {
        this.context = context;
    }

    @Override
    public boolean isValid(Principal principal, Object credential, Subject activeSubject) {
        return context.getAuthenticationManager().isValid(principal, credential, activeSubject);
    }

    @Override
    public boolean doesUserHaveRole(Principal principal, Set<Principal> roles) {
        return context.getAuthorizationManager().doesUserHaveRole(principal, roles);
    }

    @Override
    public String getSecurityDomain() {
        return context.getAuthenticationManager().getSecurityDomain();
    }

    @Override
    public Set<Principal> getUserRoles(Principal principal) {
        return context.getAuthorizationManager().getUserRoles(principal);
    }

    @Override
    public void pushSubjectContext(final Subject subject, final Principal principal, final Object credential) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {

            public Void run() {
                SecurityContext securityContext = SecurityContextAssociation.getSecurityContext();
                if (securityContext == null) {
                    securityContext = createSecurityContext(getSecurityDomain());
                    setSecurityContextOnAssociation(securityContext);
                }
                securityContext.getUtil().createSubjectInfo(principal, credential, subject);
                return null;
            }
        });
    }

    /**
     * Create a JBoss Security Context with the given security domain name
     *
     * @param domain the security domain name (such as "other" )
     * @return an instanceof {@code SecurityContext}
     */
    private static SecurityContext createSecurityContext(final String domain) {
        return AccessController.doPrivileged(new PrivilegedAction<SecurityContext>() {

            @Override
            public SecurityContext run() {
                try {
                    return SecurityContextFactory.createSecurityContext(domain);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    /**
     * Set the {@code SecurityContext} on the {@code SecurityContextAssociation}
     *
     * @param sc the security context
     */
    private static void setSecurityContextOnAssociation(final SecurityContext sc) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            @Override
            public Void run() {
                SecurityContextAssociation.setSecurityContext(sc);
                return null;
            }
        });
    }

    //subject will be pushed in thread local context, so directly run this action
    public void runAs(Callable<Void> action) throws Exception {
        action.call();
    }
}


/*
 * 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.jdr;

import java.util.List;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;

public final class JdrReportSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>,
        XMLElementWriter<SubsystemMarshallingContext> {

    static final JdrReportSubsystemParser INSTANCE = new JdrReportSubsystemParser();

    public static JdrReportSubsystemParser getInstance() {
        return INSTANCE;
    }

    public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
        ParseUtils.requireNoAttributes(reader);
        ParseUtils.requireNoContent(reader);

        final ModelNode subsystem = Util.createAddOperation(PathAddress.pathAddress(JdrReportExtension.SUBSYSTEM_PATH));
        list.add(subsystem);
    }

    /**
     * {@inheritDoc}
     */
    public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context)
            throws XMLStreamException {
        context.startSubsystemElement(org.jboss.as.jdr.Namespace.CURRENT.getUriString(), false);
        writer.writeEndElement();
    }

}


/*
 * 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.jdr.commands;

import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;

import org.jboss.dmr.ModelNode;

/**
 *

 * Command to call the AS 7 management system and store the output in a file.
 *

 *

 * This class' methods are meant to be chained together to describe a management call. For example:
 *

 *

 * new CallAS7("my_file").resource("foo", "bar").operation("read-resource")
 *

 *
 *

 * will return a configured CallAS7 instance that will, when executed call {@code read-resource}
 * on the {@code /foo=bar/} resource, and store the output in a file called {@code my_file.json}.
 *

 */
public class CallAS7 extends JdrCommand {

    private String operation = "read-resource";
    private LinkedList<String> resource = new LinkedList<String>();
    private Map<String, String> parameters = new HashMap<String, String>();
    private String name;

    /**
     * constructs an instance and sets the resulting file name
     *
     * @param name of the file to write results to
     */
    public CallAS7(String name) {
        this.name = name + ".json";
    }

    /**
     * sets the operation to call
     *
     * @param operation to call, defaults to {@code read-resource}
     * @return this
     */
    public CallAS7 operation(String operation) {
        this.operation = operation;
        return this;
    }

    /**
     * adds a key/value parameter pair to the call
     *
     * @param key
     * @param val
     * @return this
     */
    public CallAS7 param(String key, String val) {
        this.parameters.put(key, val);
        return this;
    }

    /**
     * appends resource parts to the resource to call
     *

     * If you want to call /foo=bar/baz=boo/, do this:
     *

     * .resource("foo", "bar", "baz", "boo")
     *

     *
     * @param parts to call
     * @return this
     */
    public CallAS7 resource(String... parts) {
        for(String part : parts ) {
            this.resource.add(part);
        }
        return this;
    }

    @Override
    public void execute() throws Exception {
        ModelNode request = new ModelNode();
        request.get("operation").set(this.operation);

        assert this.resource.size() % 2 == 0;
        while (this.resource.size() > 0) {
            request.get("address").add(this.resource.removeFirst(),
                    this.resource.removeFirst());
        }

        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            request.get(entry.getKey()).set(entry.getValue());
        }

        if (this.env.getHostControllerName() != null) {
            request.get("host").set(this.env.getHostControllerName());
        }

        if (this.env.getServerName() != null) {
            request.get("server").set(this.env.getServerName());
        }

        this.env.getZip().add(this.env.getClient().execute(request).toJSONString(true), this.name);
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright (c) 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.ejb3.component.stateful;

import static org.jboss.as.ejb3.component.stateful.StatefulComponentInstanceInterceptor.getComponentInstance;
import static org.jboss.as.ejb3.component.stateful.StatefulSessionComponentInstance.SYNC_STATE_AFTER_COMPLETE_DELAYED_COMMITTED;
import static org.jboss.as.ejb3.component.stateful.StatefulSessionComponentInstance.SYNC_STATE_AFTER_COMPLETE_DELAYED_NO_COMMIT;
import static org.jboss.as.ejb3.component.stateful.StatefulSessionComponentInstance.SYNC_STATE_INVOCATION_IN_PROGRESS;
import static org.jboss.as.ejb3.component.stateful.StatefulSessionComponentInstance.SYNC_STATE_NO_INVOCATION;
import static org.jboss.as.ejb3.logging.EjbLogger.ROOT_LOGGER;

import java.util.concurrent.atomic.AtomicInteger;

import javax.ejb.EJBException;
import javax.ejb.TransactionManagementType;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.TransactionSynchronizationRegistry;

import org.jboss.as.ee.component.Component;
import org.jboss.as.ee.component.ComponentInstanceInterceptorFactory;
import org.jboss.as.ejb3.component.interceptors.AbstractEJBInterceptor;
import org.jboss.as.ejb3.concurrency.AccessTimeoutDetails;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.tx.OwnableReentrantLock;
import org.jboss.invocation.Interceptor;
import org.jboss.invocation.InterceptorContext;
import org.jboss.invocation.InterceptorFactory;
import org.jboss.invocation.InterceptorFactoryContext;

/**
 * {@link org.jboss.invocation.Interceptor} which manages {@link Synchronization} semantics on a stateful session bean.
 *
 * @author Carlo de Wolf
 * @author Jaikiran Pai
 */
public class StatefulSessionSynchronizationInterceptor extends AbstractEJBInterceptor {

    private final boolean containerManagedTransactions;

    private static final Factory CONTAINER_MANAGED = new Factory(TransactionManagementType.CONTAINER);
    private static final Factory BEAN_MANAGED = new Factory(TransactionManagementType.BEAN);

    public static InterceptorFactory factory(final TransactionManagementType type) {
        //we need to always return the same factory instance
        //otherwise multiple synchronization interceptors may be created
        return type == TransactionManagementType.CONTAINER ? CONTAINER_MANAGED : BEAN_MANAGED;
    }

    public StatefulSessionSynchronizationInterceptor(final boolean containerManagedTransactions) {
        this.containerManagedTransactions = containerManagedTransactions;
    }

    @Override
    public Object processInvocation(final InterceptorContext context) throws Exception {
        final StatefulSessionComponent component = getComponent(context, StatefulSessionComponent.class);
        final StatefulSessionComponentInstance instance = getComponentInstance(context);

        final OwnableReentrantLock lock = instance.getLock();
        final Object threadLock = instance.getThreadLock();
        final AtomicInteger invocationSyncState = instance.getInvocationSynchState();

        final TransactionSynchronizationRegistry transactionSynchronizationRegistry = component.getTransactionSynchronizationRegistry();
        final Object lockOwner = getLockOwner(transactionSynchronizationRegistry);
        final AccessTimeoutDetails timeout = component.getAccessTimeout(context.getMethod());
        boolean toDiscard = false;
        if (ROOT_LOGGER.isTraceEnabled()) {
            ROOT_LOGGER.trace("Trying to acquire lock: " + lock + " for stateful component instance: " + instance + " during invocation: " + context);
        }
        // we obtain a lock in this synchronization interceptor because the lock needs to be tied to the synchronization
        // so that it can released on the tx synchronization callbacks
        boolean acquired = lock.tryLock(timeout.getValue(), timeout.getTimeUnit(), lockOwner);
        if (!acquired) {
            throw EjbLogger.ROOT_LOGGER.failToObtainLock(component.getComponentName(), timeout.getValue(), timeout.getTimeUnit());
        }
        synchronized (threadLock) {
            invocationSyncState.set(SYNC_STATE_INVOCATION_IN_PROGRESS); //invocation in progress
            if (ROOT_LOGGER.isTraceEnabled()) {
                ROOT_LOGGER.trace("Acquired lock: " + lock + " for stateful component instance: " + instance + " during invocation: " + context);
            }

            Object currentTransactionKey = null;
            boolean wasTxSyncRegistered = false;
            try {
                //we never register a sync for bean managed transactions
                //the inner BMT interceptor is going to setup the correct transaction anyway
                //so enrolling in an existing transaction is not correct
                if (containerManagedTransactions) {
                    if (!instance.isSynchronizationRegistered()) {
                        // get the key to current transaction associated with this thread
                        currentTransactionKey = transactionSynchronizationRegistry.getTransactionKey();
                        final int status = transactionSynchronizationRegistry.getTransactionStatus();
                        // if this SFSB instance is already associated with a different transaction, then it's an error
                        // if the thread is currently associated with a tx, then register a tx synchronization
                        if (currentTransactionKey != null && status != Status.STATUS_COMMITTED && status != Status.STATUS_ROLLEDBACK) {
                            // register a tx synchronization for this SFSB instance
                            final Synchronization statefulSessionSync = new StatefulSessionSynchronization(instance);
                            transactionSynchronizationRegistry.registerInterposedSynchronization(statefulSessionSync);
                            wasTxSyncRegistered = true;
                            if (ROOT_LOGGER.isTraceEnabled()) {
                                ROOT_LOGGER.trace("Registered tx synchronization: " + statefulSessionSync + " for tx: " + currentTransactionKey +
                                        " associated with stateful component instance: " + instance);
                            }
                            // invoke the afterBegin callback on the SFSB
                            instance.afterBegin();
                            instance.setSynchronizationRegistered(true);
                            context.putPrivateData(StatefulTransactionMarker.class, StatefulTransactionMarker.of(true));
                        }
                    } else {
                        context.putPrivateData(StatefulTransactionMarker.class, StatefulTransactionMarker.of(false));
                    }
                }
                // proceed with the invocation
                // handle exceptions to coincide with exception handling in StatefulComponentInstanceInterceptor
                try {
                    return context.proceed();
                } catch (Exception ex) {
                    if(component.shouldDiscard(ex, context.getMethod())) {
                        toDiscard = true;
                    }
                    throw ex;
                } catch (Error e) {
                    // discard bean cache state on error
                    toDiscard = true;
                    throw e;
                } catch (Throwable t) {
                    // discard bean cache state on error
                    toDiscard = true;
                    throw t;
                }
            } finally {
                // if the current call did *not* register a tx SessionSynchronization, then we have to explicitly mark the
                // SFSB instance as "no longer in use". If it registered a tx SessionSynchronization, then releasing the lock is
                // taken care off by a tx synchronization callbacks.
                // case: sync was not registered in this invocation nor in a previous one
                if (!wasTxSyncRegistered && !instance.isSynchronizationRegistered()) {
                    ROOT_LOGGER.tracef("Calling release from synchronization interceptor (#1), instance id K = %s", instance.getId());
                    releaseInstance(instance, toDiscard);
                } else if (!wasTxSyncRegistered) {
                    // case: sync was not registered in this invocation but in a previous one
                    // if we don't release the lock here then it will be acquired multiple times and only released once
                    releaseLock(instance);
                    //we also call the cache release to decrease the usage count
                    if (!instance.isDiscarded() && !toDiscard) {
                        ROOT_LOGGER.tracef("Calling release from synchronization interceptor (#2), instance id K = %s", instance.getId());
                        instance.getComponent().getCache().release(instance);
                    }
                }
                for(;;) {
                    int state = invocationSyncState.get();
                    if(state == SYNC_STATE_INVOCATION_IN_PROGRESS && invocationSyncState.compareAndSet(SYNC_STATE_INVOCATION_IN_PROGRESS, SYNC_STATE_NO_INVOCATION)) {
                        break;
                    } else if (state == SYNC_STATE_AFTER_COMPLETE_DELAYED_COMMITTED || state == SYNC_STATE_AFTER_COMPLETE_DELAYED_NO_COMMIT) {
                        try {
                            //invoke the after completion method, other after completion syncs may have already run
                            handleAfterCompletion(state == SYNC_STATE_AFTER_COMPLETE_DELAYED_COMMITTED, instance, toDiscard);
                        } finally {
                            invocationSyncState.set(SYNC_STATE_NO_INVOCATION);
                        }
                    } else {
                        EjbLogger.ROOT_LOGGER.unexpectedInvocationState(state);
                        break;
                    }
                }
            }
        }
    }

    /**
     * Use either the active transaction or the current thread as the lock owner
     *
     * @return The lock owner
     */
    private static Object getLockOwner(final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
        Object owner = transactionSynchronizationRegistry.getTransactionKey();
        return owner != null ? owner : Thread.currentThread();
    }

    /**
     * Releases the passed {@link StatefulSessionComponentInstance} i.e. marks it as no longer in use. After releasing the
     * instance, this method releases the lock, held by this thread, on the stateful component instance.
     *
     * @param instance The stateful component instance
     * @param toDiscard indicates if bean should be discarded due to exception
     */
    static void releaseInstance(final StatefulSessionComponentInstance instance, boolean toDiscard) {
        try {
            if (!instance.isDiscarded() && !toDiscard) {
                // mark the SFSB instance as no longer in use
                instance.getComponent().getCache().release(instance);
            }
        } finally {
            instance.setSynchronizationRegistered(false);
            // release the lock on the SFSB instance
            releaseLock(instance);
        }
    }

    /**
     * Releases the lock, held by this thread, on the stateful component instance.
     */
    static void releaseLock(final StatefulSessionComponentInstance instance) {
        instance.getLock().unlock(getLockOwner(instance.getComponent().getTransactionSynchronizationRegistry()));
        ROOT_LOGGER.tracef("Released lock: %s", instance.getLock());
    }

    private static class Factory extends ComponentInstanceInterceptorFactory {

        private final TransactionManagementType type;

        public Factory(final TransactionManagementType type) {
            this.type = type;
        }

        @Override
        protected Interceptor create(final Component component, final InterceptorFactoryContext context) {
            return new StatefulSessionSynchronizationInterceptor(type == TransactionManagementType.CONTAINER );
        }
    }

    private class StatefulSessionSynchronization implements Synchronization {

        private final StatefulSessionComponentInstance statefulSessionComponentInstance;

        StatefulSessionSynchronization(StatefulSessionComponentInstance statefulSessionComponentInstance) {
            this.statefulSessionComponentInstance = statefulSessionComponentInstance;
        }

        @Override
        public void beforeCompletion() {
            try {
                ROOT_LOGGER.tracef("Before completion callback invoked on Transaction synchronization: %s of stateful component instance: %s" , this, statefulSessionComponentInstance);

                if (!statefulSessionComponentInstance.isDiscarded()) {
                    statefulSessionComponentInstance.beforeCompletion();
                }
            } catch (Throwable t) {
                handleThrowable(t, statefulSessionComponentInstance);
            }
        }

        @Override
        public void afterCompletion(int status) {
            AtomicInteger state = statefulSessionComponentInstance.getInvocationSynchState();
            final boolean committed = status == Status.STATUS_COMMITTED;
            for(;;) {
                int s = state.get();
                if(s == SYNC_STATE_NO_INVOCATION) {
                    handleAfterCompletion(committed, statefulSessionComponentInstance, false);
                    break;
                } else if(s == SYNC_STATE_INVOCATION_IN_PROGRESS  && state.compareAndSet(SYNC_STATE_INVOCATION_IN_PROGRESS, committed ? SYNC_STATE_AFTER_COMPLETE_DELAYED_COMMITTED : SYNC_STATE_AFTER_COMPLETE_DELAYED_NO_COMMIT)) {
                    break;
                }
            }
        }
    }
    static void handleAfterCompletion(boolean committed, StatefulSessionComponentInstance statefulSessionComponentInstance, boolean toDiscard) {
        try {
            ROOT_LOGGER.tracef("After completion callback invoked on Transaction synchronization: %s", statefulSessionComponentInstance);

            if (!statefulSessionComponentInstance.isDiscarded() && !toDiscard) {
                statefulSessionComponentInstance.afterCompletion(committed);
            }
        } catch (Throwable t) {
            handleThrowable(t, statefulSessionComponentInstance);
        }
        if(statefulSessionComponentInstance.isRemoved() && !statefulSessionComponentInstance.isDiscarded() && !toDiscard) {
            try {
                statefulSessionComponentInstance.destroy();
            } catch (Throwable t) {
                handleThrowable(t, statefulSessionComponentInstance);
            }
        }

        // tx has completed, so mark the SFSB instance as no longer in use
        releaseInstance(statefulSessionComponentInstance, toDiscard);
    }

    private static void handleThrowable(Throwable t, StatefulSessionComponentInstance statefulSessionComponentInstance) {
        ROOT_LOGGER.discardingStatefulComponent(statefulSessionComponentInstance, t);
        try {
            // discard the SFSB instance
            statefulSessionComponentInstance.discard();
        } finally {
            // release the lock associated with the SFSB instance
            releaseLock(statefulSessionComponentInstance);
        }
        // throw back an appropriate exception
        if (t instanceof RuntimeException)
            throw (RuntimeException) t;
        if (t instanceof Error)
            throw (Error) t;
        throw (EJBException) new EJBException().initCause(t);
    }
}


/*
 * 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.jdr.util;

import org.jboss.as.jdr.commands.JdrEnvironment;
import org.jboss.vfs.VirtualFile;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;

import static org.jboss.as.jdr.logger.JdrLogger.ROOT_LOGGER;

/**
 * Abstracts the zipfile used for packaging the JDR Report.
 */
public class JdrZipFile {

    ZipOutputStream zos;
    String jbossHome;
    JdrEnvironment env;
    String name;
    String baseName;

    public JdrZipFile(JdrEnvironment env) throws Exception {
        this.env = env;
        this.jbossHome = this.env.getJbossHome();
        SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd_hh-mm-ss");
        baseName = "jdr_" + fmt.format(new Date());

        if (this.env.getHostControllerName() != null) {
            this.baseName += "." + this.env.getHostControllerName();
        }

        if (this.env.getServerName() != null) {
            this.baseName += "_" + this.env.getServerName();
        }

        this.name = this.env.getOutputDirectory() +
                java.io.File.separator +
                baseName + ".zip";

        zos = new ZipOutputStream(new FileOutputStream(this.name));
    }

    /**
     * @return the full pathname to the zipfile on disk
     */
    public String name() {
        return this.name;
    }

    /**
     * Adds the contents of the {@link InputStream} to the path in the zip.
     *
     * This method allows for absolute control of the destination of the content to be stored.
     * It is not common to use this method.
     * @param is content to write
     * @param path destination to write to in the zip file
     */
    public void add(InputStream is, String path) {
        byte [] buffer = new byte[1024];

        try {
            String entryName = this.baseName + "/" + path;
            ZipEntry ze = new ZipEntry(entryName);
            zos.putNextEntry(ze);
            int bytesRead = is.read(buffer);
            while( bytesRead > -1 ) {
                zos.write(buffer, 0, bytesRead);
                bytesRead = is.read(buffer);
            }
        }
        catch (ZipException ze) {
            ROOT_LOGGER.debugf(ze, "%s is already in the zip", path);
        }
        catch (Exception e) {
            ROOT_LOGGER.debugf(e, "Error when adding %s", path);
        }
        finally {
            try {
                zos.closeEntry();
            }
            catch (Exception e) {
                ROOT_LOGGER.debugf(e, "Error when closing entry for %s", path);
            }
        }
    }

    /**
     * Adds the content of the {@link InputStream} to the zip in a location that mirrors where {@link VirtualFile file} is located.
     *
     * For example if {@code file} is at {@code /tmp/foo/bar} and {@code $JBOSS_HOME} is {@code tmp} then the destination will be {@code JBOSSHOME/foo/bar}
     *
     * @param file {@link VirtualFile} where metadata is read from
     * @param is content to write to the zip file
     * @throws Exception
     */
    public void add(VirtualFile file, InputStream is) throws Exception {
        String name = "JBOSS_HOME" + file.getPhysicalFile().getAbsolutePath().substring(this.jbossHome.length());
        this.add(is, name);
    }

    /**
     * Adds content to the zipfile at path
     *
     * path is prepended with the directory reserved for generated text files in JDR
     *
     * @param content
     * @param path
     * @throws Exception
     */
    public void add(String content, String path) throws Exception {
        StringBuilder name = new StringBuilder("sos_strings/");

        name.append(this.env.getProductName().replace(" ", "_").toLowerCase());
        name.append("-");
        name.append(this.env.getProductVersion().split("\\.")[0]);
        name.append("/");
        name.append(path);

        this.add(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), name.toString());
    }

    /**
     * Adds content to the zipfile at path
     *
     * path is prepended with the directory reserved for generated text files in JDR
     *
     * @param stream
     * @param path
     * @throws Exception
     */
    public void addAsString(InputStream stream, String path) throws Exception {
        StringBuilder name = new StringBuilder("sos_strings/");

        name.append(this.env.getProductName().replace(" ", "_").toLowerCase());
        name.append("-");
        name.append(this.env.getProductVersion().split("\\.")[0]);
        name.append("/");
        name.append(path);

        this.add(stream, name.toString());
    }

    /**
     * Adds content to the zipfile in a file named logName
     *
     * path is prepended with the directory reserved for JDR log files
     *
     * @param content
     * @param logName
     * @throws Exception
     */
    public void addLog(String content, String logName) throws Exception {
        String name = "sos_logs/" + logName;
        this.add(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), name);
    }

    public void close() throws Exception {
        this.zos.close();
    }
}


/*
 * 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.iiop.openjdk.service;

import static java.security.AccessController.doPrivileged;

import org.wildfly.security.manager.WildFlySecurityManager;
import org.wildfly.security.manager.action.CreateThreadAction;

/**
 *

 * This class defines actions that must be executed in privileged blocks.
 *

 *
 * @author Stefan Guilhen
 */
class SecurityActions {

    /**
     *

     * Creates a thread with the specified {@code Runnable} and name.
     *

     *
     * @param runnable   the {@code Runnable} to be set in the new {@code Thread}.
     * @param threadName the name of the new {@code Thread}.
     * @return the construct {@code Thread} instance.
     */
    static Thread createThread(final Runnable runnable, final String threadName) {
        return ! WildFlySecurityManager.isChecking() ? new Thread(runnable, threadName) : doPrivileged(new CreateThreadAction(runnable, threadName));
    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2012, 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.jsf.deployment;

import static org.jboss.as.weld.Capabilities.WELD_CAPABILITY_NAME;

import java.util.ArrayList;
import java.util.List;

import org.jboss.as.controller.capability.CapabilityServiceSupport;
import org.jboss.as.ee.structure.DeploymentType;
import org.jboss.as.ee.structure.DeploymentTypeMarker;
import org.jboss.as.jsf.logging.JSFLogger;
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.module.ModuleDependency;
import org.jboss.as.server.deployment.module.ModuleSpecification;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.as.weld.WeldCapability;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.filter.PathFilters;

/**
 * @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc.
 * @author Stuart Douglas
 */
public class JSFDependencyProcessor implements DeploymentUnitProcessor {
    public static final String IS_CDI_PARAM = "org.jboss.jbossfaces.IS_CDI";

    private static final ModuleIdentifier JSF_SUBSYSTEM = ModuleIdentifier.create("org.jboss.as.jsf");

    private JSFModuleIdFactory moduleIdFactory = JSFModuleIdFactory.getInstance();

    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();

        final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
        final ModuleLoader moduleLoader = Module.getBootModuleLoader();
        if(JsfVersionMarker.isJsfDisabled(deploymentUnit)) {
            addJSFAPI(JsfVersionMarker.JSF_2_0, moduleSpecification, moduleLoader);
            return;
        }
        if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit) && !DeploymentTypeMarker.isType(DeploymentType.EAR, deploymentUnit)) {
            return;
        }
        String jsfVersion = JsfVersionMarker.getVersion(deploymentUnit);
        if (jsfVersion.equals(JsfVersionMarker.WAR_BUNDLES_JSF_IMPL)) {
            //if JSF is provided by the application we leave it alone
            return;
        }
        //TODO: we should do that same check that is done in com.sun.faces.config.FacesInitializer
        //and only add the dependency if JSF is actually needed

        String defaultJsfVersion = JSFModuleIdFactory.getInstance().getDefaultSlot();
        if (!moduleIdFactory.isValidJSFSlot(jsfVersion)) {
            JSFLogger.ROOT_LOGGER.unknownJSFVersion(jsfVersion, defaultJsfVersion);
            jsfVersion = defaultJsfVersion;
        }

        if (jsfVersion.equals(defaultJsfVersion) && !moduleIdFactory.isValidJSFSlot(jsfVersion)) {
            throw JSFLogger.ROOT_LOGGER.invalidDefaultJSFImpl(defaultJsfVersion);
        }

        addJSFAPI(jsfVersion, moduleSpecification, moduleLoader);
        addJSFImpl(jsfVersion, moduleSpecification, moduleLoader);

        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, JSF_SUBSYSTEM, false, false, true, false));

        addJSFInjection(jsfVersion, moduleSpecification, moduleLoader);

        WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
        if(warMetaData != null) {
            addCDIFlag(warMetaData, deploymentUnit);
        }
    }

    @Override
    public void undeploy(DeploymentUnit context) {
    }

    private void addJSFAPI(String jsfVersion, ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
        if (jsfVersion.equals(JsfVersionMarker.WAR_BUNDLES_JSF_IMPL)) return;

        ModuleIdentifier jsfModule = moduleIdFactory.getApiModId(jsfVersion);
        ModuleDependency jsfAPI = new ModuleDependency(moduleLoader, jsfModule, false, false, false, false);
        moduleSpecification.addSystemDependency(jsfAPI);
    }

    // Is JSF spec greater than 1.1?  If we add JSF 1.1 support we'll need this to keep from calling addJSFInjection()
  /*  private boolean isJSFSpecOver1_1(ModuleIdentifier jsfModule, ModuleDependency jsfAPI) throws DeploymentUnitProcessingException {
        try {
            return (jsfAPI.getModuleLoader().loadModule(jsfModule).getClassLoader().getResource("/javax/faces/component/ActionSource2.class") != null);
        } catch (ModuleLoadException e) {
            throw new DeploymentUnitProcessingException(e);
        }
    } */

    private void addJSFImpl(String jsfVersion,
            ModuleSpecification moduleSpecification,
            ModuleLoader moduleLoader) {
        if (jsfVersion.equals(JsfVersionMarker.WAR_BUNDLES_JSF_IMPL)) return;

        ModuleIdentifier jsfModule = moduleIdFactory.getImplModId(jsfVersion);
        ModuleDependency jsfImpl = new ModuleDependency(moduleLoader, jsfModule, false, false, true, false);
        jsfImpl.addImportFilter(PathFilters.getMetaInfFilter(), true);
        moduleSpecification.addSystemDependency(jsfImpl);
    }

    private void addJSFInjection(String jsfVersion, ModuleSpecification moduleSpecification, ModuleLoader moduleLoader)
            throws DeploymentUnitProcessingException {
        if (jsfVersion.equals(JsfVersionMarker.WAR_BUNDLES_JSF_IMPL)) return;

        ModuleIdentifier jsfInjectionModule = moduleIdFactory.getInjectionModId(jsfVersion);
        ModuleDependency jsfInjectionDependency = new ModuleDependency(moduleLoader, jsfInjectionModule, false, true, true, false);

        try {
            if (isJSF12(jsfInjectionDependency, jsfInjectionModule.toString())) {
                JSFLogger.ROOT_LOGGER.loadingJsf12();
                jsfInjectionDependency.addImportFilter(PathFilters.is("META-INF/faces-config.xml"), false);
                jsfInjectionDependency.addImportFilter(PathFilters.is("META-INF/1.2/faces-config.xml"), true);
            } else {
                JSFLogger.ROOT_LOGGER.loadingJsf2x();
                jsfInjectionDependency.addImportFilter(PathFilters.getMetaInfFilter(), true);
                // Exclude JSF 1.2 faces-config.xml to make extra sure it won't interfere with JSF 2.0 deployments
                jsfInjectionDependency.addImportFilter(PathFilters.is("META-INF/1.2/faces-config.xml"), false);
            }
        } catch (ModuleLoadException e) {
            throw JSFLogger.ROOT_LOGGER.jsfInjectionFailed(jsfVersion);
        }

        moduleSpecification.addSystemDependency(jsfInjectionDependency);
    }

    private boolean isJSF12(ModuleDependency moduleDependency, String identifier) throws ModuleLoadException {

        // The class javax.faces.event.NamedEvent was introduced in JSF 2.0
        return (moduleDependency.getModuleLoader().loadModule(identifier)
                                .getClassLoader().getResource("/javax/faces/event/NamedEvent.class") == null);
    }

    // Add a flag to the sevlet context so that we know if we need to instantiate
    // a CDI ViewHandler.
    private void addCDIFlag(WarMetaData warMetaData, DeploymentUnit deploymentUnit) {
        JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
        if (webMetaData == null) {
            webMetaData = new JBossWebMetaData();
            warMetaData.setMergedJBossWebMetaData(webMetaData);
        }

        List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
        if (contextParams == null) {
            contextParams = new ArrayList<ParamValueMetaData>();
        }

        boolean isCDI = false;
        final CapabilityServiceSupport support = deploymentUnit.getAttachment(Attachments.CAPABILITY_SERVICE_SUPPORT);
        if (support.hasCapability(WELD_CAPABILITY_NAME)) {
            isCDI = support.getOptionalCapabilityRuntimeAPI(WELD_CAPABILITY_NAME, WeldCapability.class).get()
                    .isPartOfWeldDeployment(deploymentUnit);
        }

        ParamValueMetaData param = new ParamValueMetaData();
        param.setParamName(IS_CDI_PARAM);
        param.setParamValue(Boolean.toString(isCDI));
        contextParams.add(param);

        webMetaData.setContextParams(contextParams);
    }
}


/*
 * 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.webservices.invocation;

import static org.jboss.as.webservices.metadata.model.AbstractEndpoint.COMPONENT_VIEW_NAME;
import static org.jboss.as.webservices.util.ASHelper.getMSCService;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collection;
import java.util.concurrent.Callable;

import javax.management.MBeanException;
import javax.xml.ws.soap.SOAPFaultException;

import org.jboss.as.ee.component.Component;
import org.jboss.as.ee.component.ComponentView;
import org.jboss.as.naming.ManagedReference;
import org.jboss.as.webservices.injection.WSComponent;
import org.jboss.as.webservices.logging.WSLogger;
import org.jboss.invocation.InterceptorContext;
import org.jboss.msc.service.ServiceName;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jboss.wsf.spi.deployment.EndpointState;
import org.jboss.wsf.spi.invocation.Invocation;
import org.jboss.wsf.spi.security.SecurityDomainContext;
import org.wildfly.transaction.client.ContextTransactionManager;
import org.wildfly.transaction.client.LocalTransactionContext;

/**
 * Invocation abstraction for all endpoint types
 *
 * @author Richard Opalka
 * @author Jim Ma
 */
abstract class AbstractInvocationHandler extends org.jboss.ws.common.invocation.AbstractInvocationHandler {

   private volatile ServiceName componentViewName;
   private volatile ComponentView componentView;
   protected volatile ManagedReference reference;

   /**
    * Initializes component view name.
    *
    * @param endpoint web service endpoint
    */
   public void init(final Endpoint endpoint) {
       componentViewName = (ServiceName) endpoint.getProperty(COMPONENT_VIEW_NAME);
   }

    /**
     * Gets endpoint container lazily.
     *
     * @return endpoint container
     */
    protected ComponentView getComponentView() {
        ComponentView cv = componentView;
        // we need to check both, otherwise it is possible for
        // componentView to be initialized before reference
        if (cv == null) {
            synchronized (this) {
                cv = componentView;
                if (cv == null) {
                    cv = getMSCService(componentViewName, ComponentView.class);
                    if (cv == null) {
                        throw WSLogger.ROOT_LOGGER.cannotFindComponentView(componentViewName);
                    }
                    if (reference == null) {
                        try {
                            reference = cv.createInstance();
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                    componentView = cv;
                }
            }
        }
        return cv;
    }

   /**
    * Invokes WS endpoint.
    *
    * @param endpoint WS endpoint
    * @param wsInvocation web service invocation
    * @throws Exception if any error occurs
    */
    public void invoke(final Endpoint endpoint, final Invocation wsInvocation) throws Exception {
        try {
            if (!EndpointState.STARTED.equals(endpoint.getState())) {
                throw WSLogger.ROOT_LOGGER.endpointAlreadyStopped(endpoint.getShortName());
            }
            SecurityDomainContext securityDomainContext = endpoint.getSecurityDomainContext();
            securityDomainContext.runAs((Callable<Void>) () -> {
                invokeInternal(endpoint, wsInvocation);
                return null;
            });
        } catch (Throwable t) {
            handleInvocationException(t);
        } finally {
            onAfterInvocation(wsInvocation);
        }
    }

    public void invokeInternal(final Endpoint endpoint, final Invocation wsInvocation) throws Exception {
        // prepare for invocation
        onBeforeInvocation(wsInvocation);
        // prepare invocation data
        final ComponentView componentView = getComponentView();
        Component component = componentView.getComponent();
        // in case of @FactoryType annotation we don't need to go into EE interceptors
        final boolean forceTargetBean = (wsInvocation.getInvocationContext().getProperty("forceTargetBean") != null);
        if (forceTargetBean) {
            this.reference = new ManagedReference() {
                public void release() {
                }

                public Object getInstance() {
                    return wsInvocation.getInvocationContext().getTargetBean();
                }
            };
            if (component instanceof WSComponent) {
                ((WSComponent) component).setReference(reference);
            }
        }
        final Method method = getComponentViewMethod(wsInvocation.getJavaMethod(), componentView.getViewMethods());
        final InterceptorContext context = new InterceptorContext();
        prepareForInvocation(context, wsInvocation);
        context.setMethod(method);
        context.setParameters(wsInvocation.getArgs());
        context.putPrivateData(Component.class, component);
        context.putPrivateData(ComponentView.class, componentView);
        // pull in any XTS transaction
        LocalTransactionContext.getCurrent().importProviderTransaction();
        context.setTransaction(ContextTransactionManager.getInstance().getTransaction());
        if (forceTargetBean) {
            context.putPrivateData(ManagedReference.class, reference);
        }
        // invoke method
        final Object retObj = componentView.invoke(context);
        // set return value
        wsInvocation.setReturnValue(retObj);
    }

   protected void prepareForInvocation(final InterceptorContext context, final Invocation wsInvocation) {
      // does nothing
   }

   /**
    * Translates SEI method to component view method.
    *
    * @param seiMethod SEI method
    * @param viewMethods component view methods
    * @return matching component view method
    */
   protected Method getComponentViewMethod(final Method seiMethod, final Collection<Method> viewMethods) {
       for (final Method viewMethod : viewMethods) {
           if (matches(seiMethod, viewMethod)) {
               return viewMethod;
           }
       }
       throw new IllegalStateException();
   }

   protected void handleInvocationException(final Throwable t) throws Exception {
      if (t instanceof MBeanException) {
         throw ((MBeanException) t).getTargetException();
      }
      if (t instanceof Exception) {
         if (t instanceof InvocationTargetException) {
            throw (Exception) t;
         } else {
            SOAPFaultException ex = findSoapFaultException(t);
            if (ex != null) {
               throw new InvocationTargetException(ex);
            }
            throw new InvocationTargetException(t);
         }
      }
      if (t instanceof Error) {
         throw (Error) t;
      }
      throw new UndeclaredThrowableException(t);
    }

    protected SOAPFaultException findSoapFaultException(Throwable ex) {
        if (ex instanceof SOAPFaultException) {
            return (SOAPFaultException) ex;
        }
        if (ex.getCause() != null) {
            return findSoapFaultException(ex.getCause());
        }
        return null;
    }

   /**
    * Compares two methods if they are identical.
    *
    * @param seiMethod reference method
    * @param viewMethod target method
    * @return true if they match, false otherwise
    */
   private boolean matches(final Method seiMethod, final Method viewMethod) {
       if (!seiMethod.getName().equals(viewMethod.getName())) return false;
       final Class<?>[] sourceParams = seiMethod.getParameterTypes();
       final Class<?>[] targetParams = viewMethod.getParameterTypes();
       if (sourceParams.length != targetParams.length) return false;
       for (int i = 0; i < sourceParams.length; i++) {
           if (!sourceParams[i].equals(targetParams[i])) return false;
       }
       return true;
   }

}


/*
 * 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.jpa.service;

import static org.jboss.as.jpa.messages.JpaLogger.ROOT_LOGGER;

import java.util.HashSet;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.jpa.config.ExtendedPersistenceInheritance;
import org.jboss.as.jpa.management.DynamicManagementStatisticsResource;
import org.jboss.as.jpa.management.EntityManagerFactoryLookup;
import org.jboss.as.jpa.management.ManagementResourceDefinition;
import org.jboss.as.jpa.processor.CacheDeploymentHelper;
import org.jboss.as.jpa.processor.PersistenceUnitServiceHandler;
import org.jboss.as.jpa.subsystem.JPAExtension;
import org.jboss.as.jpa.util.JPAServiceNames;
import org.jboss.as.server.deployment.DeploymentModelUtils;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jipijapa.management.spi.Statistics;
import org.jipijapa.plugin.spi.ManagementAdaptor;

/**
 * represents the global JPA Service
 *
 * @author Scott Marlow
 */
public class JPAService implements Service<Void> {

    public static final ServiceName SERVICE_NAME = JPAServiceNames.getJPAServiceName();

    private static volatile String defaultDataSourceName = null;
    private static volatile ExtendedPersistenceInheritance defaultExtendedPersistenceInheritance = null;
    private static final Set<String> existingResourceDescriptionResolver = new HashSet<>();
    private final CacheDeploymentHelper cacheDeploymentHelper = new CacheDeploymentHelper();

    public static String getDefaultDataSourceName() {
        ROOT_LOGGER.tracef("JPAService.getDefaultDataSourceName() == %s", JPAService.defaultDataSourceName);
        return defaultDataSourceName;
    }

    public static void setDefaultDataSourceName(String dataSourceName) {
        ROOT_LOGGER.tracef("JPAService.setDefaultDataSourceName(%s), previous value = %s", dataSourceName, JPAService.defaultDataSourceName);
        defaultDataSourceName = dataSourceName;
    }

    public static ExtendedPersistenceInheritance getDefaultExtendedPersistenceInheritance() {
        ROOT_LOGGER.tracef("JPAService.getDefaultExtendedPersistenceInheritance() == %s", defaultExtendedPersistenceInheritance.toString());
        return defaultExtendedPersistenceInheritance;
    }

    public static void setDefaultExtendedPersistenceInheritance(ExtendedPersistenceInheritance defaultExtendedPersistenceInheritance) {
        ROOT_LOGGER.tracef("JPAService.setDefaultExtendedPersistenceInheritance(%s)", defaultExtendedPersistenceInheritance.toString());
        JPAService.defaultExtendedPersistenceInheritance = defaultExtendedPersistenceInheritance;
    }

    public static void addService(
            final ServiceTarget target,
            final String defaultDataSourceName,
            final ExtendedPersistenceInheritance defaultExtendedPersistenceInheritance) {
        JPAService jpaService = new JPAService();
        setDefaultDataSourceName(defaultDataSourceName);
        setDefaultExtendedPersistenceInheritance(defaultExtendedPersistenceInheritance);
        final ServiceBuilder sb = target.addService(SERVICE_NAME, jpaService);
        sb.setInitialMode(ServiceController.Mode.ACTIVE);
        sb.requires(JPAUserTransactionListenerService.SERVICE_NAME);
        sb.install();
    }

    /**
     * Create single instance of management statistics resource per managementAdaptor version.
     *
     * ManagementAccess
     *
     * The persistence provider and jipijapa adapters will be in the same classloader,
     * either a static module or included directly in the application.  Those are the two supported use
     * cases for management of deployment persistence units also.
     *
     * From a management point of view, the requirements are:
     *   1.  show management statistics for static persistence provider modules and applications that have
     *       their own persistence provider module.
     *
     *   2.  persistence provider adapters will provide a unique key that identifies the management version of supported
     *       management statistics/operations.  For example, Hibernate 3.x might be 1.0, Hibernate 4.1/4.2 might
     *       be version 2.0 and Hibernate 4.3 could be 2.0 also as long as its compatible (same stats) with 4.1/4.2.
     *       Eventually, a Hibernate (later version) change in statistics is likely to happen, the management version
     *       will be incremented.
     *
     *
     * @param managementAdaptor the management adaptor that will provide statistics
     * @param scopedPersistenceUnitName name of the persistence unit
     * @param deploymentUnit deployment unit for the deployment requesting a resource
     * @return the management resource
     */
    public static Resource createManagementStatisticsResource(
            final ManagementAdaptor managementAdaptor,
            final String scopedPersistenceUnitName,
            final DeploymentUnit deploymentUnit) {

        synchronized (existingResourceDescriptionResolver) {
            final EntityManagerFactoryLookup entityManagerFactoryLookup = new EntityManagerFactoryLookup();
            final Statistics statistics = managementAdaptor.getStatistics();

            if (false == existingResourceDescriptionResolver.contains(managementAdaptor.getVersion())) {

                // setup statistics (this used to be part of JPA subsystem startup)
                ResourceDescriptionResolver resourceDescriptionResolver = new StandardResourceDescriptionResolver(
                        statistics.getResourceBundleKeyPrefix(), statistics.getResourceBundleName(), statistics.getClass().getClassLoader()){
                    private ResourceDescriptionResolver fallback = JPAExtension.getResourceDescriptionResolver();
                    //add a fallback in case provider doesn't have all properties properly defined
                    @Override
                    public String getResourceAttributeDescription(String attributeName, Locale locale, ResourceBundle bundle) {
                        if (bundle.containsKey(getBundleKey(attributeName))) {
                            return super.getResourceAttributeDescription(attributeName, locale, bundle);
                        }else{
                            return fallback.getResourceAttributeDescription(attributeName, locale, fallback.getResourceBundle(locale));
                        }
                    }
                };

                PathElement subsystemPE = PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, JPAExtension.SUBSYSTEM_NAME);
                ManagementResourceRegistration deploymentResourceRegistration = deploymentUnit.getAttachment(DeploymentModelUtils.MUTABLE_REGISTRATION_ATTACHMENT);
                ManagementResourceRegistration deploymentSubsystemRegistration =
                        deploymentResourceRegistration.getSubModel(PathAddress.pathAddress(subsystemPE));
                ManagementResourceRegistration subdeploymentSubsystemRegistration =
                        deploymentResourceRegistration.getSubModel(PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBDEPLOYMENT), subsystemPE));

                ManagementResourceRegistration providerResource = deploymentSubsystemRegistration.registerSubModel(
                        new ManagementResourceDefinition(PathElement.pathElement(managementAdaptor.getIdentificationLabel()), resourceDescriptionResolver, statistics, entityManagerFactoryLookup));
                providerResource.registerReadOnlyAttribute(PersistenceUnitServiceHandler.SCOPED_UNIT_NAME, null);

                providerResource = subdeploymentSubsystemRegistration.registerSubModel(
                        new ManagementResourceDefinition(PathElement.pathElement(managementAdaptor.getIdentificationLabel()), resourceDescriptionResolver, statistics, entityManagerFactoryLookup));
                providerResource.registerReadOnlyAttribute(PersistenceUnitServiceHandler.SCOPED_UNIT_NAME, null);

                existingResourceDescriptionResolver.add(managementAdaptor.getVersion());
            }
            // create (per deployment) dynamic Resource implementation that can reflect the deployment specific names (e.g. jpa entity classname/Hibernate region name)
            return new DynamicManagementStatisticsResource(statistics, scopedPersistenceUnitName, managementAdaptor.getIdentificationLabel(), entityManagerFactoryLookup);
        }
    }

    @Override
    public void start(StartContext startContext) throws StartException {
        cacheDeploymentHelper.register();
    }

    @Override
    public void stop(StopContext stopContext) {
        cacheDeploymentHelper.unregister();
        synchronized (existingResourceDescriptionResolver) {
            existingResourceDescriptionResolver.clear();
        }
    }

    @Override
    public Void getValue() throws IllegalStateException, IllegalArgumentException {
        return null;
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, 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.txn.subsystem;

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.duplicateNamedElement;
import static org.jboss.as.controller.parsing.ParseUtils.missingOneOf;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequired;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequiredElement;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoContent;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoNamespaceAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedElement;

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

import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.txn.logging.TransactionLogger;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLExtendedStreamReader;

/**
 */
class TransactionSubsystem11Parser implements XMLStreamConstants, XMLElementReader<List<ModelNode>> {

    TransactionSubsystem11Parser() {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readElement(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
        // no attributes
        if (reader.getAttributeCount() > 0) {
            throw unexpectedAttribute(reader, 0);
        }

        final ModelNode address = new ModelNode();
        address.add(ModelDescriptionConstants.SUBSYSTEM, TransactionExtension.SUBSYSTEM_NAME);
        address.protect();

        final ModelNode subsystem = new ModelNode();
        subsystem.get(OP).set(ADD);
        subsystem.get(OP_ADDR).set(address);

        list.add(subsystem);

        // elements
        final EnumSet<Element> required = EnumSet.of(Element.RECOVERY_ENVIRONMENT, Element.CORE_ENVIRONMENT);
        final EnumSet<Element> encountered = EnumSet.noneOf(Element.class);
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            switch (Namespace.forUri(reader.getNamespaceURI())) {
                case TRANSACTIONS_1_1: {
                    final Element element = Element.forName(reader.getLocalName());
                    required.remove(element);
                    if (!encountered.add(element)) {
                        throw unexpectedElement(reader);
                    }
                    switch (element) {
                        case RECOVERY_ENVIRONMENT: {
                            parseRecoveryEnvironmentElement(reader, subsystem);
                            break;
                        }
                        case CORE_ENVIRONMENT: {
                            parseCoreEnvironmentElement(reader, subsystem);
                            break;
                        }
                        case COORDINATOR_ENVIRONMENT: {
                            parseCoordinatorEnvironmentElement(reader, subsystem);
                            break;
                        }
                        case OBJECT_STORE: {
                            parseObjectStoreEnvironmentElementAndEnrichOperation(reader, subsystem);
                            break;
                        }
                        case JTS: {
                            parseJts(reader, subsystem);
                            break;
                        }
                        default: {
                            throw unexpectedElement(reader);
                        }
                    }
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
        if (!required.isEmpty()) {
            throw missingRequiredElement(reader, required);
        }
        final ModelNode logStoreAddress = address.clone();
        final ModelNode operation = new ModelNode();
        operation.get(OP).set(ADD);
        logStoreAddress.add(LogStoreConstants.LOG_STORE, LogStoreConstants.LOG_STORE);

        logStoreAddress.protect();

        operation.get(OP_ADDR).set(logStoreAddress);
        list.add(operation);

    }

    private void parseJts(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {
        // no attributes
        if (reader.getAttributeCount() > 0) {
            throw unexpectedAttribute(reader, 0);
        }
        operation.get(CommonAttributes.JTS).set(true);
        requireNoContent(reader);
    }

    static void parseObjectStoreEnvironmentElementAndEnrichOperation(final XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            switch (attribute) {
                case RELATIVE_TO:
                    TransactionSubsystemRootResourceDefinition.OBJECT_STORE_RELATIVE_TO.parseAndSetParameter(value, operation, reader);
                    break;
                case PATH:
                    TransactionSubsystemRootResourceDefinition.OBJECT_STORE_PATH.parseAndSetParameter(value, operation, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        // Handle elements
        requireNoContent(reader);

    }

    static void parseCoordinatorEnvironmentElement(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            switch (attribute) {
                case ENABLE_STATISTICS:
                    TransactionSubsystemRootResourceDefinition.ENABLE_STATISTICS.parseAndSetParameter(value, operation, reader);
                    break;
                case ENABLE_TSM_STATUS:
                    TransactionSubsystemRootResourceDefinition.ENABLE_TSM_STATUS.parseAndSetParameter(value, operation, reader);
                    break;
                case DEFAULT_TIMEOUT:
                    TransactionSubsystemRootResourceDefinition.DEFAULT_TIMEOUT.parseAndSetParameter(value, operation, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        // Handle elements
        requireNoContent(reader);

    }

    /**
     * Handle the core-environment element and children
     *
     * @param reader the stream reader
     * @param operation ModelNode for the core-environment add operation
     * @throws javax.xml.stream.XMLStreamException
     *
     */
    static void parseCoreEnvironmentElement(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            switch (attribute) {
                case NODE_IDENTIFIER:
                    TransactionSubsystemRootResourceDefinition.NODE_IDENTIFIER.parseAndSetParameter(value, operation, reader);
                    break;
                case PATH:
                case RELATIVE_TO:
                    throw TransactionLogger.ROOT_LOGGER.unsupportedAttribute(attribute.getLocalName(), reader.getLocation());
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        // elements
        final EnumSet<Element> required = EnumSet.of(Element.PROCESS_ID);
        final EnumSet<Element> encountered = EnumSet.noneOf(Element.class);
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            required.remove(element);
            switch (element) {
                case PROCESS_ID: {
                    if (!encountered.add(element)) {
                        throw duplicateNamedElement(reader, reader.getLocalName());
                    }
                    parseProcessIdEnvironmentElement(reader, operation);
                    break;
                }
                default:
                    throw unexpectedElement(reader);
            }
        }
        if (!required.isEmpty()) {
            throw missingRequiredElement(reader, required);
        }
    }

    /**
     * Handle the process-id child elements
     *
     * @param reader  the stream reader
     * @param coreEnvironmentAdd the add operation model node
     *
     * @throws javax.xml.stream.XMLStreamException
     *
     */
    static void parseProcessIdEnvironmentElement(XMLExtendedStreamReader reader, ModelNode coreEnvironmentAdd) throws XMLStreamException {
        // no attributes
        if (reader.getAttributeCount() > 0) {
            throw unexpectedAttribute(reader, 0);
        }

        // elements
        boolean encountered = false;
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            switch (element) {
                case UUID:
                    if (encountered) {
                        throw unexpectedElement(reader);
                    }
                    encountered = true;
                    if (reader.getAttributeCount() > 0) {
                        throw unexpectedAttribute(reader, 0);
                    }
                    coreEnvironmentAdd.get(TransactionSubsystemRootResourceDefinition.PROCESS_ID_UUID.getName()).set(true);
                    requireNoContent(reader);
                    break;
                case SOCKET: {
                    if (encountered) {
                        throw unexpectedElement(reader);
                    }
                    encountered = true;
                    parseSocketProcessIdElement(reader, coreEnvironmentAdd);
                    break;
                }
                default:
                    throw unexpectedElement(reader);
            }
        }

        if (!encountered) {
            throw missingOneOf(reader, EnumSet.of(Element.UUID, Element.SOCKET));
        }
    }

    static void parseSocketProcessIdElement(XMLExtendedStreamReader reader, ModelNode coreEnvironmentAdd) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        final EnumSet<Attribute> required = EnumSet.of(Attribute.BINDING);
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            required.remove(attribute);
            switch (attribute) {
                case BINDING:
                    TransactionSubsystemRootResourceDefinition.PROCESS_ID_SOCKET_BINDING.parseAndSetParameter(value, coreEnvironmentAdd, reader);
                    break;
                case SOCKET_PROCESS_ID_MAX_PORTS:
                    TransactionSubsystemRootResourceDefinition.PROCESS_ID_SOCKET_MAX_PORTS.parseAndSetParameter(value, coreEnvironmentAdd, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        if (!required.isEmpty()) {
            throw missingRequired(reader, required);
        }
        // Handle elements
        requireNoContent(reader);
    }

    static void parseRecoveryEnvironmentElement(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {

        Set<Attribute> required = EnumSet.of(Attribute.BINDING, Attribute.STATUS_BINDING);
        final int count = reader.getAttributeCount();
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            required.remove(attribute);
            switch (attribute) {
                case BINDING:
                    TransactionSubsystemRootResourceDefinition.BINDING.parseAndSetParameter(value, operation, reader);
                    break;
                case STATUS_BINDING:
                    TransactionSubsystemRootResourceDefinition.STATUS_BINDING.parseAndSetParameter(value, operation, reader);
                    break;
                case RECOVERY_LISTENER:
                    TransactionSubsystemRootResourceDefinition.RECOVERY_LISTENER.parseAndSetParameter(value, operation, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }

        if (!required.isEmpty()) {
            throw missingRequired(reader, required);
        }
        // Handle elements
        requireNoContent(reader);

    }
}