001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.util.ArrayList;
026import java.util.Map;
027import java.util.concurrent.ConcurrentHashMap;
028import java.util.concurrent.CopyOnWriteArrayList;
029import java.util.concurrent.atomic.AtomicBoolean;
030
031import org.eclipse.aether.MultiRuntimeException;
032import org.eclipse.aether.RepositorySystemSession.CloseableSession;
033import org.eclipse.aether.impl.RepositorySystemLifecycle;
034
035import static java.util.Objects.requireNonNull;
036
037/**
038 *
039 */
040@Singleton
041@Named
042public class DefaultRepositorySystemLifecycle implements RepositorySystemLifecycle {
043    private final AtomicBoolean shutdown;
044
045    private final CopyOnWriteArrayList<Runnable> onSystemEndedHandlers;
046
047    private final ConcurrentHashMap<String, CopyOnWriteArrayList<Runnable>> onSessionEndedHandlers;
048
049    @Inject
050    public DefaultRepositorySystemLifecycle() {
051        this.shutdown = new AtomicBoolean(false);
052        this.onSystemEndedHandlers = new CopyOnWriteArrayList<>();
053        this.onSessionEndedHandlers = new ConcurrentHashMap<>();
054    }
055
056    @Override
057    public void systemEnded() {
058        final ArrayList<Exception> exceptions = new ArrayList<>();
059        if (shutdown.compareAndSet(false, true)) {
060            for (Map.Entry<String, CopyOnWriteArrayList<Runnable>> sessionEndedHandlers :
061                    onSessionEndedHandlers.entrySet()) {
062                IllegalStateException sessionNotClosed =
063                        new IllegalStateException("Session " + sessionEndedHandlers.getKey() + " not closed");
064                exceptions.add(sessionNotClosed);
065                for (Runnable onCloseHandler : sessionEndedHandlers.getValue()) {
066                    try {
067                        onCloseHandler.run();
068                    } catch (Exception e) {
069                        sessionNotClosed.addSuppressed(e);
070                    }
071                }
072            }
073            for (Runnable onCloseHandler : onSystemEndedHandlers) {
074                try {
075                    onCloseHandler.run();
076                } catch (Exception e) {
077                    exceptions.add(e);
078                }
079            }
080            MultiRuntimeException.mayThrow("system on-close handler failures", exceptions);
081        }
082    }
083
084    @Override
085    public void addOnSystemEndedHandler(Runnable handler) {
086        requireNonNull(handler, "handler cannot be null");
087        requireNotShutdown();
088        onSystemEndedHandlers.add(0, handler);
089    }
090
091    @Override
092    public void sessionStarted(CloseableSession session) {
093        requireNonNull(session, "session cannot be null");
094        requireNotShutdown();
095        String sessionId = session.sessionId();
096        onSessionEndedHandlers.compute(sessionId, (k, v) -> {
097            if (v != null) {
098                throw new IllegalStateException("session instance already registered");
099            }
100            return new CopyOnWriteArrayList<>();
101        });
102    }
103
104    @Override
105    public void sessionEnded(CloseableSession session) {
106        requireNonNull(session, "session cannot be null");
107        requireNotShutdown();
108        String sessionId = session.sessionId();
109        ArrayList<Runnable> handlers = new ArrayList<>();
110        onSessionEndedHandlers.compute(sessionId, (k, v) -> {
111            if (v == null) {
112                throw new IllegalStateException("session instance not registered");
113            }
114            handlers.addAll(v);
115            return null;
116        });
117
118        ArrayList<Exception> exceptions = new ArrayList<>();
119        for (Runnable handler : handlers) {
120            try {
121                handler.run();
122            } catch (Exception e) {
123                exceptions.add(e);
124            }
125        }
126        MultiRuntimeException.mayThrow("sessionEnded handler issue(s)", exceptions);
127    }
128
129    @Override
130    public void addOnSessionEndedHandle(CloseableSession session, Runnable handler) {
131        requireNonNull(session, "session cannot be null");
132        requireNonNull(handler, "handler cannot be null");
133        requireNotShutdown();
134        String sessionId = session.sessionId();
135        onSessionEndedHandlers.compute(sessionId, (k, v) -> {
136            if (v == null) {
137                throw new IllegalStateException("session instance not registered");
138            }
139            v.add(handler);
140            return v;
141        });
142    }
143
144    private void requireNotShutdown() {
145        if (shutdown.get()) {
146            throw new IllegalStateException("repository system is already shut down");
147        }
148    }
149}