]> source.dussan.org Git - archiva.git/commitdiff
Improving event API
authorMartin Schreier <martin_s@apache.org>
Sun, 13 Feb 2022 09:27:32 +0000 (10:27 +0100)
committerMartin Schreier <martin_s@apache.org>
Sun, 13 Feb 2022 09:27:32 +0000 (10:27 +0100)
47 files changed:
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/Event.java [deleted file]
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventHandler.java [deleted file]
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventManager.java [deleted file]
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventSource.java [deleted file]
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventType.java [deleted file]
archiva-modules/archiva-base/archiva-common/src/test/java/org/apache/archiva/event/EventManagerTest.java [deleted file]
archiva-modules/archiva-base/archiva-consumers/archiva-consumer-archetype/pom.xml
archiva-modules/archiva-base/archiva-event-api/pom.xml [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/AbstractEventManager.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/BasicEventManager.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/Event.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventContext.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventContextBuilder.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventHandler.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventSource.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventType.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/RepositoryContext.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/RestContext.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/UserContext.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/package-info.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-api/src/test/java/org/apache/archiva/event/BasicEventManagerTest.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-central/pom.xml [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-central/src/main/java/org/apache/archiva/event/central/CentralEventManager.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-event-central/src/main/resources/META-INF/spring-context.xml [new file with mode: 0644]
archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/pom.xml
archiva-modules/archiva-base/archiva-repository-api/pom.xml
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryHandler.java
archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/event/RepositoryEvent.java
archiva-modules/archiva-base/archiva-repository-layer/pom.xml
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/AbstractRepository.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/AbstractRepositoryHandler.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/ArchivaRepositoryRegistry.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/managed/ManagedRepositoryHandler.java
archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/base/group/RepositoryGroupHandlerTest.java
archiva-modules/archiva-base/pom.xml
archiva-modules/archiva-maven/archiva-maven-repository/pom.xml
archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/maven/repository/MavenRepositoryProvider.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/v2/model/MavenManagedRepository.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/v2/model/MavenManagedRepositoryUpdate.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/v2/model/map/MavenRepositoryMapper.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/v2/svc/ErrorKeys.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/test/java/org/apache/archiva/rest/api/v2/model/map/MavenRepositoryMapperTest.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/pom.xml
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/AbstractService.java [new file with mode: 0644]
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/maven/DefaultMavenManagedRepositoryService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/v2/svc/maven/NativeMavenManagedRepositoryServiceTest.java
pom.xml

diff --git a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/Event.java b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/Event.java
deleted file mode 100644 (file)
index eda8570..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-package org.apache.archiva.event;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.time.LocalDateTime;
-import java.util.EventObject;
-
-/**
- * Base class for events. Events have a type and a source.
- * The source is the instance that raised the event.
- *
- * There are different event types for a given event. The types are represented in a hierarchical structure.
- *
- * Events can be chained, which means a event listener can catch events and rethrow them as its own event.
- *
- */
-public class Event extends EventObject implements Cloneable {
-
-    private static final long serialVersionUID = -7171846575892044990L;
-
-    public static final EventType<Event> ANY = EventType.ROOT;
-
-    private Event previous;
-    private final EventType<? extends Event> type;
-    private final LocalDateTime createTime;
-
-    public Event(EventType<? extends Event> type, Object originator) {
-        super(originator);
-        this.type = type;
-        this.createTime = LocalDateTime.now();
-    }
-
-    private Event(Event previous, Object originator) {
-        super(originator);
-        this.previous = previous;
-        this.type = previous.getType();
-        this.createTime = previous.getCreateTime();
-    }
-
-    /**
-     * Returns the event type that is associated with this event instance.
-     * @return the event type
-     */
-    public EventType<? extends Event> getType() {
-        return type;
-    };
-
-    /**
-     * Returns the time, when the event was created.
-     * @return
-     */
-    public LocalDateTime getCreateTime() {
-        return createTime;
-    }
-
-
-    /**
-     * Recreates the event with the given instance as the new source. The
-     * current source is stored in the previous event.
-     * @param newSource The new source
-     * @return a new event instance, where <code>this</code> is stored as previous event
-     */
-    public Event copyFor(Object newSource) {
-        Event newEvent = (Event) this.clone();
-        newEvent.previous = this;
-        newEvent.source = newSource;
-        return newEvent;
-    }
-
-    /**
-     * Returns the previous event or <code>null</code>, if this is a root event.
-     * @return the previous event or <code>null</code>, if it does not exist
-     */
-    public Event getPreviousEvent() {
-        return previous;
-    }
-
-    /**
-     * Returns <code>true</code>, if the event has a previous event.
-     * @return <code>true</code>, if this has a previous event, otherwise <code>false</code>
-     */
-    public boolean hasPreviousEvent() {
-        return previous!=null;
-    }
-
-    @Override
-    protected Object clone() {
-        try {
-            return super.clone();
-        } catch (CloneNotSupportedException e) {
-            // this should not happen
-            throw new RuntimeException("Event is not clonable");
-        }
-    }
-}
diff --git a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventHandler.java b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventHandler.java
deleted file mode 100644 (file)
index f98adcd..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.apache.archiva.event;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-import java.util.EventListener;
-
-/**
- * A listener that accepts events.
- */
-public interface EventHandler<T extends Event> extends EventListener {
-
-    void handle(T event);
-}
diff --git a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventManager.java b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventManager.java
deleted file mode 100644 (file)
index 00a2400..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.apache.archiva.event;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class EventManager implements EventSource
-{
-
-    private static final Logger LOG = LoggerFactory.getLogger(EventManager.class);
-
-    private final ConcurrentHashMap<EventType<? extends Event>, Set<EventHandler>> handlerMap = new ConcurrentHashMap<>();
-
-    private final Object source;
-
-    public EventManager(Object source) {
-        if (source==null) {
-            throw new IllegalArgumentException("The source may not be null");
-        }
-        this.source = source;
-    }
-
-    @Override
-    public <T extends Event> void registerEventHandler(EventType<T> type, EventHandler<? super T> eventHandler) {
-        Set<EventHandler> handlers = handlerMap.computeIfAbsent(type, t -> new LinkedHashSet<>());
-        if (!handlers.contains(eventHandler)) {
-            handlers.add(eventHandler);
-        }
-    }
-
-    @Override
-    public <T extends Event> void unregisterEventHandler(EventType<T> type, EventHandler<? super T> eventHandler) {
-        if (handlerMap.containsKey(type)) {
-            handlerMap.get(type).remove(eventHandler);
-        }
-    }
-
-    public void fireEvent(Event fireEvent) {
-        final EventType<? extends Event> type = fireEvent.getType();
-        Event event;
-        if (fireEvent.getSource()!=source) {
-            event = fireEvent.copyFor(source);
-        } else {
-            event = fireEvent;
-        }
-        for (EventType<? extends Event> handlerType : handlerMap.keySet()) {
-            if (EventType.isInstanceOf(type, handlerType)) {
-                    for (EventHandler handler : handlerMap.get(handlerType)) {
-                        try {
-                            handler.handle(event);
-                        } catch (Exception e) {
-                            // We catch all errors from handlers
-                            LOG.error("An error occured during event handling: {}", e.getMessage(), e);
-                        }
-                    }
-            }
-        }
-    }
-}
diff --git a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventSource.java b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventSource.java
deleted file mode 100644 (file)
index 452a5e9..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.apache.archiva.event;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * A repository event source raises events to its registered listeners.
- * Listeners register to event types that are structured hierarchical.
- *
- */
-public interface EventSource {
-
-    <T extends Event> void registerEventHandler( EventType<T> type, EventHandler<? super T> eventHandler);
-
-    <T extends Event> void unregisterEventHandler(EventType<T> type, EventHandler<? super T> eventHandler);
-
-}
diff --git a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventType.java b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/event/EventType.java
deleted file mode 100644 (file)
index 50d4267..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-package org.apache.archiva.event;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
-import java.io.Serializable;
-import java.util.*;
-
-/**
- * Event types define a hierarchical structure of events. Each event is bound to a certain event type.
- * All event types have a super type, only the root event type {@link EventType#ROOT} has no super type.
- *
- * Event types should be stored as static fields on the events itself.
- *
- * @param <T> The type class parameter allows to define the types in a type safe way and represents a event class,
- *           where the type is associated to.
- */
-public class EventType<T extends Event> implements Serializable  {
-
-
-    public static final EventType<Event> ROOT = new EventType<>();
-
-    private final String name;
-    private final EventType<? super T> superType;
-    private WeakHashMap<EventType<? extends T>, Void> subTypes;
-
-    /**
-     * Creates a type with the given name and the root type as parent.
-     * @param name the name of the new type
-     */
-    public EventType(String name) {
-        this.superType = ROOT;
-        this.name = name;
-    }
-
-    /**
-     * Creates a event type instance with the given super type and name.
-     *
-     * @param superType The super type or <code>null</code>, if this is the root type.
-     * @param name
-     */
-    public EventType(EventType<? super T> superType, String name) {
-        if (superType==null) {
-            throw new NullPointerException("Super Type may not be null");
-        }
-        this.name = name;
-        this.superType = superType;
-        superType.register(this);
-    }
-
-    /**
-     * Creates the root type
-     */
-    private EventType() {
-        this.name="ROOT";
-        this.superType=null;
-    }
-
-    public String name() {
-        return name;
-    }
-
-    public EventType<? super T> getSuperType() {
-        return superType;
-    }
-
-    private void register(EventType<? extends T> subType) {
-        if (subTypes == null) {
-            subTypes = new WeakHashMap<>();
-        }
-        for (EventType<? extends T> t : subTypes.keySet()) {
-            if (((t.name == null && subType.name == null) || (t.name != null && t.name.equals(subType.name)))) {
-                throw new IllegalArgumentException("EventType \"" + subType + "\""
-                        + "with parent \"" + subType.getSuperType()+"\" already exists");
-            }
-        }
-        subTypes.put(subType, null);
-    }
-
-
-    public static List<EventType<?>> fetchSuperTypes(EventType<?> type) {
-        List<EventType<?>> typeList = new ArrayList<>();
-        EventType<?> cType = type;
-        while (cType!=null) {
-            typeList.add(cType);
-            cType = cType.getSuperType();
-        }
-        return typeList;
-    }
-
-    public static boolean isInstanceOf(EventType<?> type, EventType<?> baseType) {
-        EventType<?> cType = type;
-        while(cType!=null) {
-            if (cType == baseType) {
-                return true;
-            }
-            cType = cType.getSuperType();
-        }
-        return false;
-    }
-
-
-    private Object writeReplace() throws ObjectStreamException {
-        Deque<String> path = new LinkedList<>();
-        EventType<?> t = this;
-        while (t != ROOT) {
-            path.addFirst(t.name);
-            t = t.superType;
-        }
-        return new EventTypeSerialization(new ArrayList<>(path));
-    }
-
-    static class EventTypeSerialization implements Serializable {
-        private static final long serialVersionUID = 1841649460281865547L;
-        private List<String> path;
-
-        public EventTypeSerialization(List<String> path) {
-            this.path = path;
-        }
-
-        private Object readResolve() throws ObjectStreamException {
-            EventType t = ROOT;
-            for (int i = 0; i < path.size(); ++i) {
-                String p = path.get(i);
-                if (t.subTypes != null) {
-                    EventType<?> s = findSubType(t.subTypes.keySet(), p);
-                    if (s == null) {
-                        throw new InvalidObjectException("Cannot find event type \"" + p + "\" (of " + t + ")");
-                    }
-                    t = s;
-                } else {
-                    throw new InvalidObjectException("Cannot find event type \"" + p + "\" (of " + t + ")");
-                }
-            }
-            return t;
-        }
-
-        private EventType<?> findSubType(Set<EventType> subTypes, String name) {
-            for (EventType t : subTypes) {
-                if (((t.name == null && name == null) || (t.name != null && t.name.equals(name)))) {
-                    return t;
-                }
-            }
-            return null;
-        }
-
-    }
-}
diff --git a/archiva-modules/archiva-base/archiva-common/src/test/java/org/apache/archiva/event/EventManagerTest.java b/archiva-modules/archiva-base/archiva-common/src/test/java/org/apache/archiva/event/EventManagerTest.java
deleted file mode 100644 (file)
index f894cb5..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.apache.archiva.event;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * @author Martin Stockhammer <martin_s@apache.org>
- */
-public class EventManagerTest
-{
-
-    private class TestHandler implements EventHandler<Event> {
-
-        private List<Event> eventList = new ArrayList<>( );
-        @Override
-        public void handle( Event event )
-        {
-            eventList.add( event );
-        }
-
-        public List<Event> getEventList() {
-            return eventList;
-        }
-    }
-
-    private EventType<Event> testType = new EventType<>( "TEST" );
-    private EventType<Event> testTestType = new EventType<>( testType,"TEST.TEST" );
-    private EventType<Event> otherType = new EventType( "OTHER" );
-
-    @Test
-    public void registerEventHandler( )
-    {
-        EventManager eventManager = new EventManager( this );
-        TestHandler handler1 = new TestHandler( );
-        TestHandler handler2 = new TestHandler( );
-        TestHandler handler3 = new TestHandler( );
-        TestHandler handler4 = new TestHandler( );
-
-        eventManager.registerEventHandler( Event.ANY, handler1 );
-        eventManager.registerEventHandler( testType, handler2 );
-        eventManager.registerEventHandler( testTestType, handler3 );
-        eventManager.registerEventHandler( otherType, handler4 );
-
-        Event event1 = new Event( testType, this );
-        eventManager.fireEvent( event1 );
-        assertEquals( 1, handler1.eventList.size( ) );
-        assertEquals( 1, handler2.eventList.size( ) );
-        assertEquals( 0, handler3.eventList.size( ) );
-        assertEquals( 0, handler4.eventList.size( ) );
-
-        Event event2 = new Event( testTestType, event1 );
-        eventManager.fireEvent( event2 );
-        assertEquals( 2, handler1.eventList.size( ) );
-        assertEquals( 2, handler2.eventList.size( ) );
-        assertEquals( 1, handler3.eventList.size( ) );
-        assertEquals( 0, handler4.eventList.size( ) );
-
-        Event event3 = new Event( otherType, event1 );
-        eventManager.fireEvent( event3 );
-        assertEquals( 3, handler1.eventList.size( ) );
-        assertEquals( 2, handler2.eventList.size( ) );
-        assertEquals( 1, handler3.eventList.size( ) );
-        assertEquals( 1, handler4.eventList.size( ) );
-
-
-
-    }
-
-    @Test
-    public void unregisterEventHandler( )
-    {
-        EventManager eventManager = new EventManager( this );
-        TestHandler handler1 = new TestHandler( );
-        TestHandler handler2 = new TestHandler( );
-        TestHandler handler3 = new TestHandler( );
-        TestHandler handler4 = new TestHandler( );
-
-        eventManager.registerEventHandler( Event.ANY, handler1 );
-        eventManager.registerEventHandler( testType, handler2 );
-        eventManager.registerEventHandler( testTestType, handler3 );
-        eventManager.registerEventHandler( otherType, handler4 );
-
-        eventManager.unregisterEventHandler( Event.ANY, handler1 );
-        Event event1 = new Event( testType, this );
-        eventManager.fireEvent( event1 );
-        assertEquals( 0, handler1.eventList.size( ) );
-        assertEquals( 1, handler2.eventList.size( ) );
-        assertEquals( 0, handler3.eventList.size( ) );
-        assertEquals( 0, handler4.eventList.size( ) );
-
-        eventManager.unregisterEventHandler( otherType, handler2 );
-        Event event2 = new Event( testType, this );
-        eventManager.fireEvent( event2 );
-        assertEquals( 0, handler1.eventList.size( ) );
-        assertEquals( 2, handler2.eventList.size( ) );
-        assertEquals( 0, handler3.eventList.size( ) );
-        assertEquals( 0, handler4.eventList.size( ) );
-    }
-
-    @Test
-    public void fireEvent( )
-    {
-        Object other = new Object( );
-        EventManager eventManager = new EventManager( this );
-        assertThrows( NullPointerException.class, ( ) -> eventManager.fireEvent( null ) );
-        Event event = new Event( EventType.ROOT, other );
-        assertEquals( other, event.getSource( ) );
-        TestHandler handler = new TestHandler( );
-        eventManager.registerEventHandler( EventType.ROOT, handler );
-        eventManager.fireEvent( event );
-        assertEquals( 1, handler.getEventList( ).size( ) );
-        Event newEvent = handler.getEventList( ).get( 0 );
-        assertNotEquals( event, newEvent );
-        assertEquals( this, newEvent.getSource( ) );
-
-    }
-}
\ No newline at end of file
index 42844565ebc7d4b641c35283660bec822d3bebfe..6e2f82f47884f1692b5cc7d22ec1f47644f859e7 100644 (file)
       <artifactId>jcl-over-slf4j</artifactId>
     </dependency>
 
+    <!-- Test scope -->
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-api</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-consumer-api</artifactId>
diff --git a/archiva-modules/archiva-base/archiva-event-api/pom.xml b/archiva-modules/archiva-base/archiva-event-api/pom.xml
new file mode 100644 (file)
index 0000000..6c9119c
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>archiva-base</artifactId>
+    <groupId>org.apache.archiva</groupId>
+    <version>3.0.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <name>Archiva :: Base :: Event API</name>
+  <groupId>org.apache.archiva.event</groupId>
+  <artifactId>archiva-event-api</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.archiva</groupId>
+      <artifactId>archiva-common</artifactId>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/AbstractEventManager.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/AbstractEventManager.java
new file mode 100644 (file)
index 0000000..6d2ddff
--- /dev/null
@@ -0,0 +1,84 @@
+package org.apache.archiva.event;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public class AbstractEventManager implements EventSource
+{
+    private static final Logger log = LoggerFactory.getLogger( AbstractEventManager.class );
+
+    protected final ConcurrentHashMap<EventType<? extends Event>, Set<EventHandler>> handlerMap = new ConcurrentHashMap<>();
+
+    @Override
+    public <T extends Event> void registerEventHandler( EventType<T> type, EventHandler<? super T> eventHandler) {
+        Set<EventHandler> handlers = handlerMap.computeIfAbsent(type, t -> new LinkedHashSet<>());
+        if (!handlers.contains(eventHandler)) {
+            handlers.add(eventHandler);
+        }
+        log.debug( "Event handler registered: " + eventHandler.getClass( ) );
+    }
+
+    @Override
+    public <T extends Event> void unregisterEventHandler( EventType<T> type, EventHandler<? super T> eventHandler) {
+        if (handlerMap.containsKey(type)) {
+            handlerMap.get(type).remove(eventHandler);
+            log.debug( "Event handler unregistered: " + eventHandler.getClass( ) );
+        }
+    }
+
+    /**
+     * Fires the given event for the given source. If the source of the provided event does not match the <code>source</code>
+     * parameter the event will be chained.
+     *
+     * The event will be sent to all registered event handler. Exceptions during handling are not propagated to the
+     * caller.
+     *
+     * @param fireEvent the event to fire
+     * @param source the source object
+     */
+    public void fireEvent(Event fireEvent, Object source) {
+        final EventType<? extends Event> type = fireEvent.getType();
+        Event event;
+        if (fireEvent.getSource()!=source) {
+            event = fireEvent.copyFor(source);
+        } else {
+            event = fireEvent;
+        }
+        for (EventType<? extends Event> handlerType : handlerMap.keySet()) {
+            if (EventType.isInstanceOf(type, handlerType)) {
+                for (EventHandler handler : handlerMap.get(handlerType)) {
+                    try {
+                        handler.handle(event);
+                    } catch (Throwable e) {
+                        // We catch all errors from handlers
+                        log.error("An error occured during event handling: {}", e.getMessage(), e);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/BasicEventManager.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/BasicEventManager.java
new file mode 100644 (file)
index 0000000..f952b9e
--- /dev/null
@@ -0,0 +1,41 @@
+package org.apache.archiva.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BasicEventManager extends AbstractEventManager implements EventSource
+{
+
+    private static final Logger LOG = LoggerFactory.getLogger( BasicEventManager.class);
+
+    private final Object source;
+
+    public BasicEventManager( Object source) {
+        if (source==null) {
+            throw new IllegalArgumentException("The source may not be null");
+        }
+        this.source = source;
+    }
+
+    public void fireEvent(Event fireEvent) {
+        super.fireEvent( fireEvent, source );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/Event.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/Event.java
new file mode 100644 (file)
index 0000000..2439105
--- /dev/null
@@ -0,0 +1,150 @@
+package org.apache.archiva.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.time.LocalDateTime;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Base class for events. Events have a type and a source.
+ * The source is the instance that raised the event.
+ *
+ * There are different event types for a given event. The types are represented in a hierarchical structure.
+ *
+ * Events can be chained, which means a event listener can catch events and rethrow them as its own event.
+ *
+ */
+public class Event extends EventObject implements Cloneable {
+
+    private static final long serialVersionUID = -7171846575892044990L;
+
+    public static final EventType<Event> ANY = EventType.ROOT;
+
+    private Event previous;
+    private final EventType<? extends Event> type;
+    private final LocalDateTime createTime;
+
+    private HashMap<Class<? extends EventContext>, EventContext> contextMap = new HashMap<>( );
+
+    public Event(EventType<? extends Event> type, Object originator) {
+        super(originator);
+        this.type = type;
+        this.createTime = LocalDateTime.now();
+    }
+
+    private Event(Event previous, Object originator) {
+        super(originator);
+        this.previous = previous;
+        this.type = previous.getType();
+        this.createTime = previous.getCreateTime();
+        this.contextMap = previous.contextMap;
+    }
+
+    /**
+     * Returns the event type that is associated with this event instance.
+     * @return the event type
+     */
+    public EventType<? extends Event> getType() {
+        return type;
+    };
+
+    /**
+     * Returns the time, when the event was created.
+     * @return
+     */
+    public LocalDateTime getCreateTime() {
+        return createTime;
+    }
+
+    public <T extends EventContext> T getContext(Class<T> contextClazz) throws IllegalArgumentException {
+        if (contextMap.containsKey( contextClazz )) {
+            return (T) contextMap.get( contextClazz );
+        } else {
+            T ctx = null;
+            for ( Map.Entry<Class<? extends EventContext>, EventContext> clazzEntry : contextMap.entrySet()) {
+                if ( contextClazz.isAssignableFrom( clazzEntry.getKey() ) )
+                {
+                    ctx = (T) clazzEntry.getValue( );
+                    break;
+                }
+            }
+            if (ctx!=null) {
+                contextMap.put( contextClazz, ctx );
+                return ctx;
+            }
+        }
+        throw new IllegalArgumentException( "No matching event context registered for " + contextClazz );
+    }
+
+    public Map<String, String> getContextData() {
+        return contextMap.entrySet( ).stream( ).flatMap( ctx -> ctx.getValue( ).getData( ).entrySet( ).stream( ) )
+            .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) );
+    }
+
+    public <T extends EventContext> void setContext( Class<T> clazz, T context) {
+        this.contextMap.put( clazz, context );
+    }
+
+    public <T extends EventContext> void setContext( T context) {
+        this.contextMap.put( context.getClass(), context );
+    }
+
+    /**
+     * Recreates the event with the given instance as the new source. The
+     * current source is stored in the previous event.
+     * @param newSource The new source
+     * @return a new event instance, where <code>this</code> is stored as previous event
+     */
+    public Event copyFor(Object newSource) {
+        Event newEvent = (Event) this.clone();
+        newEvent.previous = this;
+        newEvent.source = newSource;
+        newEvent.contextMap = this.contextMap;
+        return newEvent;
+    }
+
+    /**
+     * Returns the previous event or <code>null</code>, if this is a root event.
+     * @return the previous event or <code>null</code>, if it does not exist
+     */
+    public Event getPreviousEvent() {
+        return previous;
+    }
+
+    /**
+     * Returns <code>true</code>, if the event has a previous event.
+     * @return <code>true</code>, if this has a previous event, otherwise <code>false</code>
+     */
+    public boolean hasPreviousEvent() {
+        return previous!=null;
+    }
+
+    @Override
+    protected Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            // this should not happen
+            throw new RuntimeException("Event is not clonable");
+        }
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventContext.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventContext.java
new file mode 100644 (file)
index 0000000..9e113de
--- /dev/null
@@ -0,0 +1,48 @@
+package org.apache.archiva.event;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Map;
+
+/**
+ * Context information about a specific event.
+ * This is used to provide specific information about the event context by using the generic
+ * event interface.
+ * Some event handler may need information about the underlying event but have no access to the
+ * API classes that represent the event.
+ *
+ * Context information is always string based and should not depend on external classes apart from JDK classes.
+ *
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public interface EventContext
+{
+    /**
+     * Returns the prefix used for entry keys in the repository data map.
+     * @return the prefix string for this context
+     */
+    String getPrefix();
+
+    /**
+     * Returns the context data as map of strings. Each entry key is prefixed with
+     * the unique prefix of this context.
+     *
+     * @return the map of key value pairs stored in this context
+     */
+    Map<String,String> getData();
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventContextBuilder.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventContextBuilder.java
new file mode 100644 (file)
index 0000000..41f10ce
--- /dev/null
@@ -0,0 +1,72 @@
+package org.apache.archiva.event;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.event.context.RepositoryContext;
+import org.apache.archiva.event.context.RestContext;
+import org.apache.archiva.event.context.UserContext;
+
+/**
+ * Static helper class that allows to set certain context data
+ *
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public class EventContextBuilder
+{
+    Event evt;
+
+    public static void setUserContext(Event evt, String user, String remoteAddress) {
+        evt.setContext( UserContext.class, new UserContext( user, remoteAddress ) );
+    }
+
+    public static void setRestcontext(Event evt, String service, String path, String operation, int resultCode, String... parameters ) {
+        evt.setContext( RestContext.class, new RestContext( service, path, operation, resultCode, parameters ) );
+    }
+
+    public static void setRepositoryContext(Event evt, String id, String type, String flavour ) {
+        evt.setContext( RepositoryContext.class, new RepositoryContext( id, type, flavour ) );
+    }
+
+    private EventContextBuilder( Event evt) {
+        this.evt = evt;
+    }
+
+    public static EventContextBuilder withEvent( Event evt )
+    {
+        return new EventContextBuilder( evt );
+    }
+
+    public EventContextBuilder withUser( String user, String remoteAddress) {
+        setUserContext( this.evt, user, remoteAddress );
+        return this;
+    }
+
+    public EventContextBuilder witRest( String service, String path, String operation, int resultCode, String... parameters) {
+        setRestcontext( this.evt, service, path, operation, resultCode, parameters );
+        return this;
+    }
+
+    public EventContextBuilder withRepository(String id, String type, String flavour) {
+        setRepositoryContext( this.evt, id, type, flavour );
+        return this;
+    }
+
+    public Event apply() {
+        return this.evt;
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventHandler.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventHandler.java
new file mode 100644 (file)
index 0000000..f98adcd
--- /dev/null
@@ -0,0 +1,31 @@
+package org.apache.archiva.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import java.util.EventListener;
+
+/**
+ * A listener that accepts events.
+ */
+public interface EventHandler<T extends Event> extends EventListener {
+
+    void handle(T event);
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventSource.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventSource.java
new file mode 100644 (file)
index 0000000..452a5e9
--- /dev/null
@@ -0,0 +1,33 @@
+package org.apache.archiva.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A repository event source raises events to its registered listeners.
+ * Listeners register to event types that are structured hierarchical.
+ *
+ */
+public interface EventSource {
+
+    <T extends Event> void registerEventHandler( EventType<T> type, EventHandler<? super T> eventHandler);
+
+    <T extends Event> void unregisterEventHandler(EventType<T> type, EventHandler<? super T> eventHandler);
+
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventType.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/EventType.java
new file mode 100644 (file)
index 0000000..6927c81
--- /dev/null
@@ -0,0 +1,177 @@
+package org.apache.archiva.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.InvalidObjectException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Event types define a hierarchical structure of events. Each event is bound to a certain event type.
+ * All event types have a super type, only the root event type {@link EventType#ROOT} has no super type.
+ *
+ * Event types should be stored as static fields on the events itself.
+ *
+ * @param <T> The type class parameter allows to define the types in a type safe way and represents a event class,
+ *           where the type is associated to.
+ */
+public class EventType<T extends Event> implements Serializable  {
+
+
+    public static final EventType<Event> ROOT = new EventType<>();
+
+    private final String name;
+    private final EventType<? super T> superType;
+    private WeakHashMap<EventType<? extends T>, Void> subTypes;
+
+    /**
+     * Creates a type with the given name and the root type as parent.
+     * @param name the name of the new type
+     */
+    public EventType(String name) {
+        this.superType = ROOT;
+        this.name = name;
+    }
+
+    /**
+     * Creates a event type instance with the given super type and name.
+     *
+     * @param superType The super type or <code>null</code>, if this is the root type.
+     * @param name
+     */
+    public EventType(EventType<? super T> superType, String name) {
+        if (superType==null) {
+            throw new NullPointerException("Super Type may not be null");
+        }
+        this.name = name;
+        this.superType = superType;
+        superType.register(this);
+    }
+
+    /**
+     * Creates the root type
+     */
+    private EventType() {
+        this.name="ROOT";
+        this.superType=null;
+    }
+
+    public String name() {
+        return name;
+    }
+
+    public EventType<? super T> getSuperType() {
+        return superType;
+    }
+
+    public String getPath() {
+        List<String> path = new ArrayList<>( );
+        EventType eventType = this;
+        while(eventType!=ROOT)
+        {
+            path.add( eventType.name( ) );
+            eventType = this.getSuperType( );
+        }
+        Collections.reverse( path );
+        return String.join( "/", path );
+    }
+
+    private void register(EventType<? extends T> subType) {
+        if (subTypes == null) {
+            subTypes = new WeakHashMap<>();
+        }
+        for (EventType<? extends T> t : subTypes.keySet()) {
+            if (((t.name == null && subType.name == null) || (t.name != null && t.name.equals(subType.name)))) {
+                throw new IllegalArgumentException("EventType \"" + subType + "\""
+                        + "with parent \"" + subType.getSuperType()+"\" already exists");
+            }
+        }
+        subTypes.put(subType, null);
+    }
+
+
+    public static List<EventType<?>> fetchSuperTypes(EventType<?> type) {
+        List<EventType<?>> typeList = new ArrayList<>();
+        EventType<?> cType = type;
+        while (cType!=null) {
+            typeList.add(cType);
+            cType = cType.getSuperType();
+        }
+        return typeList;
+    }
+
+    public static boolean isInstanceOf(EventType<?> type, EventType<?> baseType) {
+        EventType<?> cType = type;
+        while(cType!=null) {
+            if (cType == baseType) {
+                return true;
+            }
+            cType = cType.getSuperType();
+        }
+        return false;
+    }
+
+
+    private Object writeReplace() throws ObjectStreamException {
+        Deque<String> path = new LinkedList<>();
+        EventType<?> t = this;
+        while (t != ROOT) {
+            path.addFirst(t.name);
+            t = t.superType;
+        }
+        return new EventTypeSerialization(new ArrayList<>(path));
+    }
+
+    static class EventTypeSerialization implements Serializable {
+        private static final long serialVersionUID = 1841649460281865547L;
+        private List<String> path;
+
+        public EventTypeSerialization(List<String> path) {
+            this.path = path;
+        }
+
+        private Object readResolve() throws ObjectStreamException {
+            EventType t = ROOT;
+            for (int i = 0; i < path.size(); ++i) {
+                String p = path.get(i);
+                if (t.subTypes != null) {
+                    EventType<?> s = findSubType(t.subTypes.keySet(), p);
+                    if (s == null) {
+                        throw new InvalidObjectException("Cannot find event type \"" + p + "\" (of " + t + ")");
+                    }
+                    t = s;
+                } else {
+                    throw new InvalidObjectException("Cannot find event type \"" + p + "\" (of " + t + ")");
+                }
+            }
+            return t;
+        }
+
+        private EventType<?> findSubType(Set<EventType> subTypes, String name) {
+            for (EventType t : subTypes) {
+                if (((t.name == null && name == null) || (t.name != null && t.name.equals(name)))) {
+                    return t;
+                }
+            }
+            return null;
+        }
+
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/RepositoryContext.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/RepositoryContext.java
new file mode 100644 (file)
index 0000000..4df023d
--- /dev/null
@@ -0,0 +1,90 @@
+package org.apache.archiva.event.context;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.event.EventContext;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This context provides repository data.
+ *
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public class RepositoryContext implements EventContext, Serializable
+{
+    private static final long serialVersionUID = -4172663291198878307L;
+
+    private static final String PREFIX = "repository";
+
+    private final String id;
+    private final String type;
+    private final String flavour;
+
+    public RepositoryContext( String id, String type, String flavour )
+    {
+        this.id = id;
+        this.type = type;
+        this.flavour = flavour;
+    }
+
+    /**
+     * Returns the repository id
+     * @return the repository id
+     */
+    public String getId( )
+    {
+        return id;
+    }
+
+    /**
+     * Returns the repository type (e.g. MAVEN)
+     * @return the string representation of the repository type
+     */
+    public String getType( )
+    {
+        return type;
+    }
+
+    /**
+     * Returns the repository flavour (e.g. Remote, Managed, Group)
+     * @return
+     */
+    public String getFlavour( )
+    {
+        return flavour;
+    }
+
+    @Override
+    public Map<String, String> getData( )
+    {
+        Map<String, String> values = new HashMap<>( );
+        values.put( PREFIX+".id", id );
+        values.put( PREFIX+".type", type );
+        values.put( PREFIX+".flavour", flavour );
+        return values;
+    }
+
+    @Override
+    public String getPrefix( )
+    {
+        return PREFIX;
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/RestContext.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/RestContext.java
new file mode 100644 (file)
index 0000000..615759d
--- /dev/null
@@ -0,0 +1,96 @@
+package org.apache.archiva.event.context;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.event.EventContext;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides information about a REST call.
+ *
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public class RestContext implements EventContext, Serializable
+{
+    private static final long serialVersionUID = -4109505194250928317L;
+
+    public static final String PREFIX = "rest";
+
+    private final String service;
+    private final String path;
+    private final String operation;
+    private final List<String> parameters;
+    private final int resultCode;
+
+
+    public RestContext( String service, String path, String operation, int resultCode, String... parameters )
+    {
+        this.service = service;
+        this.path = path;
+        this.operation = operation;
+        this.resultCode = resultCode;
+        this.parameters = Arrays.asList( parameters );
+    }
+
+    public String getService( )
+    {
+        return service;
+    }
+
+    public String getPath( )
+    {
+        return path;
+    }
+
+    public String getOperation( )
+    {
+        return operation;
+    }
+
+    public List<String> getParameters( )
+    {
+        return parameters;
+    }
+
+    public int getResultCode( )
+    {
+        return resultCode;
+    }
+
+    @Override
+    public Map<String, String> getData( )
+    {
+        Map<String, String> values = new HashMap<>( );
+        values.put( PREFIX+".service", service );
+        values.put( PREFIX+".path", path );
+        values.put( PREFIX+".operation", operation );
+        values.put( PREFIX+".parameter", String.join( ",", parameters ) );
+        return values;
+    }
+
+    @Override
+    public String getPrefix( )
+    {
+        return PREFIX;
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/UserContext.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/context/UserContext.java
new file mode 100644 (file)
index 0000000..8a83c73
--- /dev/null
@@ -0,0 +1,71 @@
+package org.apache.archiva.event.context;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.event.EventContext;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This context provides user information.
+ *
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public class UserContext implements EventContext, Serializable
+{
+    private static final long serialVersionUID = -3499164111736559781L;
+
+    private static final String PREFIX = "user";
+
+    private final String userId;
+    private final String remoteAddress;
+
+    public UserContext( String user, String remoteAddress )
+    {
+        this.userId = user == null ? "" : user;
+        this.remoteAddress = remoteAddress == null ? "" : remoteAddress;
+
+    }
+
+    public String getUserId( )
+    {
+        return userId;
+    }
+
+    public String getRemoteAddress( )
+    {
+        return remoteAddress;
+    }
+
+    @Override
+    public Map<String, String> getData( )
+    {
+        Map<String, String> values = new HashMap<>( );
+        values.put( PREFIX+".user_id", userId );
+        values.put( PREFIX+".remote_address", remoteAddress );
+        return values;
+    }
+
+    @Override
+    public String getPrefix( )
+    {
+        return PREFIX;
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/package-info.java b/archiva-modules/archiva-base/archiva-event-api/src/main/java/org/apache/archiva/event/package-info.java
new file mode 100644 (file)
index 0000000..30db350
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This module provides an event mechanism for all archiva subsystems.
+ *
+ * The events are hierarchical organized. That means each subsystem has its own event manager that collects events
+ * and processes and forwards them to the parent event manager (normally the central event manager).
+ * Each event manager clones the event and stores the origin event in the chain before forwarding them to the parent manager.
+ *
+ * Event Types are also hierarchical. There is one special type {@link org.apache.archiva.event.EventType#ROOT} that is the
+ * root type and has no parent type. All other types must be descendants of the ROOT type.
+ *
+ * Event types may have certain methods to access context information. But context information can also be accessed in a
+ * subsystem independent way using the event context data. Event contexts provide access to data without using the
+ * subsystem API and classes.
+ * Event types may be used for filtering events.
+ *
+ * @since 3.0
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+package org.apache.archiva.event;
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-event-api/src/test/java/org/apache/archiva/event/BasicEventManagerTest.java b/archiva-modules/archiva-base/archiva-event-api/src/test/java/org/apache/archiva/event/BasicEventManagerTest.java
new file mode 100644 (file)
index 0000000..6e0ba61
--- /dev/null
@@ -0,0 +1,139 @@
+package org.apache.archiva.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class BasicEventManagerTest
+{
+
+    private class TestHandler implements EventHandler<Event> {
+
+        private List<Event> eventList = new ArrayList<>( );
+        @Override
+        public void handle( Event event )
+        {
+            eventList.add( event );
+        }
+
+        public List<Event> getEventList() {
+            return eventList;
+        }
+    }
+
+    private EventType<Event> testType = new EventType<>( "TEST" );
+    private EventType<Event> testTestType = new EventType<>( testType,"TEST.TEST" );
+    private EventType<Event> otherType = new EventType( "OTHER" );
+
+    @Test
+    public void registerEventHandler( )
+    {
+        BasicEventManager eventManager = new BasicEventManager( this );
+        TestHandler handler1 = new TestHandler( );
+        TestHandler handler2 = new TestHandler( );
+        TestHandler handler3 = new TestHandler( );
+        TestHandler handler4 = new TestHandler( );
+
+        eventManager.registerEventHandler( Event.ANY, handler1 );
+        eventManager.registerEventHandler( testType, handler2 );
+        eventManager.registerEventHandler( testTestType, handler3 );
+        eventManager.registerEventHandler( otherType, handler4 );
+
+        Event event1 = new Event( testType, this );
+        eventManager.fireEvent( event1 );
+        assertEquals( 1, handler1.eventList.size( ) );
+        assertEquals( 1, handler2.eventList.size( ) );
+        assertEquals( 0, handler3.eventList.size( ) );
+        assertEquals( 0, handler4.eventList.size( ) );
+
+        Event event2 = new Event( testTestType, event1 );
+        eventManager.fireEvent( event2 );
+        assertEquals( 2, handler1.eventList.size( ) );
+        assertEquals( 2, handler2.eventList.size( ) );
+        assertEquals( 1, handler3.eventList.size( ) );
+        assertEquals( 0, handler4.eventList.size( ) );
+
+        Event event3 = new Event( otherType, event1 );
+        eventManager.fireEvent( event3 );
+        assertEquals( 3, handler1.eventList.size( ) );
+        assertEquals( 2, handler2.eventList.size( ) );
+        assertEquals( 1, handler3.eventList.size( ) );
+        assertEquals( 1, handler4.eventList.size( ) );
+
+
+
+    }
+
+    @Test
+    public void unregisterEventHandler( )
+    {
+        BasicEventManager eventManager = new BasicEventManager( this );
+        TestHandler handler1 = new TestHandler( );
+        TestHandler handler2 = new TestHandler( );
+        TestHandler handler3 = new TestHandler( );
+        TestHandler handler4 = new TestHandler( );
+
+        eventManager.registerEventHandler( Event.ANY, handler1 );
+        eventManager.registerEventHandler( testType, handler2 );
+        eventManager.registerEventHandler( testTestType, handler3 );
+        eventManager.registerEventHandler( otherType, handler4 );
+
+        eventManager.unregisterEventHandler( Event.ANY, handler1 );
+        Event event1 = new Event( testType, this );
+        eventManager.fireEvent( event1 );
+        assertEquals( 0, handler1.eventList.size( ) );
+        assertEquals( 1, handler2.eventList.size( ) );
+        assertEquals( 0, handler3.eventList.size( ) );
+        assertEquals( 0, handler4.eventList.size( ) );
+
+        eventManager.unregisterEventHandler( otherType, handler2 );
+        Event event2 = new Event( testType, this );
+        eventManager.fireEvent( event2 );
+        assertEquals( 0, handler1.eventList.size( ) );
+        assertEquals( 2, handler2.eventList.size( ) );
+        assertEquals( 0, handler3.eventList.size( ) );
+        assertEquals( 0, handler4.eventList.size( ) );
+    }
+
+    @Test
+    public void fireEvent( )
+    {
+        Object other = new Object( );
+        BasicEventManager eventManager = new BasicEventManager( this );
+        assertThrows( NullPointerException.class, ( ) -> eventManager.fireEvent( null ) );
+        Event event = new Event( EventType.ROOT, other );
+        assertEquals( other, event.getSource( ) );
+        TestHandler handler = new TestHandler( );
+        eventManager.registerEventHandler( EventType.ROOT, handler );
+        eventManager.fireEvent( event );
+        assertEquals( 1, handler.getEventList( ).size( ) );
+        Event newEvent = handler.getEventList( ).get( 0 );
+        assertNotEquals( event, newEvent );
+        assertEquals( this, newEvent.getSource( ) );
+
+    }
+}
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-event-central/pom.xml b/archiva-modules/archiva-base/archiva-event-central/pom.xml
new file mode 100644 (file)
index 0000000..cd8a5b0
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>archiva-base</artifactId>
+    <groupId>org.apache.archiva</groupId>
+    <version>3.0.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.archiva.event</groupId>
+  <artifactId>archiva-event-central</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-event-central/src/main/java/org/apache/archiva/event/central/CentralEventManager.java b/archiva-modules/archiva-base/archiva-event-central/src/main/java/org/apache/archiva/event/central/CentralEventManager.java
new file mode 100644 (file)
index 0000000..44ba9df
--- /dev/null
@@ -0,0 +1,42 @@
+package org.apache.archiva.event.central;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.event.AbstractEventManager;
+import org.apache.archiva.event.Event;
+import org.apache.archiva.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+/**
+ * Event manager that collects all events from archiva subsystems.
+ *
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+@Service("eventManager#archiva")
+public class CentralEventManager extends AbstractEventManager implements EventHandler<Event>
+{
+    private static final Logger log = LoggerFactory.getLogger( CentralEventManager.class );
+
+    @Override
+    public void handle( Event event )
+    {
+        log.info( "Event: type={}, sourceClass={}, source={}", event.getType( ), event.getSource().getClass(), event.getSource() );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-event-central/src/main/resources/META-INF/spring-context.xml b/archiva-modules/archiva-base/archiva-event-central/src/main/resources/META-INF/spring-context.xml
new file mode 100644 (file)
index 0000000..654b2f8
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans.xsd
+           http://www.springframework.org/schema/context
+           http://www.springframework.org/schema/context/spring-context.xsd"
+       default-lazy-init="true">
+
+  <context:annotation-config />
+  <context:component-scan base-package="org.apache.archiva.event.central"/>
+
+</beans>
\ No newline at end of file
index 8c231f2c1183404831d7e8761530d619ecd77bc1..b34922d17cba5f1d0cbf46af47eae6c61cf4df2b 100644 (file)
   </properties>
 
   <dependencies>
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.archiva.configuration</groupId>
       <artifactId>archiva-configuration-provider</artifactId>
index 4ae27d9e3fb0d0c89f3f5e0032e38229dcc6e00e..820525f9cefc211ed2b1140165ecf53678099372 100644 (file)
 
   <dependencies>
 
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-common</artifactId>
index 8ed6c37da020d91a4a790506d17e2d55fd045494..4b877d9057fc088d6c0ddf676bc5340a65095491 100644 (file)
@@ -277,11 +277,11 @@ public interface RepositoryHandler<R extends Repository, C extends AbstractRepos
      * Returns the repository variant, this handler manages.
      * @return the concrete variant class
      */
-    Class<R> getVariant();
+    Class<R> getFlavour();
 
     /**
      * Returns the repository configuration variant, this handler manages.
      * @return the concrete configuration variant class
      */
-    Class<C> getConfigurationVariant();
+    Class<C> getConfigurationFlavour();
 }
index 88c95adde778c61621094ca89215f5575faa9c2b..a4b667b3caff23ed5f1b6167a26db791b5d19e30 100644 (file)
@@ -20,8 +20,12 @@ package org.apache.archiva.repository.event;
  */
 
 import org.apache.archiva.event.Event;
+import org.apache.archiva.event.EventContextBuilder;
 import org.apache.archiva.event.EventType;
+import org.apache.archiva.repository.ManagedRepository;
+import org.apache.archiva.repository.RemoteRepository;
 import org.apache.archiva.repository.Repository;
+import org.apache.archiva.repository.RepositoryGroup;
 
 /**
  * A repository event is specific to a repository and holds a reference to the repository that
@@ -39,6 +43,12 @@ public class RepositoryEvent extends Event
     public RepositoryEvent(EventType<? extends RepositoryEvent> type, Object origin, Repository repository) {
         super(type, origin);
         this.repository = repository;
+        EventContextBuilder builder = EventContextBuilder.withEvent( this );
+        if (repository!=null)
+        {
+            builder.withRepository( repository.getId( ), repository.getType( ).name( ), getFlavour( repository ) );
+        }
+        builder.apply( );
     }
 
     public Repository getRepository() {
@@ -49,4 +59,16 @@ public class RepositoryEvent extends Event
     public EventType<? extends RepositoryEvent> getType() {
         return (EventType<? extends RepositoryEvent>) super.getType();
     }
+
+    private String getFlavour(Repository repository) {
+        if (repository instanceof RemoteRepository ) {
+            return RemoteRepository.class.getName( );
+        } else if (repository instanceof ManagedRepository ) {
+            return ManagedRepository.class.getName( );
+        } else if ( repository instanceof RepositoryGroup ) {
+            return RepositoryGroup.class.getName( );
+        } else {
+            return "UNKNOWN";
+        }
+    }
 }
index b9b6f45588d76c85b4890332598b082e4e22773b..8c424f6de25b65fee3afaeb9dcd4064e04e9b293 100644 (file)
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-common</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-central</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
index ca15e5f31ab2b80cba0a4cd59a9f5d026de0c26f..7a10b7387a1ccb62f9dd1797734290b6c110e69e 100644 (file)
@@ -24,7 +24,7 @@ import com.cronutils.model.definition.CronDefinition;
 import com.cronutils.model.definition.CronDefinitionBuilder;
 import org.apache.archiva.event.Event;
 import org.apache.archiva.event.EventHandler;
-import org.apache.archiva.event.EventManager;
+import org.apache.archiva.event.BasicEventManager;
 import org.apache.archiva.event.EventType;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.repository.EditableRepository;
@@ -86,7 +86,7 @@ public abstract class AbstractRepository implements EditableRepository, EventHan
     public static final CronDefinition CRON_DEFINITION = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);
     private RepositoryState state;
 
-    private final EventManager eventManager;
+    private final BasicEventManager eventManager;
 
     Map<Class<? extends RepositoryFeature<?>>, RepositoryFeature<?>> featureMap = new HashMap<>(  );
 
@@ -100,7 +100,7 @@ public abstract class AbstractRepository implements EditableRepository, EventHan
         this.storage = repositoryStorage;
         this.location = repositoryStorage.getLocation();
         this.openStatus.compareAndSet(false, true);
-        this.eventManager = new EventManager(this);
+        this.eventManager = new BasicEventManager(this);
     }
 
     public AbstractRepository(Locale primaryLocale, RepositoryType type, String id, String name, RepositoryStorage repositoryStorage) {
@@ -111,7 +111,7 @@ public abstract class AbstractRepository implements EditableRepository, EventHan
         this.storage = repositoryStorage;
         this.location = repositoryStorage.getLocation();
         this.openStatus.compareAndSet(false, true);
-        this.eventManager = new EventManager(this);
+        this.eventManager = new BasicEventManager(this);
     }
 
     protected void setPrimaryLocale(Locale locale) {
index 0e17aacd6428c55c08453fb8c421c0d354d0830c..2ffeac28155e9610105db834220874874744568c 100644 (file)
@@ -22,7 +22,7 @@ import org.apache.archiva.configuration.model.AbstractRepositoryConfiguration;
 import org.apache.archiva.configuration.model.Configuration;
 import org.apache.archiva.configuration.provider.IndeterminateConfigurationException;
 import org.apache.archiva.event.Event;
-import org.apache.archiva.event.EventManager;
+import org.apache.archiva.event.BasicEventManager;
 import org.apache.archiva.event.EventType;
 import org.apache.archiva.repository.EditableRepository;
 import org.apache.archiva.repository.Repository;
@@ -61,14 +61,14 @@ public abstract class AbstractRepositoryHandler<R extends Repository, C extends
     private CombinedValidator<R> combinedValidator;
     private final Class<R> repositoryClazz;
     private final Class<C> configurationClazz;
-    private final EventManager eventManager;
+    private final BasicEventManager eventManager;
     private final Map<String, R> repositoryMap  = new HashMap<>(  );
     private final ConfigurationHandler configurationHandler;
 
     public AbstractRepositoryHandler(Class<R> repositoryClazz, Class<C> configurationClazz, ConfigurationHandler configurationHandler) {
         this.repositoryClazz = repositoryClazz;
         this.configurationClazz = configurationClazz;
-        this.eventManager = new EventManager( this );
+        this.eventManager = new BasicEventManager( this );
         this.configurationHandler = configurationHandler;
     }
 
@@ -142,13 +142,13 @@ public abstract class AbstractRepositoryHandler<R extends Repository, C extends
     }
 
     @Override
-    public Class<R> getVariant( )
+    public Class<R> getFlavour( )
     {
         return this.repositoryClazz;
     }
 
     @Override
-    public Class<C> getConfigurationVariant( )
+    public Class<C> getConfigurationFlavour( )
     {
         return this.configurationClazz;
     }
index 1d0c32207dab3ec57014f186bce95c9458dd76d8..c3a3c0b0573708bb74f94964ec46fef47375595b 100644 (file)
@@ -31,8 +31,10 @@ import org.apache.archiva.configuration.model.RemoteRepositoryConfiguration;
 import org.apache.archiva.configuration.model.RepositoryGroupConfiguration;
 import org.apache.archiva.event.Event;
 import org.apache.archiva.event.EventHandler;
-import org.apache.archiva.event.EventManager;
+import org.apache.archiva.event.BasicEventManager;
+import org.apache.archiva.event.EventSource;
 import org.apache.archiva.event.EventType;
+import org.apache.archiva.event.central.CentralEventManager;
 import org.apache.archiva.indexer.ArchivaIndexManager;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.indexer.IndexCreationFailedException;
@@ -65,6 +67,7 @@ import org.springframework.stereotype.Service;
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
+import javax.inject.Named;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -111,9 +114,13 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa
     @Inject
     List<RepositoryValidator<? extends Repository>> repositoryValidatorList;
 
+    @Inject
+    @Named("eventManager#archiva")
+    CentralEventManager centralEventManager;
+
     private boolean ignoreIndexing = false;
 
-    private final EventManager eventManager;
+    private final BasicEventManager eventManager;
 
 
     private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock( );
@@ -133,7 +140,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa
 
     public ArchivaRepositoryRegistry( ConfigurationHandler configurationHandler, List<RepositoryValidator<? extends Repository>> validatorList )
     {
-        this.eventManager = new EventManager( this );
+        this.eventManager = new BasicEventManager( this );
         this.configurationHandler = configurationHandler;
         this.validators = initValidatorList( validatorList );
     }
@@ -172,6 +179,7 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa
                 provider.addRepositoryEventHandler( this );
             }
             this.configurationHandler.addListener( this );
+            registerEventHandler( EventType.ROOT, centralEventManager );
         }
         finally
         {
@@ -1162,15 +1170,15 @@ public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHa
     @Override
     public void registerHandler( RepositoryHandler<?, ?> handler )
     {
-        if ( handler.getVariant( ).isAssignableFrom( RepositoryGroup.class ) )
+        if ( handler.getFlavour( ).isAssignableFrom( RepositoryGroup.class ) )
         {
             registerGroupHandler( (RepositoryHandler<RepositoryGroup, RepositoryGroupConfiguration>) handler );
         }
-        else if ( handler.getVariant( ).isAssignableFrom( ManagedRepository.class ) )
+        else if ( handler.getFlavour( ).isAssignableFrom( ManagedRepository.class ) )
         {
             registerManagedRepositoryHandler( (RepositoryHandler<ManagedRepository, ManagedRepositoryConfiguration>) handler );
         }
-        else if ( handler.getVariant().isAssignableFrom( RemoteRepository.class )) {
+        else if ( handler.getFlavour().isAssignableFrom( RemoteRepository.class )) {
             registerRemoteRepositoryHandler( (RepositoryHandler<RemoteRepository, RemoteRepositoryConfiguration>) handler );
         }
     }
index f7cf8a46f7bd57b4c3742b0ff40282fbd5c30095..b46c12a7900b93e4c53cda670eab687d32394807 100644 (file)
@@ -47,6 +47,8 @@ import org.springframework.stereotype.Service;
 import javax.annotation.PostConstruct;
 import javax.inject.Named;
 import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
index fb607e427d30775816d9e705da15e8003d9b1f21..be4933beb8b1e91f0e15eb2268646683f0f65fcc 100644 (file)
@@ -148,7 +148,7 @@ class RepositoryGroupHandlerTest
 
     private RepositoryGroupHandler createHandler( )
     {
-        Mockito.when( managedRepositoryHandler.getVariant( ) ).thenReturn( ManagedRepository.class );
+        Mockito.when( managedRepositoryHandler.getFlavour( ) ).thenReturn( ManagedRepository.class );
         final ManagedRepository internalRepo;
         try
         {
@@ -161,7 +161,7 @@ class RepositoryGroupHandlerTest
         Mockito.when( managedRepositoryHandler.get( ArgumentMatchers.eq("internal") ) ).thenReturn( internalRepo );
         repositoryRegistry.registerHandler( managedRepositoryHandler );
 
-        Mockito.when( remoteRepositoryHandler.getVariant( ) ).thenReturn( RemoteRepository.class );
+        Mockito.when( remoteRepositoryHandler.getFlavour( ) ).thenReturn( RemoteRepository.class );
         final RemoteRepository centralRepo;
         try
         {
index 55b0f87f15542db3111b8657095eae0d39a7f584..91c0278ca398fa982cd66dfa27f934f0af74eaa1 100644 (file)
@@ -33,6 +33,7 @@
     <site.staging.base>${project.parent.basedir}</site.staging.base>
   </properties>
   <modules>
+    <module>archiva-event-api</module>
     <module>archiva-test-utils</module>
     <module>archiva-common</module>
     <module>archiva-mock</module>
@@ -53,5 +54,6 @@
     <module>archiva-security-common</module>
     <module>archiva-storage-api</module>
     <module>archiva-storage-fs</module>
+    <module>archiva-event-central</module>
   </modules>
 </project>
index 7c042b5c7e79d3ec73af8125bb9b781408c362a1..1406524776d911c0c4400c0a0032ce7a0f67ac9d 100644 (file)
   </properties>
 
   <dependencies>
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-repository-api</artifactId>
index 8319e2704660b1ee76945f5ddbd3935feed24726..bb740960c4b6bde0da9d8aab385744ea2553c5db 100644 (file)
@@ -118,6 +118,16 @@ public class MavenRepositoryProvider implements RepositoryProvider {
         return repo;
     }
 
+    private Path getBaseDir(String location) {
+        String lPathStr = location == null ? "" : location;
+        Path lPath = Paths.get( lPathStr );
+        if (lPath.isAbsolute()) {
+            return lPath.getParent( );
+        } else {
+            return archivaConfiguration.getRepositoryBaseDir( ).resolve( lPath );
+        }
+    }
+
     @Override
     public MavenRemoteRepository createRemoteInstance(String id, String name) {
         return createRemoteInstance(id, name, archivaConfiguration.getRemoteRepositoryBaseDir());
@@ -194,7 +204,7 @@ public class MavenRepositoryProvider implements RepositoryProvider {
 
     @Override
     public ManagedRepository createManagedInstance(ManagedRepositoryConfiguration cfg) throws RepositoryException {
-        MavenManagedRepository repo = createManagedInstance(cfg.getId(), cfg.getName(), Paths.get(cfg.getLocation()).getParent());
+        MavenManagedRepository repo = createManagedInstance( cfg.getId( ), cfg.getName( ), getBaseDir( cfg.getLocation( ) ) );
         updateManagedInstance(repo, cfg);
         return repo;
     }
index 01ec552c0e656df39454e1986e97a1becce4c371..d3ce96819b93fdf58f596c345bba3a51e4538c84 100644 (file)
@@ -73,37 +73,6 @@ public class MavenManagedRepository extends Repository
         super.setType( RepositoryType.MAVEN.name( ) );
     }
 
-    protected static void update(MavenManagedRepository repo, ManagedRepository beanRepo) {
-        repo.setDescription( beanRepo.getDescription() );
-        repo.setId( beanRepo.getId() );
-        repo.setIndex( true );
-        repo.setLayout( beanRepo.getLayout() );
-        repo.setBlocksRedeployments( beanRepo.blocksRedeployments() );
-        repo.setReleaseSchemes( beanRepo.getActiveReleaseSchemes().stream().map( Objects::toString).collect( Collectors.toList()) );
-        repo.setLocation( beanRepo.getLocation().toString() );
-        repo.setName( beanRepo.getName());
-        repo.setScanned( beanRepo.isScanned() );
-        repo.setSchedulingDefinition( beanRepo.getSchedulingDefinition() );
-        ArtifactCleanupFeature artifactCleanupFeature = beanRepo.getFeature( ArtifactCleanupFeature.class );
-        repo.setDeleteSnapshotsOfRelease( artifactCleanupFeature.isDeleteReleasedSnapshots());
-        repo.setRetentionCount( artifactCleanupFeature.getRetentionCount());
-        repo.setRetentionPeriod( artifactCleanupFeature.getRetentionPeriod() );
-        IndexCreationFeature icf = beanRepo.getFeature( IndexCreationFeature.class );
-        repo.setIndex( icf.hasIndex( ) );
-        repo.setIndexPath( icf.getIndexPath( ).getPath( ) );
-        repo.setPackedIndexPath( icf.getPackedIndexPath( ).getPath( ) );
-        repo.setSkipPackedIndexCreation( icf.isSkipPackedIndexCreation() );
-        StagingRepositoryFeature srf = beanRepo.getFeature( StagingRepositoryFeature.class );
-        repo.setHasStagingRepository( srf.isStageRepoNeeded( ) );
-        repo.setStagingRepository( srf.getStagingRepository()!=null?srf.getStagingRepository().getId():"" );
-    }
-
-    public static MavenManagedRepository of( ManagedRepository beanRepo ) {
-        MavenManagedRepository repo = new MavenManagedRepository( );
-        update( repo, beanRepo );
-        return repo;
-    }
-
     @Schema(name="blocks_redeployments",description = "True, if redeployments to this repository are not allowed")
     public boolean isBlocksRedeployments( )
     {
index 5536ae9b04d2c71a7626c1cb0ad9c301bfbb6759..1fcb1a0e301f0e79bffd54f81932bd12d742948c 100644 (file)
@@ -17,6 +17,7 @@ package org.apache.archiva.rest.api.v2.model;
  * under the License.
  */
 
+import io.swagger.v3.oas.annotations.media.Schema;
 import org.apache.archiva.repository.ManagedRepository;
 
 import java.io.Serializable;
@@ -24,17 +25,13 @@ import java.io.Serializable;
 /**
  * @author Martin Stockhammer <martin_s@apache.org>
  */
+@Schema(name="MavenManagedRepositoryUpdate",description = "Data object for updating maven managed repositories")
 public class MavenManagedRepositoryUpdate extends MavenManagedRepository implements Serializable
 {
     private static final long serialVersionUID = -9181643343284109862L;
     private boolean resetStats = false;
 
-    public static MavenManagedRepositoryUpdate of( ManagedRepository repository ) {
-        MavenManagedRepositoryUpdate repo = new MavenManagedRepositoryUpdate( );
-        update( repo, repository );
-        return repo;
-    }
-
+    @Schema(name="reset_stats",description = "True, if statistics should be reset after update")
     public boolean isResetStats( )
     {
         return resetStats;
index 1e4ed68a0b354bd8ea3b64ac456c59a1eb819fb8..75be52388a0dc9d58a81f6c1ef64ec6beaee8f72 100644 (file)
@@ -64,14 +64,22 @@ public class MavenRepositoryMapper extends RestServiceMapper<MavenManagedReposit
         if (source.getLayout()!=null)
             target.setLayout( source.getLayout() );
         if (source.getLocation()!=null)
-            target.setLocation( source.getLocation() );
+        {
+            target.setLocation( source.getLocation( ) );
+        } else {
+            if (target.getLocation()==null) {
+                target.setLocation( "" );
+            }
+        }
+
         if (source.getPackedIndexPath()!=null)
             target.setPackedIndexDir( source.getPackedIndexPath() );
         if (source.getSchedulingDefinition()!=null)
             target.setRefreshCronExpression( source.getSchedulingDefinition() );
         target.setReleases( source.getReleaseSchemes( ).contains( ReleaseScheme.RELEASE.name() ) );
         target.setRetentionCount( source.getRetentionCount() );
-        target.setRetentionPeriod( source.getRetentionPeriod().getDays() );
+        if (source.getRetentionPeriod()!=null)
+            target.setRetentionPeriod( source.getRetentionPeriod().getDays() );
         target.setScanned( source.isScanned() );
         target.setSkipPackedIndexCreation( source.isSkipPackedIndexCreation() );
         target.setSnapshots( source.getReleaseSchemes( ).contains( ReleaseScheme.SNAPSHOT.name() ) );
index cc0a836a2614391906867134b9ed939e9907f19e..57e4d401ffe7006780b0a17740ca3f609676c256 100644 (file)
@@ -122,4 +122,10 @@ public interface ErrorKeys
      * When the operation needs authentication, but not authenticated user was found in the request context.
      */
     String NOT_AUTHENTICATED = PREFIX + "user.not_authenticated";
+
+    /**
+     * Repository add action failed
+     */
+    String REPOSITORY_ADD_FAILED = PREFIX + "add.failed";
+    
 }
index 0fd77703c757323329c17cf8fe7f14f367f0c579..3f0e04e12817b6d85f71f5b539e32cb4fb0865ca 100644 (file)
@@ -140,6 +140,51 @@ class MavenRepositoryMapperTest
         assertFalse( result.isSkipPackedIndexCreation( ) );
     }
 
+    @Test
+    void updateWithNullValues( )
+    {
+        MavenRepositoryMapper mapper = new MavenRepositoryMapper( );
+        MavenManagedRepository repo = new MavenManagedRepository( );
+        ManagedRepositoryConfiguration result = new ManagedRepositoryConfiguration( );
+        repo.setId( "repo01" );
+        repo.setName( "Repo 01" );
+        repo.setDescription( "This is repo 01" );
+        repo.setLocation( null );
+        repo.setHasStagingRepository( true );
+        repo.setSchedulingDefinition( "0,1,2 * * * *" );
+        repo.setPackedIndexPath( null );
+        repo.setIndexPath( null );
+        repo.setIndex( true );
+        repo.setDeleteSnapshotsOfRelease( false );
+        repo.setBlocksRedeployments( false );
+        repo.setReleaseSchemes( Arrays.asList( ReleaseScheme.RELEASE.name(), ReleaseScheme.SNAPSHOT.name() ) );
+        repo.setCharacteristic( Repository.CHARACTERISTIC_MANAGED );
+        repo.setScanned( true );
+        repo.setRetentionPeriod( null );
+        repo.setRetentionCount( 15 );
+        repo.setSkipPackedIndexCreation( false );
+        repo.setStagingRepository( null );
+        mapper.update( repo, result );
+
+        assertNotNull( result );
+        assertEquals( "repo01", result.getId( ) );
+        assertEquals( "Repo 01", result.getName( ) );
+        assertEquals( "This is repo 01", result.getDescription( ) );
+        assertNotNull( result.getLocation( ) );
+        assertTrue( result.isStageRepoNeeded( ) );
+        assertEquals( "0,1,2 * * * *", result.getRefreshCronExpression( ) );
+        assertEquals( "", result.getIndexDir( ) );
+        assertEquals( "", result.getPackedIndexDir( ) );
+        assertFalse( result.isDeleteReleasedSnapshots( ) );
+        assertFalse( result.isBlockRedeployments( ) );
+        assertTrue( result.isSnapshots( ) );
+        assertTrue( result.isReleases( ) );
+        assertTrue( result.isScanned( ) );
+        assertEquals( 100, result.getRetentionPeriod( ) );
+        assertEquals( 15, result.getRetentionCount( ) );
+        assertFalse( result.isSkipPackedIndexCreation( ) );
+    }
+
     @Test
     void reverseMap( ) throws IOException, URISyntaxException, UnsupportedURIException
     {
index 7dd8dad507b4c1e3ab30d6f15a7b14972d2750f0..519fb1e5b3ef6903d77b05e4ad959e375c43e0dc 100644 (file)
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-storage-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.archiva.event</groupId>
+      <artifactId>archiva-event-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-repository-admin-api</artifactId>
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/AbstractService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/v2/svc/AbstractService.java
new file mode 100644 (file)
index 0000000..09e367e
--- /dev/null
@@ -0,0 +1,51 @@
+package org.apache.archiva.rest.v2.svc;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.admin.model.AuditInformation;
+import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal;
+import org.apache.archiva.redback.rest.services.RedbackRequestInformation;
+import org.apache.archiva.redback.users.User;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Context;
+
+/**
+ * @author Martin Schreier <martin_s@apache.org>
+ */
+public class AbstractService
+{
+    @Context
+    private HttpServletRequest httpServletRequest;
+
+    protected AuditInformation getAuditInformation( )
+    {
+        RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get( );
+        User user;
+        String remoteAddr;
+        if (redbackRequestInformation==null) {
+            user = null;
+            remoteAddr = httpServletRequest.getRemoteAddr( );
+        } else
+        {
+            user = redbackRequestInformation.getUser( );
+            remoteAddr = redbackRequestInformation.getRemoteAddr( );
+        }
+        return new AuditInformation( user, remoteAddr );
+    }
+}
index 5bec54dff096479d610d85755467a92a53fd9d1b..406acff595d9f980d92827495c8c6f3456685464 100644 (file)
@@ -20,13 +20,12 @@ package org.apache.archiva.rest.v2.svc.maven;
 import org.apache.archiva.admin.model.AuditInformation;
 import org.apache.archiva.admin.model.RepositoryAdminException;
 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
+import org.apache.archiva.common.MultiModelMapper;
 import org.apache.archiva.components.rest.model.PagedResult;
 import org.apache.archiva.components.rest.util.QueryHelper;
 import org.apache.archiva.configuration.model.ManagedRepositoryConfiguration;
 import org.apache.archiva.redback.authentication.AuthenticationResult;
 import org.apache.archiva.redback.authorization.AuthorizationException;
-import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal;
-import org.apache.archiva.redback.rest.services.RedbackRequestInformation;
 import org.apache.archiva.redback.system.DefaultSecuritySession;
 import org.apache.archiva.redback.system.SecuritySession;
 import org.apache.archiva.redback.system.SecuritySystem;
@@ -36,6 +35,7 @@ import org.apache.archiva.redback.users.UserNotFoundException;
 import org.apache.archiva.repository.ManagedRepository;
 import org.apache.archiva.repository.ReleaseScheme;
 import org.apache.archiva.repository.Repository;
+import org.apache.archiva.repository.RepositoryException;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.content.ContentItem;
@@ -44,10 +44,12 @@ import org.apache.archiva.repository.storage.fs.FsStorageUtil;
 import org.apache.archiva.rest.api.v2.model.FileInfo;
 import org.apache.archiva.rest.api.v2.model.MavenManagedRepository;
 import org.apache.archiva.rest.api.v2.model.MavenManagedRepositoryUpdate;
+import org.apache.archiva.rest.api.v2.model.map.ServiceMapperFactory;
 import org.apache.archiva.rest.api.v2.svc.ArchivaRestServiceException;
 import org.apache.archiva.rest.api.v2.svc.ErrorKeys;
 import org.apache.archiva.rest.api.v2.svc.ErrorMessage;
 import org.apache.archiva.rest.api.v2.svc.maven.MavenManagedRepositoryService;
+import org.apache.archiva.rest.v2.svc.AbstractService;
 import org.apache.archiva.security.common.ArchivaRoleConstants;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -65,14 +67,14 @@ import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_READ_REPOSITORY;
 import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_ADD_ARTIFACT;
+import static org.apache.archiva.security.common.ArchivaRoleConstants.OPERATION_READ_REPOSITORY;
 
 /**
  * @author Martin Stockhammer <martin_s@apache.org>
  */
 @Service("v2.managedMavenRepositoryService#rest")
-public class DefaultMavenManagedRepositoryService implements MavenManagedRepositoryService
+public class DefaultMavenManagedRepositoryService extends AbstractService implements MavenManagedRepositoryService
 {
     @Context
     HttpServletResponse httpServletResponse;
@@ -80,6 +82,8 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit
     @Context
     UriInfo uriInfo;
 
+
+
     private static final Logger log = LoggerFactory.getLogger( DefaultMavenManagedRepositoryService.class );
     private static final QueryHelper<ManagedRepository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} );
     static
@@ -96,36 +100,20 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit
     private final ManagedRepositoryAdmin managedRepositoryAdmin;
     private final RepositoryRegistry repositoryRegistry;
     private final SecuritySystem securitySystem;
+    private final ServiceMapperFactory serviceMapperFactory;
+    private final MultiModelMapper<MavenManagedRepository, ManagedRepositoryConfiguration, ManagedRepository> mapper;
+
 
     public DefaultMavenManagedRepositoryService( SecuritySystem securitySystem,
                                                  RepositoryRegistry repositoryRegistry,
-                                                 ManagedRepositoryAdmin managedRepositoryAdmin )
+                                                 ManagedRepositoryAdmin managedRepositoryAdmin,
+                                                 ServiceMapperFactory serviceMapperFactory ) throws IllegalArgumentException
     {
         this.securitySystem = securitySystem;
         this.repositoryRegistry = repositoryRegistry;
         this.managedRepositoryAdmin = managedRepositoryAdmin;
-    }
-
-    protected AuditInformation getAuditInformation( )
-    {
-        RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get( );
-        User user;
-        String remoteAddr;
-        if (redbackRequestInformation==null) {
-            user = null;
-            remoteAddr = null;
-        } else
-        {
-            user = redbackRequestInformation.getUser( );
-            remoteAddr = redbackRequestInformation.getRemoteAddr( );
-        }
-        return new AuditInformation( user, remoteAddr );
-    }
-
-    public static ManagedRepositoryConfiguration toConfig(MavenManagedRepository repo) {
-        ManagedRepositoryConfiguration cfg = new ManagedRepositoryConfiguration( );
-        return cfg;
-
+        this.serviceMapperFactory = serviceMapperFactory;
+        this.mapper = serviceMapperFactory.getMapper( MavenManagedRepository.class, ManagedRepositoryConfiguration.class, ManagedRepository.class );
     }
 
     @Override
@@ -140,7 +128,7 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit
             final Comparator<ManagedRepository> comparator = QUERY_HELPER.getComparator( orderBy, order );
             int totalCount = Math.toIntExact( repos.stream( ).filter( queryFilter ).count( ) );
             return PagedResult.of( totalCount, offset, limit, repos.stream( ).filter( queryFilter ).sorted( comparator )
-                .map( MavenManagedRepository::of ).skip( offset ).limit( limit ).collect( Collectors.toList( ) ) );
+                .map( mapper::reverseMap ).skip( offset ).limit( limit ).collect( Collectors.toList( ) ) );
         }
         catch (ArithmeticException e) {
             log.error( "Invalid number of repositories detected." );
@@ -158,7 +146,7 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit
         if (repo.getType()!=RepositoryType.MAVEN) {
             throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_WRONG_TYPE, repositoryId, repo.getType().name() ), 404 );
         }
-        return MavenManagedRepository.of( repo );
+        return mapper.reverseMap( repo );
     }
 
     @Override
@@ -220,13 +208,14 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit
         }
         try
         {
-            managedRepositoryAdmin.addManagedRepository( convert( managedRepository ), managedRepository.hasStagingRepository(), getAuditInformation() );
+            repositoryRegistry.putRepository( mapper.map( managedRepository ) );
             httpServletResponse.setStatus( 201 );
-            return MavenManagedRepository.of( repositoryRegistry.getManagedRepository( repoId ) );
+            return mapper.reverseMap( repositoryRegistry.getManagedRepository( repoId ) );
         }
-        catch ( RepositoryAdminException e )
+        catch ( RepositoryException e )
         {
-            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_ADMIN_ERROR, e.getMessage( ) ) );
+            log.error( "Could not create repository: {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_ADD_FAILED, repoId ) );
         }
     }
 
@@ -241,7 +230,7 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit
             if (newRepo==null) {
                 throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_UPDATE_FAILED, repositoryId ) );
             }
-            return MavenManagedRepository.of( newRepo );
+            return mapper.reverseMap( newRepo );
         }
         catch ( RepositoryAdminException e )
         {
index 3dffabed1dfc2cfb67ebd3c899e9af7c4f99930e..d012d617535b674813695c81310632e16ebb78ee 100644 (file)
@@ -100,7 +100,6 @@ public class NativeMavenManagedRepositoryServiceTest extends AbstractNativeRestS
     }
 
 
-    @Disabled
     @Test
     @Order( 2 )
     void testCreateRepository() {
@@ -111,7 +110,7 @@ public class NativeMavenManagedRepositoryServiceTest extends AbstractNativeRestS
         assertNotNull( json );
         assertEquals( "repo001", json.get( "id" ) );
         assertEquals( "Repository 001", json.get( "name" ) );
-        assertEquals( "maven", json.get( "type" ) );
+        assertEquals( "MAVEN", json.get( "type" ) );
         assertEquals( "This is repository 001", json.get( "description" ) );
     }
 
diff --git a/pom.xml b/pom.xml
index 4eaa74622f00ff6d9ff4446e9b63d73f9dd2e8dc..ffcd4be772f97720203ec9ce73be9b0d9cf921f8 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       </dependency>
 
 
+      <dependency>
+        <groupId>org.apache.archiva.event</groupId>
+        <artifactId>archiva-event-api</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.archiva.event</groupId>
+        <artifactId>archiva-event-central</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.archiva.maven</groupId>
         <artifactId>archiva-maven-common</artifactId>