001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.nuxeo.runtime.api.login;
017
018import java.util.concurrent.atomic.AtomicInteger;
019
020import javax.security.auth.login.AppConfigurationEntry;
021import javax.security.auth.login.Configuration;
022
023import org.apache.commons.logging.LogFactory;
024
025public class LoginConfiguration extends Configuration {
026
027    public static final LoginConfiguration INSTANCE = new LoginConfiguration();
028
029    protected final AtomicInteger counter = new AtomicInteger(0);
030
031    public interface Provider {
032
033        public AppConfigurationEntry[] getAppConfigurationEntry(String name);
034
035    }
036
037    protected final InheritableThreadLocal<Provider> holder = new InheritableThreadLocal<Provider>() {
038        @Override
039        protected Provider initialValue() {
040            return context.provider;
041        };
042    };
043
044    @Override
045    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
046        AppConfigurationEntry[] appConfigurationEntry = holder.get().getAppConfigurationEntry(name);
047        if (appConfigurationEntry == null) {
048            appConfigurationEntry = context.parent.getAppConfigurationEntry(name);
049        }
050        return appConfigurationEntry;
051
052    }
053
054    @Override
055    public void refresh() {
056        context.parent.refresh();
057    }
058
059    protected class InstallContext {
060        protected final Configuration parent = Configuration.getConfiguration();
061
062        protected final Provider provider;
063
064        protected final Thread thread = Thread.currentThread();
065
066        protected final Throwable stacktrace = new Throwable();
067
068        protected InstallContext(Provider provider) {
069            this.provider = provider;
070        }
071
072        @Override
073        public String toString() {
074            return "Login Installation Context [parent=" + parent + ", thread=" + thread + "]";
075        }
076
077    }
078
079    protected InstallContext context;
080
081    public void install(Provider provider) {
082        holder.set(provider);
083        int count = counter.incrementAndGet();
084        if (count == 1) {
085            context = new InstallContext(provider);
086            Configuration.setConfiguration(this);
087            LogFactory.getLog(LoginConfiguration.class).trace("installed login configuration", context.stacktrace);
088        }
089    }
090
091    public void uninstall() {
092        holder.remove();
093        int count = counter.decrementAndGet();
094        if (count == 0) {
095            LogFactory.getLog(LoginConfiguration.class).trace("uninstalled login configuration " + context.thread,
096                    context.stacktrace);
097            Configuration.setConfiguration(context.parent);
098            context = null;
099        }
100    }
101
102    /**
103     * @since 5.9.5
104     */
105    public void cleanupThisThread() {
106        holder.remove();
107    }
108
109}