Package com.sun.jaspic.config.factory

Source Code of com.sun.jaspic.config.factory.BaseAuthConfigFactory

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package com.sun.jaspic.config.factory;

import com.sun.jaspic.config.helper.JASPICLogManager;
import java.lang.reflect.Constructor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.message.config.AuthConfigFactory;
import javax.security.auth.message.config.AuthConfigProvider;
import javax.security.auth.message.config.RegistrationListener;


/**
* This class implements methods in the abstract class AuthConfigFactory.
* @author  Shing Wai Chan
*/
public abstract class BaseAuthConfigFactory extends AuthConfigFactory {

    private static final Logger logger =
            Logger.getLogger(JASPICLogManager.JASPIC_LOGGER, JASPICLogManager.RES_BUNDLE);


    private static final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
    public static final Lock rLock = rwLock.readLock();
    public static final Lock wLock = rwLock.writeLock();

    private static Map<String, AuthConfigProvider> id2ProviderMap;
    private static Map<String, RegistrationContext> id2RegisContextMap;
    private static Map<String, List<RegistrationListener>> id2RegisListenersMap;
    private static Map<AuthConfigProvider, List<String>> provider2IdsMap;

    protected static final String CONF_FILE_NAME = "auth.conf";

    abstract protected RegStoreFileParser getRegStore();

    /**
     * Get a registered AuthConfigProvider from the factory.
     *
     * Get the provider of ServerAuthConfig and/or
     * ClientAuthConfig objects registered for the identified message
     * layer and application context.
     *
     * @param layer a String identifying the message layer
     *    for which the registered AuthConfigProvider is
     *          to be returned. This argument may be null.
     *
     * @param appContext a String that identifies the application messaging
     *          context for which the registered AuthConfigProvider
     *          is to be returned. This argument may be null.
     *
     * @param listener the RegistrationListener whose
     *          <code>notify</code> method is to be invoked
     *          if the corresponding registration is unregistered or
     *          replaced. The value of this argument may be null.
     *
     * @return the implementation of the AuthConfigProvider interface
     *          registered at the factory for the layer and appContext
     *          or null if no AuthConfigProvider is selected.
     *
     * <p>All factories shall employ the following precedence rules to select
     * the registered AuthConfigProvider that matches (via matchConstructors) the
     * layer and appContext arguments:
     *<ul>
     * <li> The provider that is specifically registered for both the
     * corresponding message layer and appContext
     * shall be selected.
     * <li> if no provider is selected according to the preceding rule,
     * the provider specifically registered for the
     * corresponding appContext and for all message layers
     * shall be selected.
     * <li> if no provider is selected according to the preceding rules,
     * the provider specifically registered for the
     * corresponding message layer and for all appContexts
     * shall be selected.
     * <li> if no provider is selected according to the preceding rules,
     * the provider registered for all message layers and for all
     * appContexts shall be selected.
     * <li> if no provider is selected according to the preceding rules,
     * the factory shall terminate its search for a registered provider.
     *</ul>
     */
    @Override
    public AuthConfigProvider
            getConfigProvider(String layer, String appContext,
      RegistrationListener listener) {
        AuthConfigProvider provider = null;
        if (listener == null) {
            rLock.lock();
            try {
                provider = getConfigProviderUnderLock(layer,appContext,null);
            } finally {
                rLock.unlock();
            }
        } else {
            wLock.lock();
            try {
                provider = getConfigProviderUnderLock(layer,appContext,listener);
            } finally {
                wLock.unlock();
            }
        }
        return provider;
    }

    /**
     * Registers within the factory, a provider
     * of ServerAuthConfig and/or ClientAuthConfig objects for a
     * message layer and application context identifier.
     *
     * <P>At most one registration may exist within the factory for a
     * given combination of message layer
     * and appContext. Any pre-existing
     * registration with identical values for layer and appContext is replaced
     * by a subsequent registration. When replacement occurs, the registration
     * identifier, layer, and appContext identifier remain unchanged,
     * and the AuthConfigProvider (with initialization properties) and
     * description are replaced.
     *
     *<p>Within the lifetime of its Java process, a factory must assign unique
     * registration identifiers to registrations, and must never
     * assign a previously used registration identifier to a registration
     * whose message layer and or appContext identifier differ from
     * the previous use.
     *
     * <p>Programmatic registrations performed via this method must update
     * (according to the replacement rules described above), the persistent
     * declarative representation of provider registrations employed by the
     * factory constructor.
     *
     * @param className the fully qualified name of an AuthConfigProvider
     *          implementation class. This argument must not be null.
     *
     * @param properties a Map object containing the initialization
     *          properties to be passed to the provider constructor.
     *          This argument may be null. When this argument is not null,
     *          all the values and keys occuring in the Map must be of
     *          type String.
     *
     * @param layer a String identifying the message layer
     *    for which the provider will be registered at the factory.
     *          A null value may be passed as an argument for this parameter,
     *          in which case, the provider is registered at all layers.
     *
     * @param appContext a String value that may be used by a runtime
     *          to request a configuration object from this provider.
     *          A null value may be passed as an argument for this parameter,
     *          in which case, the provider is registered for all
     *          configuration ids (at the indicated layers).
     *
     * @param description a text String describing the provider.
     *          this value may be null.
     *
     * @return a String identifier assigned by
     *          the factory to the provider registration, and that may be
     *          used to remove the registration from the provider.
     *
     * @exception SecurityException if the caller does not have
     *    permission to register a provider at the factory.
     *
     * @exception AuthException if the provider
     *          construction or registration fails.
     */
    @Override
    public String registerConfigProvider(String className,
           Map properties,
           String layer, String appContext,
           String description) {
        //XXX do we need doPrivilege here
        AuthConfigProvider provider =
            _constructProvider(className, properties, null);
        return _register(provider,properties,
            layer,appContext,description,true);
    }

    @Override
    public String registerConfigProvider(AuthConfigProvider provider,
            String layer, String appContext, String description) {
  return _register(provider,null,layer,appContext,description,false);
    }

    /**
     * Remove the identified provider registration from the factory
     * and invoke any listeners associated with the removed registration.
     *
     * @param registrationID a String that identifies a provider registration
     *          at the factory
     *
     * @return true if there was a registration with the specified identifier
     *          and it was removed. Return false if the registraionID was
     *          invalid.
     *
     * @exception SecurityException if the caller does not have
     *    permission to unregister the provider at the factory.
     *
     */
    @Override
    public boolean removeRegistration(String registrationID) {
        return _unRegister(registrationID);
    }

    /**
     * Disassociate the listener from all the provider
     * registrations whose layer and appContext values are matched
     * by the corresponding arguments to this method.
     *
     * @param listener the RegistrationListener to be detached.
     *
     * @param layer a String identifying the message layer or null.
     *
     * @param appContext a String value identifying the application context
     *          or null.
     *
     * @return an array of String values where each value identifies a
     *          provider registration from which the listener was removed.
     *          This method never returns null; it returns an empty array if
     *          the listener was not removed from any registrations.
     *
     * @exception SecurityException if the caller does not have
     *    permission to detach the listener from the factory.
     *
     */
    @Override
    public String[] detachListener(RegistrationListener listener,
            String layer, String appContext) {
        ArrayList<String> list = new ArrayList<String>();
        String regisID = getRegistrationID(layer, appContext);
        wLock.lock();
        try {
            Set<String> targets = id2RegisListenersMap.keySet();
            for (String targetID : targets) {
                if (regIdImplies(regisID, targetID)) {
                    List<RegistrationListener> listeners =
                            id2RegisListenersMap.get(targetID);
                    if (listeners != null && listeners.remove(listener)) {
                        list.add(targetID);
                    }
                }
            }
        } finally {
            wLock.unlock();
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * Get the registration identifiers for all registrations of the
     * provider instance at the factory.
     *
     * @param provider the AuthConfigurationProvider whose registration
     *          identifiers are to be returned. This argument may be
     *          null, in which case, it indicates that the the id's of
     *          all active registration within the factory are returned.
     *
     * @return an array of String values where each value identifies a
     * provider registration at the factory. This method never returns null;
     * it returns an empty array when their are no registrations at the
     * factory for the identified provider.
     */
    @Override
    public String[] getRegistrationIDs(AuthConfigProvider provider) {
        rLock.lock();
        try {
            Collection<String> regisIDs = null;
            if (provider != null) {
                regisIDs = provider2IdsMap.get(provider);
            } else {
                Collection<List<String>> collList = provider2IdsMap.values();
                if (collList != null) {
                    regisIDs = new HashSet<String>();
                    for (List<String> listIds : collList) {
                         if (listIds != null) {
                             regisIDs.addAll(listIds);
                         }
                    }
                }
            }
            return ((regisIDs != null)?
                regisIDs.toArray(new String[regisIDs.size()]) :
                new String[0]);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * Get the the registration context for the identified registration.
     *
     * @param registrationID a String that identifies a provider registration
     *          at the factory
     *
     * @return a RegistrationContext or null. When a Non-null value is
     * returned, it is a copy of the registration context corresponding to the
     * registration. Null is returned when the registration identifier does
     * not correspond to an active registration
     */
    @Override
    public RegistrationContext getRegistrationContext(String registrationID) {
  rLock.lock();
        try {
      return id2RegisContextMap.get(registrationID);
  } finally {
      rLock.unlock();
  }
    }

   /**
     * Cause the factory to reprocess its persistent declarative
     * representation of provider registrations.
     *
     * <p> A factory should only replace an existing registration when
     * a change of provider implementation class or initialization
     * properties has occurred.
     *
     * @exception AuthException if an error occurred during the
     *          reinitialization.
     *
     * @exception SecurityException if the caller does not have permission
     *    to refresh the factory.
     */
    @Override
    public void refresh() {
        Map<String, List<RegistrationListener>> preExistingListenersMap;
        wLock.lock();
        try {
            preExistingListenersMap = id2RegisListenersMap;
            _loadFactory();
        } finally {
            wLock.unlock();
        }

        // notify pre-existing listeners after (re)loading factory
        if (preExistingListenersMap != null) {
            notifyListeners(preExistingListenersMap);
        }
    }

    private AuthConfigProvider
  getConfigProviderUnderLock(String layer, String appContext,
            RegistrationListener listener) {
  AuthConfigProvider provider = null;
        String regisID = getRegistrationID(layer, appContext);
        String matchedID = null;
        boolean providerFound = false;
        if (id2ProviderMap.containsKey(regisID)) {
            provider = id2ProviderMap.get(regisID);
            providerFound = true;
        }
        if (!providerFound) {
            matchedID = getRegistrationID(null, appContext);
            if (id2ProviderMap.containsKey(matchedID)) {
                provider = id2ProviderMap.get(matchedID);
                providerFound = true;
            }
        }
        if (!providerFound) {
            matchedID = getRegistrationID(layer, null);
            if (id2ProviderMap.containsKey(matchedID)) {
                provider = id2ProviderMap.get(matchedID);
                providerFound = true;
            }
        }
        if (!providerFound) {
            matchedID = getRegistrationID(null, null);
            if (id2ProviderMap.containsKey(matchedID)) {
                provider = id2ProviderMap.get(matchedID);
            }
        }
        if (listener != null) {
            List<RegistrationListener> listeners = id2RegisListenersMap.get(regisID);
            if (listeners == null) {
                listeners = new ArrayList<RegistrationListener>();
                id2RegisListenersMap.put(regisID, listeners);
            }
            if (!listeners.contains(listener)) {
                listeners.add(listener);
            }
        }

        return provider;
    }


    private static String getRegistrationID(String layer, String appContext) {
        String regisID = null;

        // __0                          (null, null)
        // __1<appContext>              (null, appContext)
        // __2<layer>                   (layer, null)
        // __3<nn>_<layer><appContext>  (layer, appContext)

        if (layer != null) {
            regisID = (appContext != null) ?
                "__3" + layer.length() + "_" + layer + appContext :
                "__2" + layer;
        } else {
            regisID = (appContext != null) ?
                "__1" + appContext :
                "__0";
        }
        return regisID;
    }

    /**
     * This API decomposes the given regisID into layer and appContext.
     * @param regisID
     * @return a String array with layer and appContext
     */
    private static String[] decomposeRegisID(String regisID) {
        String layer = null;
        String appContext = null;
        if (regisID.equals("__0")) {
            // null, null
        } else if (regisID.startsWith("__1")) {
            appContext = (regisID.length() == 3)?
                   "" : regisID.substring(3);
        } else if (regisID.startsWith("__2")) {
            layer = (regisID.length() == 3)?
                   "" : regisID.substring(3);
        } else if (regisID.startsWith("__3")) {
            int ind = regisID.indexOf('_', 3);
            if (regisID.length() > 3 && ind > 0) {
                String numberString = regisID.substring(3, ind);
                int n;
                try {
                    n = Integer.parseInt(numberString);
                } catch (Exception ex) {
                    throw new IllegalArgumentException();
                }
                layer = regisID.substring(ind + 1, ind + 1 + n);
                appContext = regisID.substring(ind + 1 + n);
            } else {
                throw new IllegalArgumentException();
            }
        } else {
            throw new IllegalArgumentException();
        }

        return new String[]{layer, appContext};
    }

    private static AuthConfigProvider _constructProvider
    (String className, Map properties, AuthConfigFactory factory) {
        //XXX do we need doPrivilege here
        AuthConfigProvider provider = null;
        if (className != null) {
            try {
                ClassLoader loader =
                        Thread.currentThread().getContextClassLoader();
                Class c = Class.forName(className, true, loader);
                Constructor<AuthConfigProvider> constr =
                        c.getConstructor(Map.class, AuthConfigFactory.class);
                provider = constr.newInstance
                    (new Object[]{properties, factory});
            } catch (Throwable t) {
                Throwable cause = t.getCause();
                logger.log(Level.WARNING,
                        "jmac.factory_unable_to_load_provider",
                        new Object[]{ className, t.toString(), (cause == null ? "cannot determine" : cause.toString())});
            }
        }
        return provider;
    }

    //XXX need to update persistent state and notify effected listeners
    private String _register(AuthConfigProvider provider,
            Map<String, Object> properties,
            String layer,
            String appContext,
            String description,
            boolean persistent) {

        String regisID = getRegistrationID(layer, appContext);
        RegistrationContext rc =
                new RegistrationContextImpl(layer, appContext, description, persistent);
        RegistrationContext prevRegisContext = null;
        Map<String, List<RegistrationListener>> listenerMap;
        wLock.lock();
        try {
          prevRegisContext = id2RegisContextMap.get(regisID);
            AuthConfigProvider prevProvider = id2ProviderMap.get(regisID);

            // handle the persistence first - so that any exceptions occur before
            // the actual registration happens
            if (persistent) {
                _storeRegistration(regisID, rc, provider, properties);
            } else if (prevRegisContext != null && prevRegisContext.isPersistent()) {
                _deleteStoredRegistration(regisID, prevRegisContext);
            }

            boolean wasRegistered = id2ProviderMap.containsKey(regisID);

            if (wasRegistered) {
                List<String> prevRegisIDs = provider2IdsMap.get(prevProvider);
                prevRegisIDs.remove(regisID);
                if (prevRegisIDs.isEmpty()) {
                    provider2IdsMap.remove(prevProvider);
                }
            }

            id2ProviderMap.put(regisID, provider);
            id2RegisContextMap.put(regisID, rc);

            List<String> regisIDs = provider2IdsMap.get(provider);
            if (regisIDs == null) {
                regisIDs = new ArrayList<String>();
                provider2IdsMap.put(provider, regisIDs);
            }

            if (!regisIDs.contains(regisID)) {
                regisIDs.add(regisID);
            }

            listenerMap = getEffectedListeners(regisID);

        } finally {
            wLock.unlock();
        }

        // outside wLock to prevent dead lock
        notifyListeners(listenerMap);

        return regisID;
    }

    //XXX need to update persistent state and notify effected listeners
    private boolean _unRegister(String regisID) {
        boolean rvalue = false;
        RegistrationContext rc = null;
        Map<String, List<RegistrationListener>> listenerMap;
        wLock.lock();
        try {
            rc = id2RegisContextMap.remove(regisID);
            rvalue = id2ProviderMap.containsKey(regisID);
            AuthConfigProvider provider = id2ProviderMap.remove(regisID);
            List<String> regisIDs = provider2IdsMap.get(provider);
            if (regisIDs != null) {
                regisIDs.remove(regisID);
            }
            if (regisIDs == null || regisIDs.isEmpty()) {
                provider2IdsMap.remove(provider);
            }           
            if (!rvalue) {
                return false;
            }
            listenerMap = getEffectedListeners(regisID);
            if (rc != null && rc.isPersistent()) {
                    _deleteStoredRegistration(regisID, rc);
            }
        } finally {
            wLock.unlock();
        }

        // outside wLock to prevent dead lock
        notifyListeners(listenerMap);
        return rvalue;
    }

// the following methods implement the factory's persistence layer
    protected void _loadFactory() {
        try {
            initializeMaps();

            List<EntryInfo> entryList = getRegStore().getPersistedEntries();

            for (EntryInfo info : entryList) {
                if (info.isConstructorEntry()) {
                    _constructProvider(info.getClassName(),
                            info.getProperties(), this);
                } else {
                    boolean first = true;
                    AuthConfigProvider p = null;
                    List<RegistrationContext> contexts = (info.getRegContexts());
                    for (RegistrationContext ctx : contexts) {
                        if (first) {
                            p = _constructProvider(info.getClassName(),
                                    info.getProperties(), null);
                        }
                        _loadRegistration(p, ctx.getMessageLayer(),
                                ctx.getAppContext(), ctx.getDescription());
                    }
                }
            }
        } catch (Exception e) {
            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING,
                        "jmac.factory_auth_config_loader_failure", e);
            }
        }
    }

  /**
   * Initialize the static maps in a static method
   */
  private static void initializeMaps() {
    id2ProviderMap = new HashMap<String, AuthConfigProvider>();
    id2RegisContextMap = new HashMap<String, RegistrationContext>();
    id2RegisListenersMap =
            new HashMap<String, List<RegistrationListener>>();
    provider2IdsMap = new HashMap<AuthConfigProvider, List<String>>();
  }

    private static String _loadRegistration(AuthConfigProvider provider,
            String layer,
            String appContext,
            String description) {

        RegistrationContext rc =
                new RegistrationContextImpl(layer, appContext, description, true);
        String regisID = getRegistrationID(layer, appContext);
        id2RegisContextMap.get(regisID);
        AuthConfigProvider prevProvider = id2ProviderMap.get(regisID);
        boolean wasRegistered = id2ProviderMap.containsKey(regisID);
        if (wasRegistered) {
            List<String> prevRegisIDs = provider2IdsMap.get(prevProvider);
            prevRegisIDs.remove(regisID);
            if (prevRegisIDs.isEmpty()) {
                provider2IdsMap.remove(prevProvider);
            }
        }

        id2ProviderMap.put(regisID, provider);
        id2RegisContextMap.put(regisID, rc);

        List<String> regisIDs = provider2IdsMap.get(provider);
        if (regisIDs == null) {
            regisIDs = new ArrayList<String>();
            provider2IdsMap.put(provider, regisIDs);
        }

        if (!regisIDs.contains(regisID)) {
            regisIDs.add(regisID);
        }

        return regisID;
    }

    private void _storeRegistration(String regId,
            RegistrationContext ctx, AuthConfigProvider p, Map properties) {

        String className = null;
        if (p != null) {
            className = p.getClass().getName();
        }
    if (propertiesContainAnyNonStringValues(properties)) {
      throw new IllegalArgumentException("AuthConfigProvider cannot be registered - properties must all be of type String.");
    }
        if (ctx.isPersistent()) {
            getRegStore().store(className, ctx, properties);
        }
    }

    private boolean propertiesContainAnyNonStringValues(Map<String,Object> props) {
      if (props != null) {
        for(Map.Entry<String, Object> entry : props.entrySet()) {
          if (!(entry.getValue() instanceof String)) {
            return true;
          }
        }
      }
      return false;
  }

    private void _deleteStoredRegistration(String regId,
            RegistrationContext ctx) {

        if (ctx.isPersistent()) {
            getRegStore().delete(ctx);
        }
    }

    private static boolean regIdImplies(String reference, String target) {

        boolean rvalue = true;

        String[] refID = decomposeRegisID(reference);
        String[] targetID = decomposeRegisID(target);

        if (refID[0] != null && !refID[0].equals(targetID[0])) {
            rvalue = false;
        } else if (refID[1] != null && !refID[1].equals(targetID[1])) {
            rvalue = false;
        }
        return rvalue;
    }

    /* will return some extra listeners. iow, effected listeners could be reduced
     * by removing any associated with a provider registration id that is
     * more specific than the one being added or removed.l
     */
    private static Map<String, List<RegistrationListener>> getEffectedListeners(String regisID) {
        Map<String, List<RegistrationListener>> effectedListeners =
                new HashMap<String, List<RegistrationListener>>();
        Set<String> listenerRegistrations =
                new HashSet<String>(id2RegisListenersMap.keySet());

        for (String listenerID : listenerRegistrations) {
            if (regIdImplies(regisID, listenerID)) {
                if (!effectedListeners.containsKey(listenerID)) {
                    effectedListeners.put(listenerID, new ArrayList<RegistrationListener>());
                }
                effectedListeners.get(listenerID).addAll(id2RegisListenersMap.remove(listenerID));
            }
        }
        return effectedListeners;
    }

    private static void notifyListeners(Map<String, List<RegistrationListener>> map) {
      Set<Map.Entry<String, List<RegistrationListener>>> entrySet = map.entrySet();
      for (Map.Entry<String, List<RegistrationListener>> entry : entrySet) {
            List<RegistrationListener> listeners = map.get(entry.getKey());

            if (listeners != null && listeners.size() > 0) {
                String[] dIds = decomposeRegisID(entry.getKey());

                for (RegistrationListener listener : listeners) {
                    listener.notify(dIds[0], dIds[1]);
                }
            }
      }
    }
}
TOP

Related Classes of com.sun.jaspic.config.factory.BaseAuthConfigFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.