Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adding the API for exception control.

  • Loading branch information...
commit ac5bc0f402ca3a0d75a2acbb132b40e62bd3e0d9 1 parent fc30ed1
@LightGuard authored
View
39 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/BeforeHandles.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import java.lang.annotation.*;
+
+/**
+ * Marker annotation for a method to be considered an Exception Handler, handles the exception in the BEFORE
+ * traversal of the exception chain. Handlers are typically in the form of
+ * <code>public void ... (@BeforeHandles ... CaughtException<...> ...)</code> methods.
+ * If a method has a return type, it is ignored.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface BeforeHandles
+{
+ /**
+ * Precedence relative to callbacks for the same type
+ */
+ public abstract int ordinal() default 0;
+}
View
168 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/CaughtException.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import javax.enterprise.inject.Typed;
+
+/**
+ * Payload for an exception to be handled. This object is not immutable as small pieces of the state may be set by the
+ * handler.
+ *
+ * @param <T> Exception type this event represents
+ */
+@SuppressWarnings({"unchecked", "CdiManagedBeanInconsistencyInspection"})
+@Typed()
+public class CaughtException<T extends Throwable>
+{
+ /**
+ * Flow control enum. Used in the dispatcher to determine how to markHandled.
+ */
+ protected enum ExceptionHandlingFlow
+ {
+ HANDLED,
+ HANDLED_AND_CONTINUE,
+ SKIP_CAUSE,
+ ABORT,
+ THROW_ORIGINAL,
+ THROW
+ }
+
+ private final ExceptionStack exceptionStack;
+ private final T exception;
+ private boolean unmute;
+ private ExceptionHandlingFlow flow;
+ private Throwable throwNewException;
+ private final boolean beforeTraversal;
+ private final boolean markedHandled;
+
+
+ /**
+ * Initial state constructor.
+ *
+ * @param exceptionStack Information about the current exception and cause chain.
+ * @param beforeTraversal flag indicating the direction of the cause chain traversal
+ * @param handled flag indicating the exception has already been handled by a previous handler
+ * @throws IllegalArgumentException if exceptionStack is null
+ */
+ public CaughtException(final ExceptionStack exceptionStack, final boolean beforeTraversal, final boolean handled)
+ {
+ if (exceptionStack == null)
+ {
+ throw new IllegalArgumentException("null is not valid for exceptionStack");
+ }
+
+ this.exception = (T) exceptionStack.getCurrent();
+ this.exceptionStack = exceptionStack;
+ this.beforeTraversal = beforeTraversal;
+ this.markedHandled = handled;
+ this.flow = ExceptionHandlingFlow.HANDLED_AND_CONTINUE;
+ }
+
+ public T getException()
+ {
+ return this.exception;
+ }
+
+ /**
+ * Instructs the dispatcher to abort further processing of handlers.
+ */
+ public void abort()
+ {
+ this.flow = ExceptionHandlingFlow.ABORT;
+ }
+
+ /**
+ * Instructs the dispatcher to throw the original exception after handler processing.
+ */
+ public void throwOriginal()
+ {
+ this.flow = ExceptionHandlingFlow.THROW_ORIGINAL;
+ }
+
+ /**
+ * Instructs the dispatcher to terminate additional handler processing and mark the event as handled.
+ */
+ public void handled()
+ {
+ this.flow = ExceptionHandlingFlow.HANDLED;
+ }
+
+ /**
+ * Default instruction to dispatcher, continues handler processing.
+ */
+ public void handledAndContinue()
+ {
+ this.flow = ExceptionHandlingFlow.HANDLED_AND_CONTINUE;
+ }
+
+ /**
+ * Similar to {@link org.apache.deltaspike.core.api.exception.control.CaughtException#handledAndContinue()},
+ * but instructs the dispatcher to markHandled to the next element
+ * in the cause chain without processing additional handlers for this cause chain element.
+ */
+ public void skipCause()
+ {
+ this.flow = ExceptionHandlingFlow.SKIP_CAUSE;
+ }
+
+ /**
+ * Instructs the dispatcher to allow this handler to be invoked again.
+ */
+ public void unmute()
+ {
+ this.unmute = true;
+ }
+
+ protected boolean isUnmute()
+ {
+ return this.unmute;
+ }
+
+ /* Later
+ public ExceptionStack getExceptionStack() {
+ }
+ */
+
+ protected ExceptionHandlingFlow getFlow()
+ {
+ return this.flow;
+ }
+
+ public boolean isMarkedHandled()
+ {
+ return this.isMarkedHandled();
+ }
+
+ /**
+ * Rethrow the exception, but use the given exception instead of the original.
+ *
+ * @param t Exception to be thrown in place of the original.
+ */
+ public void rethrow(Throwable t)
+ {
+ this.throwNewException = t;
+ this.flow = ExceptionHandlingFlow.THROW;
+ }
+
+ protected Throwable getThrowNewException()
+ {
+ return this.throwNewException;
+ }
+}
View
32 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/ExceptionHandler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import java.lang.annotation.*;
+
+/**
+ * Marker for types containing Exception Handler methods.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface ExceptionHandler
+{
+}
View
186 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/ExceptionStack.java
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import javax.enterprise.inject.Typed;
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+
+/**
+ * Information about the current exception and exception cause container. This object is not immutable.
+ */
+@Typed()
+public class ExceptionStack implements Serializable
+{
+ private boolean root;
+ private boolean last;
+ private int initialStackSize;
+ private Throwable next;
+ private Collection<ExceptionStackItem> remaining;
+ private Deque<ExceptionStackItem> exceptionStackItems;
+ // private Deque<ExceptionStackItem> origExceptionStackItems; // TODO: Later
+ private Collection<Throwable> causes;
+ private Throwable current;
+
+ /**
+ * Builds the stack from the given exception.
+ *
+ * @param exception Caught exception
+ */
+ public ExceptionStack(final Throwable exception)
+ {
+ if (exception == null)
+ {
+ throw new IllegalArgumentException("exception must not be null");
+ }
+
+ Throwable e = exception;
+ this.exceptionStackItems = new ArrayDeque<ExceptionStackItem>();
+
+ do
+ {
+ this.exceptionStackItems.addFirst(new ExceptionStackItem(e));
+ if (e instanceof SQLException)
+ {
+ SQLException sqlException = (SQLException) e;
+
+ while (sqlException.getNextException() != null)
+ {
+ sqlException = sqlException.getNextException();
+ this.exceptionStackItems.addFirst(new ExceptionStackItem(sqlException));
+ }
+ }
+ }
+ while ((e = e.getCause()) != null);
+
+ this.initialStackSize = this.exceptionStackItems.size();
+ this.causes = this.createThrowableCollectionFrom(exceptionStackItems);
+ // TODO: Later this.origExceptionStackItems = new ArrayDeque<ExceptionStackItem>(exceptionStackItems);
+ this.init();
+
+ }
+
+ private void init()
+ {
+ this.root = this.exceptionStackItems.size() == this.initialStackSize;
+
+ if (!this.exceptionStackItems.isEmpty())
+ {
+ this.current = this.exceptionStackItems.removeFirst().getThrowable();
+ this.remaining = Collections.unmodifiableCollection(this.exceptionStackItems);
+ } else
+ {
+ this.remaining = Collections.emptyList();
+ this.current = null;
+ }
+
+ this.last = this.remaining.isEmpty();
+ this.next = (this.last) ? null : this.exceptionStackItems.peekFirst().getThrowable();
+ }
+
+ private Collection<ExceptionStackItem> createExceptionStackCollectionFrom(Collection<Throwable> throwables)
+ {
+ final Deque<ExceptionStackItem> returningCollection = new ArrayDeque<ExceptionStackItem>(throwables.size());
+
+ for (Throwable t : throwables)
+ {
+ returningCollection.addFirst(new ExceptionStackItem(t));
+ }
+
+ return returningCollection;
+ }
+
+ private Collection<Throwable> createThrowableCollectionFrom(final Collection<ExceptionStackItem> exceptionStackItems)
+ {
+ final Deque<Throwable> returningCollection = new ArrayDeque<Throwable>(exceptionStackItems.size() + 1); // allow current
+
+ for (ExceptionStackItem item : exceptionStackItems)
+ {
+ returningCollection.addFirst(item.getThrowable());
+ }
+
+ return returningCollection;
+ }
+
+ public Collection<Throwable> getCauseElements()
+ {
+ return Collections.unmodifiableCollection(this.causes);
+ }
+
+ /**
+ * Test if iteration is finished
+ *
+ * @return finished with iteration
+ */
+ public boolean isLast()
+ {
+ return this.last;
+ }
+
+ public Throwable getNext()
+ {
+ return this.next;
+ }
+
+ public Collection<Throwable> getRemaining()
+ {
+ return Collections.unmodifiableCollection(this.createThrowableCollectionFrom(this.remaining));
+ }
+
+ /**
+ * Tests if the current exception is the root exception
+ *
+ * @return Returns true if iteration is at the root exception (top of the inverted stack)
+ */
+ public boolean isRoot()
+ {
+ return this.root;
+ }
+
+ /**
+ * Current exception in the iteration
+ *
+ * @return current exception
+ */
+ public Throwable getCurrent()
+ {
+ return this.current;
+ }
+
+ void setCauseElements(Collection<Throwable> elements)
+ {
+ this.exceptionStackItems = new ArrayDeque<ExceptionStackItem>(this.createExceptionStackCollectionFrom(elements));
+ this.init();
+ }
+
+ /**
+ * Done later
+ * The original exception stack if it has been changed.
+ *
+ * @return The original exception stack
+
+ public Deque<ExceptionStackItem> getOrigExceptionStackItems() {
+ }
+ */
+}
View
102 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/ExceptionStackItem.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import javax.enterprise.inject.Typed;
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Container for the exception and it's stack trace.
+ */
+@SuppressWarnings("CdiManagedBeanInconsistencyInspection")
+@Typed()
+public final class ExceptionStackItem implements Serializable
+{
+ private static final long serialVersionUID = 5162936095781886477L;
+
+ final private Throwable throwable;
+ final private StackTraceElement[] stackTraceElements;
+
+ public ExceptionStackItem(final Throwable cause)
+ {
+ this(cause, cause.getStackTrace());
+ }
+
+ public ExceptionStackItem(Throwable throwable, StackTraceElement[] stackTraceElements)
+ {
+ this.stackTraceElements = stackTraceElements.clone();
+ this.throwable = throwable;
+ }
+
+ public StackTraceElement[] getStackTraceElements()
+ {
+ return this.stackTraceElements.clone();
+ }
+
+ public Throwable getThrowable()
+ {
+ return this.throwable;
+ }
+
+ @Override
+ public String toString()
+ {
+ return new StringBuilder().
+ append("throwable: ").append(throwable).append(", ").
+ append("stackTraceElements: ").append(stackTraceElements).
+ toString();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ ExceptionStackItem that = (ExceptionStackItem) o;
+
+ if (!Arrays.equals(stackTraceElements, that.stackTraceElements))
+ {
+ return false;
+ }
+ if (!throwable.equals(that.throwable))
+ {
+ return false;
+ }
+
+ return true;
+
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = throwable.hashCode();
+ result = 31 * result + Arrays.hashCode(stackTraceElements);
+ return result;
+ }
+}
View
97 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/ExceptionToCatch.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import javax.enterprise.inject.Typed;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Entry point event into the Catch system. This object is nearly immutable, the only mutable portion
+ * is the handled flag.
+ */
+@SuppressWarnings("CdiManagedBeanInconsistencyInspection")
+@Typed()
+public class ExceptionToCatch implements Serializable
+{
+ private static final long serialVersionUID = 2646115104528108266L;
+
+ private Throwable exception;
+ private boolean handled;
+ private transient Set<Annotation> qualifiers;
+
+ /**
+ * Constructor that adds qualifiers for the handler(s) to run.
+ * Typically only integrators will be using this constructor.
+ *
+ * @param exception Exception to handle
+ * @param qualifiers qualifiers to use to narrow the handlers called
+ */
+ public ExceptionToCatch(Throwable exception, Annotation... qualifiers)
+ {
+ this.exception = exception;
+ this.qualifiers = new HashSet<Annotation>();
+ Collections.addAll(this.qualifiers, qualifiers);
+ }
+
+ /**
+ * Basic constructor without any qualifiers defined.
+ *
+ * @param exception Exception to handle.
+ */
+ public ExceptionToCatch(Throwable exception)
+ {
+ this.exception = exception;
+ this.qualifiers = Collections.emptySet();
+ }
+
+ public Throwable getException()
+ {
+ return this.exception;
+ }
+
+ protected void setHandled(boolean handled)
+ {
+ this.handled = handled;
+ }
+
+ /**
+ * Test to see if the exception has been handled via Solder Catch.
+ *
+ * @return test if the exception has been through Solder Catch handling.
+ */
+ public boolean isHandled()
+ {
+ return this.handled;
+ }
+
+ /**
+ * Qualifiers with which the instance was created.
+ *
+ * @return Qualifiers with which the instance was created.
+ */
+ public Set<Annotation> getQualifiers()
+ {
+ return Collections.unmodifiableSet(this.qualifiers);
+ }
+}
View
68 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/HandlerMethod.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+/**
+ * Meta data interface about an exception handler. It is the responsibility of the
+ * implementation to support {@link javax.enterprise.inject.spi.InjectionPoint}s and to
+ * validate those {@link javax.enterprise.inject.spi.InjectionPoint}s.
+ *
+ * @param <T> Exception for which this handler is responsible
+ */
+public interface HandlerMethod<T extends Throwable>
+{
+ /**
+ * Obtains the set of handled event qualifiers.
+ */
+ Set<Annotation> getQualifiers();
+
+ /**
+ * Obtains the handled event type.
+ */
+ Type getExceptionType();
+
+ /**
+ * Calls the handler method, passing the given event object.
+ *
+ * @param event event to pass to the handler.
+ */
+ //fields will be injected via BeanProvider#injectFields
+ void notify(CaughtException<T> event);
+
+ /**
+ * Obtains the precedence of the handler.
+ */
+ int getOrdinal();
+
+ /**
+ * Basic {@link Object#equals(Object)} but must use all of the get methods from this interface to maintain compatibility.
+ *
+ * @param o Object being compared to this.
+ * @return true or false based on standard equality.
+ */
+ boolean equals(Object o);
+
+ @Override
+ int hashCode();
+}
View
33 ...aspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/HandlerMethodStorage.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+/**
+ * Injectable storage to support programmatic registration of {@link HandlerMethod} instances.
+ */
+public interface HandlerMethodStorage
+{
+ /**
+ * Registers the given handlerMethod to the storage.
+ *
+ * @param handlerMethod HandlerMethod implementation to register with the storage
+ */
+ <T extends Throwable> void registerHandlerMethod(HandlerMethod<T> handlerMethod);
+}
View
38 deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/exception/control/Handles.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package org.apache.deltaspike.core.api.exception.control;
+
+import java.lang.annotation.*;
+
+/**
+ * Marker annotation for a method to be considered an Exception Handler. Handlers are typically in the form of
+ * <code>public void ... (@Handles ... CaughtException<...> ...)</code> methods. If a method has a return type, it is
+ * ignored.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+@Documented
+public @interface Handles
+{
+ /**
+ * Precedence relative to handlers for the same type
+ */
+ public abstract int ordinal() default 0; //TODO discuss Precedence
+}
Please sign in to comment.
Something went wrong with that request. Please try again.