+++ /dev/null
-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");
- }
- }
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-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);
- }
- }
- }
- }
- }
-}
+++ /dev/null
-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);
-
-}
+++ /dev/null
-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;
- }
-
- }
-}
+++ /dev/null
-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
<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>
--- /dev/null
+<?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
--- /dev/null
+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);
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+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 );
+ }
+}
--- /dev/null
+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");
+ }
+ }
+}
--- /dev/null
+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();
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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);
+
+}
--- /dev/null
+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;
+ }
+
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+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() );
+ }
+}
--- /dev/null
+<?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
</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>
<dependencies>
+ <dependency>
+ <groupId>org.apache.archiva.event</groupId>
+ <artifactId>archiva-event-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.archiva</groupId>
<artifactId>archiva-common</artifactId>
* 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();
}
*/
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
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() {
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";
+ }
+ }
}
<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>
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;
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<>( );
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) {
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) {
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;
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;
}
}
@Override
- public Class<R> getVariant( )
+ public Class<R> getFlavour( )
{
return this.repositoryClazz;
}
@Override
- public Class<C> getConfigurationVariant( )
+ public Class<C> getConfigurationFlavour( )
{
return this.configurationClazz;
}
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;
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;
@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( );
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 );
}
provider.addRepositoryEventHandler( this );
}
this.configurationHandler.addListener( this );
+ registerEventHandler( EventType.ROOT, centralEventManager );
}
finally
{
@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 );
}
}
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;
private RepositoryGroupHandler createHandler( )
{
- Mockito.when( managedRepositoryHandler.getVariant( ) ).thenReturn( ManagedRepository.class );
+ Mockito.when( managedRepositoryHandler.getFlavour( ) ).thenReturn( ManagedRepository.class );
final ManagedRepository internalRepo;
try
{
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
{
<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>
<module>archiva-security-common</module>
<module>archiva-storage-api</module>
<module>archiva-storage-fs</module>
+ <module>archiva-event-central</module>
</modules>
</project>
</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>
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());
@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;
}
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( )
{
* under the License.
*/
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.archiva.repository.ManagedRepository;
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;
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() ) );
* 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";
+
}
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
{
<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>
--- /dev/null
+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 );
+ }
+}
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;
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;
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;
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;
@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
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
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." );
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
}
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 ) );
}
}
if (newRepo==null) {
throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_UPDATE_FAILED, repositoryId ) );
}
- return MavenManagedRepository.of( newRepo );
+ return mapper.reverseMap( newRepo );
}
catch ( RepositoryAdminException e )
{
}
- @Disabled
@Test
@Order( 2 )
void testCreateRepository() {
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" ) );
}
</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>