blob: 81c7470fd9046e84b7fcbe32e6c794590fe59a75 [file] [log] [blame]
/*
*
* 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;
}
}