| /* |
| * |
| * 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.qpid.proton.engine; |
| |
| import java.io.IOException; |
| |
| import junit.framework.TestCase; |
| |
| import org.apache.qpid.proton.engine.Event.Type; |
| import org.apache.qpid.proton.engine.EventExtensibilityTest.ExtraEvent.ExtraTypes; |
| import org.apache.qpid.proton.reactor.Reactor; |
| import org.apache.qpid.proton.reactor.Selectable; |
| import org.apache.qpid.proton.reactor.Task; |
| import org.junit.Test; |
| |
| public class EventExtensibilityTest extends TestCase { |
| |
| // ////////////// |
| // / Extra package public API classes |
| // ////////////// |
| |
| /** |
| * Sample additional information that gets generated and passed around with |
| * the extension events. The information is stored inside |
| * {@link Event#attachments()} see {@link ExtraEventImpl#getExtraInfo()} |
| */ |
| public static class ExtraInfo { |
| public int foo; |
| } |
| |
| /** |
| * Additional events generated by this extension. |
| * |
| */ |
| public interface ExtraEvent extends Event { |
| /** |
| * Enum is a convenient mechanism for defining new event types |
| */ |
| public enum ExtraTypes implements EventType { |
| FOO, |
| BAR, |
| NOT_A_EXTRA_EVENT { |
| @Override public boolean isValid() { return false; } |
| }; |
| |
| @Override |
| public boolean isValid() { |
| return true; |
| } |
| } |
| |
| /** |
| * typesafe access to event type generated by this extension useful for |
| * handling in switch statements |
| * |
| * @return one of enum values. When invoked on an Event that is not an |
| * ExtraEvent the return value shall be |
| */ |
| ExtraTypes getExtraEventType(); |
| |
| /** |
| * typesafe access to extra information attached to additional events |
| * |
| * @return ExtraInfo stored in the event attachment |
| */ |
| ExtraInfo getExtraInfo(); |
| } |
| |
| /** |
| * New handler methods for handling the extended event types. These methods |
| * can take {@link ExtraEvent} as a parameter to get typesafe access to |
| * {@link ExtraInfo} |
| * |
| * The interface needs to extend the {@link Handler} interface. |
| */ |
| public interface ExtraHandler extends Handler { |
| void onFoo(ExtraEvent e); |
| |
| void onBar(ExtraEvent e); |
| } |
| |
| /** |
| * Implementation of the default base class for ExtraHandler. All events |
| * should be forwarded to the {@link Handler#onUnhandled(Event)} method |
| */ |
| public static class ExtraBaseHandler extends BaseHandler implements ExtraHandler { |
| |
| @Override |
| public void onFoo(ExtraEvent e) { |
| this.onUnhandled(e); |
| } |
| |
| @Override |
| public void onBar(ExtraEvent e) { |
| this.onUnhandled(e); |
| } |
| |
| @Override |
| public void handle(Event e) { |
| EventType type = e.getEventType(); |
| if (type instanceof ExtraEvent.ExtraTypes) { |
| final ExtraEvent event; |
| if (e instanceof ExtraEvent) { |
| event = (ExtraEvent)e; |
| } else { |
| event = new ExtraEventImpl(e); |
| } |
| switch((ExtraEvent.ExtraTypes)type) { |
| case BAR: |
| onBar(event); |
| break; |
| case FOO: |
| onFoo(event); |
| break; |
| case NOT_A_EXTRA_EVENT: |
| super.handle(e); |
| break; |
| } |
| } else { |
| super.handle(e); |
| } |
| } |
| |
| } |
| |
| // ////////////// |
| // / Extra package implementation classes |
| // ////////////// |
| |
| |
| /** |
| * Typesafe access to ExtraInfo attached to event |
| * |
| */ |
| public static class ExtraEventImpl implements ExtraEvent { |
| /** |
| * making this accessor public allows for easy binding to the scripting language |
| */ |
| public static final ExtendableAccessor<Event, ExtraInfo> extraInfoAccessor = new ExtendableAccessor<>(ExtraInfo.class); |
| |
| private Event impl; |
| |
| public ExtraEventImpl(Event impl) { |
| this.impl = impl; |
| } |
| |
| @Override |
| public ExtraTypes getExtraEventType() { |
| EventType type = impl.getEventType(); |
| if (type instanceof ExtraTypes) |
| return (ExtraTypes) type; |
| else |
| return ExtraTypes.NOT_A_EXTRA_EVENT; |
| } |
| |
| @Override |
| public ExtraInfo getExtraInfo() { |
| return extraInfoAccessor.get(impl); |
| } |
| |
| // ---- delegate methods for the Event |
| |
| @Override |
| public Record attachments() { |
| return impl.attachments(); |
| } |
| |
| @Override |
| public EventType getEventType() { |
| return impl.getEventType(); |
| } |
| |
| @Override |
| public Type getType() { |
| return impl.getType(); |
| } |
| |
| @Override |
| public Object getContext() { |
| return impl.getContext(); |
| } |
| |
| @Override |
| public Handler getRootHandler() { |
| return impl.getRootHandler(); |
| } |
| |
| @Override |
| public void dispatch(Handler handler) throws HandlerException { |
| impl.dispatch(handler); |
| } |
| |
| @Override |
| public void redispatch(EventType as_type, Handler handler) throws HandlerException { |
| impl.redispatch(as_type, handler); |
| } |
| @Override |
| public Connection getConnection() { |
| return impl.getConnection(); |
| } |
| |
| @Override |
| public Session getSession() { |
| return impl.getSession(); |
| } |
| |
| @Override |
| public Link getLink() { |
| return impl.getLink(); |
| } |
| |
| @Override |
| public Sender getSender() { |
| return impl.getSender(); |
| } |
| |
| @Override |
| public Receiver getReceiver() { |
| return impl.getReceiver(); |
| } |
| |
| @Override |
| public Delivery getDelivery() { |
| return impl.getDelivery(); |
| } |
| |
| @Override |
| public Transport getTransport() { |
| return impl.getTransport(); |
| } |
| |
| @Override |
| public Reactor getReactor() { |
| return impl.getReactor(); |
| } |
| |
| @Override |
| public Selectable getSelectable() { |
| return impl.getSelectable(); |
| } |
| |
| @Override |
| public Task getTask() { |
| return impl.getTask(); |
| } |
| |
| @Override |
| public Event copy() { |
| return new ExtraEventImpl(impl.copy()); |
| } |
| |
| @Override |
| public void delegate() throws HandlerException { |
| impl.delegate(); |
| } |
| } |
| |
| public class ExtendedTestEventGenerator extends BaseHandler { |
| @Override |
| public void onReactorInit(Event e) { |
| ExtraInfo extra = new ExtraInfo(); |
| extra.foo = 1234; |
| ExtraEventImpl.extraInfoAccessor.set(e, extra); |
| e.redispatch(ExtraEvent.ExtraTypes.FOO, this); |
| } |
| } |
| |
| public class ExtendedTestHandler extends ExtraBaseHandler { |
| public boolean didFoo = false; |
| @Override |
| public void onFoo(ExtraEvent e) { |
| assertEquals(ExtraEvent.ExtraTypes.FOO, e.getEventType()); |
| assertEquals(ExtraEvent.ExtraTypes.FOO, e.getExtraEventType()); |
| assertEquals(Event.Type.NON_CORE_EVENT, e.getType()); |
| assertNotNull(e.getExtraInfo()); |
| assertEquals(1234, e.getExtraInfo().foo); |
| didFoo = true; |
| } |
| } |
| |
| public class FooUnawareTestHandler extends BaseHandler { |
| public boolean seenFoo = false; |
| @Override |
| public void onUnhandled(Event e) { |
| if (e.getEventType() == ExtraTypes.FOO) { |
| seenFoo = true; |
| } |
| } |
| } |
| |
| @Test |
| public void testExtendedType() throws IOException { |
| Reactor r = Reactor.Factory.create(); |
| ExtendedTestEventGenerator gen = new ExtendedTestEventGenerator(); |
| BaseHandler empty = new BaseHandler(); |
| ExtendedTestHandler extra = new ExtendedTestHandler(); |
| FooUnawareTestHandler unaware = new FooUnawareTestHandler(); |
| gen.add(empty); |
| empty.add(extra); |
| extra.add(unaware); |
| r.setGlobalHandler(gen); |
| r.run(); |
| assertTrue(extra.didFoo); |
| assertTrue(unaware.seenFoo); |
| } |
| |
| @Test |
| public void test() { |
| Event.Type t = Type.NON_CORE_EVENT; |
| switch (extracted(t)) { |
| case CONNECTION_BOUND: |
| fail(); |
| case CONNECTION_FINAL: |
| fail(); |
| case CONNECTION_INIT: |
| fail(); |
| case CONNECTION_LOCAL_CLOSE: |
| fail(); |
| case CONNECTION_LOCAL_OPEN: |
| fail(); |
| case CONNECTION_REMOTE_CLOSE: |
| fail(); |
| case CONNECTION_REMOTE_OPEN: |
| fail(); |
| case CONNECTION_UNBOUND: |
| fail(); |
| case DELIVERY: |
| fail(); |
| case LINK_FINAL: |
| fail(); |
| case LINK_FLOW: |
| fail(); |
| case LINK_INIT: |
| fail(); |
| case LINK_LOCAL_CLOSE: |
| fail(); |
| case LINK_LOCAL_DETACH: |
| fail(); |
| case LINK_LOCAL_OPEN: |
| fail(); |
| case LINK_REMOTE_CLOSE: |
| fail(); |
| case LINK_REMOTE_DETACH: |
| fail(); |
| case LINK_REMOTE_OPEN: |
| fail(); |
| case REACTOR_FINAL: |
| fail(); |
| case REACTOR_INIT: |
| fail(); |
| case REACTOR_QUIESCED: |
| fail(); |
| case SELECTABLE_ERROR: |
| fail(); |
| case SELECTABLE_EXPIRED: |
| fail(); |
| case SELECTABLE_FINAL: |
| fail(); |
| case SELECTABLE_INIT: |
| fail(); |
| case SELECTABLE_READABLE: |
| fail(); |
| case SELECTABLE_UPDATED: |
| fail(); |
| case SELECTABLE_WRITABLE: |
| fail(); |
| case SESSION_FINAL: |
| fail(); |
| case SESSION_INIT: |
| fail(); |
| case SESSION_LOCAL_CLOSE: |
| fail(); |
| case SESSION_LOCAL_OPEN: |
| fail(); |
| case SESSION_REMOTE_CLOSE: |
| fail(); |
| case SESSION_REMOTE_OPEN: |
| fail(); |
| case TIMER_TASK: |
| fail(); |
| case TRANSPORT: |
| fail(); |
| case TRANSPORT_CLOSED: |
| fail(); |
| case TRANSPORT_ERROR: |
| fail(); |
| case TRANSPORT_HEAD_CLOSED: |
| fail(); |
| case TRANSPORT_TAIL_CLOSED: |
| fail(); |
| case NON_CORE_EVENT: |
| break; |
| } |
| } |
| |
| private Type extracted(Event.Type t) { |
| return t; |
| } |
| |
| } |