avis-1.2.2/0000755000175000017500000000000011147555544012317 5ustar dpocockdpocockavis-1.2.2/common/0000755000175000017500000000000011147555544013607 5ustar dpocockdpocockavis-1.2.2/common/lib/0000755000175000017500000000000012200662546014345 5ustar dpocockdpocockavis-1.2.2/common/src/0000755000175000017500000000000011147555544014376 5ustar dpocockdpocockavis-1.2.2/common/src/test/0000755000175000017500000000000011147555544015355 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/0000755000175000017500000000000011147555544016144 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/avis/0000755000175000017500000000000011147555544017106 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/avis/common/0000755000175000017500000000000011147555544020376 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/avis/common/JUTestElvinURI.java0000644000175000017500000001713411147555544024003 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.common; import java.net.URISyntaxException; import org.avis.common.ElvinURI; import org.junit.Test; import static org.avis.common.Common.CLIENT_VERSION_MAJOR; import static org.avis.common.Common.CLIENT_VERSION_MINOR; import static org.avis.common.Common.DEFAULT_PORT; import static org.avis.util.Collections.list; import static org.avis.util.Collections.map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; public class JUTestElvinURI { @Test public void version () throws URISyntaxException { ElvinURI uri = new ElvinURI ("elvin://elvin_host"); assertEquals (CLIENT_VERSION_MAJOR, uri.versionMajor); assertEquals (CLIENT_VERSION_MINOR, uri.versionMinor); assertEquals ("elvin_host", uri.host); uri = new ElvinURI ("elvin:5.1//elvin_host"); assertEquals (5, uri.versionMajor); assertEquals (1, uri.versionMinor); assertEquals ("elvin_host", uri.host); uri = new ElvinURI ("elvin:5//elvin_host"); assertEquals (5, uri.versionMajor); assertEquals (CLIENT_VERSION_MINOR, uri.versionMinor); assertEquals ("elvin_host", uri.host); assertInvalid ("http:hello//elvin_host"); assertInvalid ("elvin:hello//elvin_host"); assertInvalid ("elvin:4.0.0//elvin_host"); assertInvalid ("elvin:4.//elvin_host"); assertInvalid ("elvin: //elvin_host"); assertInvalid ("elvin:111111111111111.2222222222222222222//elvin_host"); } @Test public void protocol () throws URISyntaxException { ElvinURI uri = new ElvinURI ("elvin://elvin_host"); assertEquals (ElvinURI.defaultProtocol (), uri.protocol); uri = new ElvinURI ("elvin:/tcp,xdr,ssl/elvin_host"); assertEquals (list ("tcp", "xdr", "ssl"), uri.protocol); assertEquals ("elvin_host", uri.host); uri = new ElvinURI ("elvin:/secure/elvin_host"); assertEquals (ElvinURI.secureProtocol (), uri.protocol); assertInvalid ("elvin:/abc,xyz/elvin_host"); assertInvalid ("elvin:/abc,xyz,dfg,qwe/elvin_host"); assertInvalid ("elvin:/abc,/elvin_host"); assertInvalid ("elvin:/,abc/elvin_host"); assertInvalid ("elvin:/abc,,xyz/elvin_host"); assertInvalid ("elvin:///elvin_host"); } @Test public void endpoint () throws URISyntaxException { ElvinURI uri = new ElvinURI ("elvin://elvin_host"); assertEquals ("elvin_host", uri.host); assertEquals (DEFAULT_PORT, uri.port); uri = new ElvinURI ("elvin://elvin_host:12345"); assertEquals ("elvin_host", uri.host); assertEquals (12345, uri.port); assertInvalid ("elvin://"); assertInvalid ("elvin://hello:there"); } @Test public void options () throws URISyntaxException { ElvinURI uri = new ElvinURI ("elvin://elvin_host;name1=value1"); assertEquals (map ("name1", "value1"), uri.options); uri = new ElvinURI ("elvin://elvin_host;name1=value1;name2=value2"); assertEquals (map ("name1", "value1", "name2", "value2"), uri.options); assertInvalid ("elvin://elvin_host;name1;name2=value2"); assertInvalid ("elvin://elvin_host;=name1;name2=value2"); assertInvalid ("elvin://elvin_host;"); assertInvalid ("elvin://;x=y"); assertInvalid ("elvin://;x="); } @Test public void equality () throws URISyntaxException { assertSameUri ("elvin://elvin_host", "elvin://elvin_host:2917"); assertSameUri ("elvin://elvin_host", "elvin:/tcp,none,xdr/elvin_host"); assertNotSameUri ("elvin://elvin_host", "elvin:/tcp,ssl,xdr/elvin_host"); assertNotSameUri ("elvin://elvin_host", "elvin://elvin_host:29170"); assertNotSameUri ("elvin://elvin_host", "elvin://elvin_host;name=value"); } @Test public void canonicalize () throws URISyntaxException { ElvinURI uri = new ElvinURI ("elvin://elvin_host"); assertEquals ("elvin://elvin_host", uri.toString ()); assertEquals ("elvin:4.0/tcp,none,xdr/elvin_host:2917", uri.toCanonicalString ()); uri = new ElvinURI ("elvin://elvin_host;name1=value1"); assertEquals ("elvin:4.0/tcp,none,xdr/elvin_host:2917;name1=value1", uri.toCanonicalString ()); uri = new ElvinURI ("elvin:/secure/elvin_host:29170;b=2;a=1"); assertEquals ("elvin:4.0/ssl,none,xdr/elvin_host:29170;a=1;b=2", uri.toCanonicalString ()); uri = new ElvinURI ("elvin:5.1/secure/elvin_host:29170;b=2;a=1"); assertEquals ("elvin:5.1/ssl,none,xdr/elvin_host:29170;a=1;b=2", uri.toCanonicalString ()); } @Test public void constructors () throws Exception { ElvinURI defaultUri = new ElvinURI ("elvin:5.6/a,b,c/default_host:1234"); ElvinURI uri = new ElvinURI ("elvin://host", defaultUri); assertEquals (defaultUri.scheme, "elvin"); assertEquals (defaultUri.versionMajor, uri.versionMajor); assertEquals (defaultUri.versionMinor, uri.versionMinor); assertEquals (defaultUri.protocol, uri.protocol); assertEquals ("host", uri.host); assertEquals (defaultUri.port, uri.port); uri = new ElvinURI ("elvin:7.0/x,y,z/host:5678", defaultUri); assertEquals (7, uri.versionMajor); assertEquals (0, uri.versionMinor); assertEquals (list ("x", "y", "z"), uri.protocol); assertEquals ("host", uri.host); assertEquals (5678, uri.port); } @Test public void ipv6 () throws URISyntaxException { ElvinURI uri = new ElvinURI ("elvin://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:1234"); assertEquals ("[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]", uri.host); assertEquals (1234, uri.port); uri = new ElvinURI ("elvin:/tcp,xdr,ssl/[::1/128]:4567"); assertEquals (list ("tcp", "xdr", "ssl"), uri.protocol); assertEquals ("[::1/128]", uri.host); assertEquals (4567, uri.port); assertInvalid ("elvin://[::1/128"); assertInvalid ("elvin://[[::1/128"); assertInvalid ("elvin://[::1/128]]"); assertInvalid ("elvin://[]"); assertInvalid ("elvin://["); assertInvalid ("elvin://[::1/128];hello"); assertInvalid ("elvin://[::1/128]:xyz"); assertInvalid ("elvin://[::1/128];"); assertInvalid ("elvin:///[::1/128]"); } private static void assertSameUri (String uri1, String uri2) throws URISyntaxException { assertEquals (new ElvinURI (uri1), new ElvinURI (uri2)); assertEquals (new ElvinURI (uri1).hashCode (), new ElvinURI (uri2).hashCode ()); } private static void assertNotSameUri (String uri1, String uri2) throws URISyntaxException { assertFalse (new ElvinURI (uri1).equals (new ElvinURI (uri2))); } private static void assertInvalid (String uriString) { try { new ElvinURI (uriString); fail ("Invalid URI \"" + uriString + "\" not detected"); } catch (InvalidURIException ex) { // ok // System.out.println ("error = " + ex.getMessage ()); } } } avis-1.2.2/common/src/test/org/avis/security/0000755000175000017500000000000011147555544020755 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/avis/security/JUTestKeys.java0000644000175000017500000003417611147555544023645 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; import org.apache.mina.common.ByteBuffer; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.avis.security.DualKeyScheme.Subset.CONSUMER; import static org.avis.security.DualKeyScheme.Subset.PRODUCER; import static org.avis.security.KeyScheme.SHA1_CONSUMER; import static org.avis.security.KeyScheme.SHA1_DUAL; import static org.avis.security.KeyScheme.SHA1_PRODUCER; import static org.avis.security.Keys.EMPTY_KEYS; public class JUTestKeys { @Test public void IO () throws Exception { ByteBuffer buff = ByteBuffer.allocate (1024); Keys keys = new Keys (); keys.add (SHA1_DUAL, PRODUCER, new Key ("dual key 1")); keys.add (SHA1_DUAL, CONSUMER, new Key ("dual key 2")); keys.add (SHA1_PRODUCER, new Key ("producer key 1")); keys.add (SHA1_CONSUMER, new Key ("consumer key 1")); keys.add (SHA1_CONSUMER, new Key ("consumer key 2")); // test roundtrip encode/decode keys.encode (buff); buff.flip (); Keys newKeys = Keys.decode (buff); assertEquals (keys, newKeys); } @Test public void equality () { assertTrue (new Key ("a test key number 1").equals (new Key ("a test key number 1"))); assertFalse (new Key ("a test key number 1").equals (new Key ("a test key number 2"))); assertEquals (new Key ("a test key number 1").hashCode (), new Key ("a test key number 1").hashCode ()); // test Keys.equals () Keys keys1 = new Keys (); keys1.add (SHA1_DUAL, PRODUCER, new Key ("dual key 1")); keys1.add (SHA1_DUAL, CONSUMER, new Key ("dual key 2")); keys1.add (SHA1_PRODUCER, new Key ("producer key 1")); keys1.add (SHA1_CONSUMER, new Key ("consumer key 1")); Keys keys2 = new Keys (); keys2.add (SHA1_DUAL, PRODUCER, new Key ("dual key 1")); keys2.add (SHA1_DUAL, CONSUMER, new Key ("dual key 2")); keys2.add (SHA1_PRODUCER, new Key ("producer key 1")); keys2.add (SHA1_CONSUMER, new Key ("consumer key 1")); assertEquals (keys1.hashCode (), keys2.hashCode ()); assertEquals (keys1, keys2); keys2.remove (SHA1_CONSUMER, new Key ("consumer key 1")); assertFalse (keys1.equals (keys2)); } /** * Test add/remove of single keys. */ @Test public void addRemove () throws Exception { Keys keys = new Keys (); keys.add (SHA1_DUAL, PRODUCER, new Key ("dual key 1")); keys.add (SHA1_DUAL, CONSUMER, new Key ("dual key 2")); keys.add (SHA1_PRODUCER, new Key ("producer key 1")); keys.add (SHA1_CONSUMER, new Key ("consumer key 1")); DualKeySet dualKeys = keys.keysetFor (SHA1_DUAL); assertEquals (1, dualKeys.consumerKeys.size ()); assertEquals (1, dualKeys.producerKeys.size ()); keys.add (SHA1_DUAL, PRODUCER, new Key ("dual key 3")); assertEquals (2, keys.keysetFor (SHA1_DUAL).producerKeys.size ()); keys.remove (SHA1_DUAL, PRODUCER, new Key ("dual key 3")); assertEquals (1, keys.keysetFor (SHA1_DUAL).producerKeys.size ()); keys.remove (SHA1_DUAL, PRODUCER, new Key ("dual key 1")); assertEquals (0, keys.keysetFor (SHA1_DUAL).producerKeys.size ()); // remove a non-existent key, check nothing Bad happens keys.remove (SHA1_CONSUMER, new Key ("blah")); keys.remove (SHA1_DUAL, CONSUMER, new Key ("dual key 2")); assertEquals (0, keys.keysetFor (SHA1_DUAL).consumerKeys.size ()); SingleKeySet consumerKeys = keys.keysetFor (SHA1_CONSUMER); assertEquals (1, consumerKeys.size ()); keys.remove (SHA1_CONSUMER, new Key ("consumer key 1")); assertEquals (0, consumerKeys.size ()); SingleKeySet producerKeys = keys.keysetFor (SHA1_PRODUCER); assertEquals (1, producerKeys.size ()); keys.remove (SHA1_PRODUCER, new Key ("producer key 1")); assertEquals (0, producerKeys.size ()); assertTrue (keys.isEmpty ()); // remove a non-existent key, check nothing Bad happens keys.remove (SHA1_CONSUMER, new Key ("blah")); } /** * Test add/remove of entire keysets. */ @Test public void addRemoveSets () throws Exception { Keys keys1 = new Keys (); Keys keys2 = new Keys (); Keys keys3 = new Keys (); keys1.add (SHA1_DUAL, PRODUCER, new Key ("dual key prod 1")); keys1.add (SHA1_DUAL, CONSUMER, new Key ("dual key cons 1")); keys1.add (SHA1_PRODUCER, new Key ("producer key 1.1")); keys1.add (SHA1_PRODUCER, new Key ("producer key 1.2")); keys2.add (SHA1_DUAL, PRODUCER, new Key ("dual key prod 2")); keys2.add (SHA1_DUAL, CONSUMER, new Key ("dual key cons 2")); keys2.add (SHA1_CONSUMER, new Key ("consumer key 1")); // add keys in bulk to keys3 keys3.add (keys1); assertEquals (1, keys3.keysetFor (SHA1_DUAL).consumerKeys.size ()); assertEquals (1, keys3.keysetFor (SHA1_DUAL).producerKeys.size ()); assertEquals (2, keys3.keysetFor (SHA1_PRODUCER).size ()); assertEquals (0, keys3.keysetFor (SHA1_CONSUMER).size ()); keys3.add (keys2); assertEquals (2, keys3.keysetFor (SHA1_DUAL).consumerKeys.size ()); assertEquals (2, keys3.keysetFor (SHA1_DUAL).producerKeys.size ()); assertEquals (2, keys3.keysetFor (SHA1_PRODUCER).size ()); assertEquals (1, keys3.keysetFor (SHA1_CONSUMER).size ()); keys3.remove (keys1); assertEquals (1, keys3.keysetFor (SHA1_DUAL).consumerKeys.size ()); assertEquals (1, keys3.keysetFor (SHA1_DUAL).producerKeys.size ()); assertEquals (0, keys3.keysetFor (SHA1_PRODUCER).size ()); assertEquals (1, keys3.keysetFor (SHA1_CONSUMER).size ()); keys3.remove (keys2); assertTrue (keys3.isEmpty ()); } @Test public void delta () throws Exception { Keys addedKeys = new Keys (); Keys removedKeys = new Keys (); Keys baseKeys = new Keys (); addedKeys.add (SHA1_DUAL, PRODUCER, new Key ("added/removed key")); addedKeys.add (SHA1_DUAL, CONSUMER, new Key ("added key 1")); addedKeys.add (SHA1_PRODUCER, new Key ("added key 2")); removedKeys.add (SHA1_DUAL, PRODUCER, new Key ("added/removed key")); removedKeys.add (SHA1_DUAL, CONSUMER, new Key ("non existent key")); removedKeys.add (SHA1_DUAL, CONSUMER, new Key ("removed key")); baseKeys.add (SHA1_DUAL, PRODUCER, new Key ("kept key")); baseKeys.add (SHA1_DUAL, CONSUMER, new Key ("removed key")); Keys delta = baseKeys.delta (addedKeys, removedKeys); Keys correctKeys = new Keys (); correctKeys.add (SHA1_DUAL, CONSUMER, new Key ("added key 1")); correctKeys.add (SHA1_PRODUCER, new Key ("added key 2")); correctKeys.add (SHA1_DUAL, PRODUCER, new Key ("kept key")); assertEquals (correctKeys, delta); // check delta works with empty keys Keys keys4 = EMPTY_KEYS.delta (addedKeys, removedKeys); correctKeys = new Keys (); correctKeys.add (SHA1_DUAL, CONSUMER, new Key ("added key 1")); correctKeys.add (SHA1_PRODUCER, new Key ("added key 2")); assertEquals (correctKeys, keys4); } /** * Basic test of computeDelta () */ @Test public void computeDeltaSingleScheme () { Keys keys1 = new Keys (); Keys keys2 = new Keys (); Key key1 = new Key ("key 1"); Key key2 = new Key ("key 2"); Keys.Delta delta = keys1.deltaFrom (keys2); assertEquals (0, delta.added.keysetFor (SHA1_PRODUCER).size ()); assertEquals (0, delta.added.keysetFor (SHA1_CONSUMER).size ()); assertEquals (0, delta.added.keysetFor (SHA1_DUAL).size ()); assertEquals (0, delta.removed.keysetFor (SHA1_PRODUCER).size ()); assertEquals (0, delta.removed.keysetFor (SHA1_CONSUMER).size ()); assertEquals (0, delta.removed.keysetFor (SHA1_DUAL).size ()); // add a single producer key keys2.add (SHA1_PRODUCER, key1); delta = keys1.deltaFrom (keys2); assertEquals (1, delta.added.keysetFor (SHA1_PRODUCER).size ()); assertEquals (0, delta.removed.keysetFor (SHA1_PRODUCER).size ()); checkApplyDelta (delta, keys1, keys2); // remove a single producer key keys1.add (SHA1_PRODUCER, key2); delta = keys1.deltaFrom (keys2); assertEquals (1, delta.added.keysetFor (SHA1_PRODUCER).size ()); assertEquals (1, delta.removed.keysetFor (SHA1_PRODUCER).size ()); checkApplyDelta (delta, keys1, keys2); // key1 is now in both sets keys1.add (SHA1_PRODUCER, key1); delta = keys1.deltaFrom (keys2); assertEquals (0, delta.added.keysetFor (SHA1_PRODUCER).size ()); assertEquals (1, delta.removed.keysetFor (SHA1_PRODUCER).size ()); // key2 is not in both keys2.add (SHA1_PRODUCER, key2); delta = keys1.deltaFrom (keys2); assertEquals (0, delta.added.keysetFor (SHA1_PRODUCER).size ()); assertEquals (0, delta.removed.keysetFor (SHA1_PRODUCER).size ()); checkApplyDelta (delta, keys1, keys2); } /** * Test computeDelta () with a multiple key schemes in use. */ @Test public void computeDeltaMultiScheme () { Keys keys1 = new Keys (); Keys keys2 = new Keys (); Key key1 = new Key ("key 1"); Key key2 = new Key ("key 2"); Key key3 = new Key ("key 3"); keys1.add (SHA1_PRODUCER, key1); keys1.add (SHA1_CONSUMER, key2); keys1.add (SHA1_CONSUMER, key3); keys2.add (SHA1_PRODUCER, key3); keys2.add (SHA1_CONSUMER, key3); Keys.Delta delta = keys1.deltaFrom (keys2); assertEquals (1, delta.added.keysetFor (SHA1_PRODUCER).size ()); assertEquals (1, delta.removed.keysetFor (SHA1_PRODUCER).size ()); assertEquals (0, delta.added.keysetFor (SHA1_CONSUMER).size ()); assertEquals (1, delta.removed.keysetFor (SHA1_CONSUMER).size ()); checkApplyDelta (delta, keys1, keys2); } /** * Test computeDelta () with a dual key set. */ @Test public void computeDeltaDual () { Keys keys1 = new Keys (); Keys keys2 = new Keys (); Key key1 = new Key ("key 1"); Key key2 = new Key ("key 2"); Key key3 = new Key ("key 3"); keys1.add (SHA1_DUAL, PRODUCER, key1); keys1.add (SHA1_DUAL, CONSUMER, key2); keys1.add (SHA1_DUAL, CONSUMER, key3); keys2.add (SHA1_DUAL, PRODUCER, key3); keys2.add (SHA1_DUAL, CONSUMER, key3); Keys.Delta delta = keys1.deltaFrom (keys2); assertEquals (1, delta.added.keysetFor (SHA1_DUAL).producerKeys.size ()); assertEquals (1, delta.removed.keysetFor (SHA1_DUAL).producerKeys.size ()); assertEquals (0, delta.added.keysetFor (SHA1_DUAL).consumerKeys.size ()); assertEquals (1, delta.removed.keysetFor (SHA1_DUAL).consumerKeys.size ()); checkApplyDelta (delta, keys1, keys2); } /** * Check applying delta to keys1 gives keys2 */ private static void checkApplyDelta (Keys.Delta delta, Keys keys1, Keys keys2) { assertEquals (keys1.delta (delta.added, delta.removed), keys2); } @Test public void producer () { Key alicePrivate = new Key ("alice private"); Key alicePublic = alicePrivate.publicKeyFor (SHA1_PRODUCER); Keys aliceNtfnKeys = new Keys (); aliceNtfnKeys.add (SHA1_PRODUCER, alicePrivate); Keys bobSubKeys = new Keys (); bobSubKeys.add (SHA1_PRODUCER, alicePublic); Keys eveSubKeys = new Keys (); eveSubKeys.add (SHA1_PRODUCER, new Key ("Not alice's key").publicKeyFor (SHA1_PRODUCER)); // turn alice's private keys into public (prime) aliceNtfnKeys.hashPrivateKeysForRole (PRODUCER); assertTrue (bobSubKeys.match (aliceNtfnKeys)); assertFalse (eveSubKeys.match (aliceNtfnKeys)); } @Test public void consumer () { Key bobPrivate = new Key ("bob private"); Key bobPublic = bobPrivate.publicKeyFor (SHA1_CONSUMER); Keys aliceNtfnKeys = new Keys (); aliceNtfnKeys.add (SHA1_CONSUMER, bobPublic); Keys bobSubKeys = new Keys (); bobSubKeys.add (SHA1_CONSUMER, bobPrivate); Keys eveSubKeys = new Keys (); eveSubKeys.add (SHA1_CONSUMER, new Key ("Not bob's key")); // turn bob and eve's private keys into public (prime) bobSubKeys.hashPrivateKeysForRole (CONSUMER); eveSubKeys.hashPrivateKeysForRole (CONSUMER); assertTrue (bobSubKeys.match (aliceNtfnKeys)); assertFalse (eveSubKeys.match (aliceNtfnKeys)); } @Test public void dual () { Key alicePrivate = new Key ("alice private"); Key alicePublic = alicePrivate.publicKeyFor (SHA1_DUAL); Key bobPrivate = new Key ("bob private"); Key bobPublic = bobPrivate.publicKeyFor (SHA1_DUAL); Keys aliceNtfnKeys = new Keys (); aliceNtfnKeys.add (SHA1_DUAL, CONSUMER, bobPublic); aliceNtfnKeys.add (SHA1_DUAL, PRODUCER, alicePrivate); Keys bobSubKeys = new Keys (); bobSubKeys.add (SHA1_DUAL, CONSUMER, bobPrivate); bobSubKeys.add (SHA1_DUAL, PRODUCER, alicePublic); Keys eveSubKeys = new Keys (); Key randomPrivate = new Key ("Not bob's key"); eveSubKeys.add (SHA1_DUAL, CONSUMER, randomPrivate); eveSubKeys.add (SHA1_DUAL, PRODUCER, randomPrivate.publicKeyFor (SHA1_DUAL)); // turn private keys into public (prime) bobSubKeys.hashPrivateKeysForRole (CONSUMER); eveSubKeys.hashPrivateKeysForRole (CONSUMER); aliceNtfnKeys.hashPrivateKeysForRole (PRODUCER); assertTrue (bobSubKeys.match (aliceNtfnKeys)); assertTrue (aliceNtfnKeys.match (bobSubKeys)); assertFalse (eveSubKeys.match (aliceNtfnKeys)); } } avis-1.2.2/common/src/test/org/avis/io/0000755000175000017500000000000011147555544017515 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/avis/io/JUTestLivenessFilter.java0000644000175000017500000000503411147555544024417 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoHandlerAdapter; import org.avis.io.messages.LivenessFailureMessage; import org.avis.io.messages.TestConn; import org.junit.Test; import static java.lang.System.currentTimeMillis; import static java.lang.Thread.sleep; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class JUTestLivenessFilter { @Test public void livenessTimeout () throws Exception { // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); AcceptorConnectorSetup testSetup = new AcceptorConnectorSetup (); LivenessFilter filter = new LivenessFilter (1000, 1000); testSetup.connectorConfig.getFilterChain ().addLast ("liveness", filter); TestingIoHandler acceptorListener = new TestingIoHandler (); TestingIoHandler connectListener = new TestingIoHandler (); testSetup.connect (acceptorListener, connectListener); // wait for TestConn message acceptorListener.waitForMessage (); assertEquals (TestConn.ID, acceptorListener.message.typeId ()); sleep (1000); // wait for LivenessTimeout connectListener.waitForMessage (); assertEquals (LivenessFailureMessage.ID, connectListener.message.typeId ()); LivenessFilter.Tracker tracker = LivenessFilter.trackerFor (testSetup.session); testSetup.close (); assertTrue (SharedExecutor.sharedExecutorDisposed ()); // wait for tracker to be disposed on session close long start = currentTimeMillis (); while (currentTimeMillis () - start < 5000 && !tracker.isDisposed ()) sleep (200); assertTrue (tracker.isDisposed ()); } static class NullAcceptorListener extends IoHandlerAdapter implements IoHandler { // zip } } avis-1.2.2/common/src/test/org/avis/io/JUTestNack.java0000644000175000017500000000225611147555544022340 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import org.avis.io.messages.Nack; import org.junit.Test; import static org.junit.Assert.assertEquals; public class JUTestNack { @Test public void formattedMessage () { Nack nack = new Nack (); nack.args = new Object [] {"foo", "bar"}; nack.message = "There was a %1 in the %2 (%1, %2) %3 %"; String formattedMessage = nack.formattedMessage (); assertEquals ("There was a foo in the bar (foo, bar) %3 %", formattedMessage); } } avis-1.2.2/common/src/test/org/avis/io/JUTestXdrCoding.java0000644000175000017500000001154011147555544023341 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.HashMap; import java.util.Map; import java.lang.reflect.Array; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.io.XdrCoding; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Test the {@link XdrCoding} utility class. */ public class JUTestXdrCoding { @Test public void stringIO () throws Exception { ByteBuffer buff = ByteBuffer.allocate (1024); XdrCoding.putString (buff, ""); assertEquals (4, buff.position ()); buff.position (0); XdrCoding.putString (buff, "hello"); assertEquals (12, buff.position ()); buff.position (0); assertEquals ("hello", XdrCoding.getString (buff)); } @Test public void utf8 () throws Exception { roundtrip ("Hello A\u0308\uFB03ns this is some bogus text"); roundtrip ("Hi there \u00C4\uFB03ns more bogus text"); // some UTF-8 data seen in the wild that caused problems... roundtrip (new String (new byte [] {(byte)0xc3, (byte)0x94, (byte)0xc3, (byte)0xb8, (byte)0xce, (byte)0xa9, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x73, 0x65, 0x20}, "UTF-8")); } private void roundtrip (String str) throws ProtocolCodecException { ByteBuffer buff = ByteBuffer.allocate (1024); XdrCoding.putString (buff, str); buff.flip (); assertEquals (str, XdrCoding.getString (buff)); } @Test public void nameValueIO () throws Exception { ByteBuffer buff = ByteBuffer.allocate (1024); HashMap nameValues = new HashMap (); XdrCoding.putNameValues (buff, nameValues); assertEquals (4, buff.position ()); buff.position (0); nameValues.put ("int", 42); nameValues.put ("opaque", new byte [] {1, 2, 3}); XdrCoding.putNameValues (buff, nameValues); assertEquals (44, buff.position ()); buff.flip (); assertMapsEqual (nameValues, XdrCoding.getNameValues (buff)); } @Test public void objectsIO () throws Exception { ByteBuffer buff = ByteBuffer.allocate (1024); Object [] objects = new Object [] {"hello", Integer.valueOf (42)}; XdrCoding.putObjects (buff, objects); buff.flip (); Object [] objectsCopy = XdrCoding.getObjects (buff); assertArraysEquals (objects, objectsCopy); } @Test public void padding () { assertEquals (0, XdrCoding.paddingFor (0)); assertEquals (3, XdrCoding.paddingFor (1)); assertEquals (2, XdrCoding.paddingFor (2)); assertEquals (1, XdrCoding.paddingFor (3)); assertEquals (0, XdrCoding.paddingFor (4)); assertEquals (3, XdrCoding.paddingFor (5)); assertEquals (3, XdrCoding.paddingFor (25)); assertEquals (3, XdrCoding.paddingFor (4 * 1234 + 1)); } private void assertArraysEquals (Object [] o1, Object [] o2) { assertEquals (o1.length, o2.length); for (int i = 0; i < o1.length; i++) assertEquals (o1 [i], o2 [i]); } private void assertMapsEqual (Map map1, Map map2) { assertEquals (map1.size (), map2.size ()); for (String name : map1.keySet ()) assertTrue (objectsEqual (map1.get (name), map2.get (name))); for (String name : map2.keySet ()) assertTrue (objectsEqual (map1.get (name), map2.get (name))); } private static boolean objectsEqual (Object o1, Object o2) { if (o1 == o2 || (o1 != null && o1.equals (o2))) { return true; } else if (o1 != null && o2 != null) { // deep test for array equality Class cls = o1.getClass (); if (cls == o2.getClass () && cls.isArray ()) { int length = Array.getLength (o1); if (length == Array.getLength (o2)) { for (int i = 0; i < length; i++) { if (!objectsEqual (Array.get (o1, i), Array.get (o2, i))) return false; } return true; } } } return false; } } avis-1.2.2/common/src/test/org/avis/io/TestingIoHandler.java0000644000175000017500000000520711147555544023567 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import junit.framework.AssertionFailedError; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoSession; import org.avis.io.messages.Message; import static org.junit.Assert.fail; import static org.avis.logging.Log.alarm; import static org.avis.util.Text.className; /** * IO handler that allows a test client to wait for an incoming * message. * * @author Matthew Phillips */ public class TestingIoHandler extends IoHandlerAdapter implements IoHandler { public Message message; @Override public synchronized void messageReceived (IoSession session, Object theMessage) throws Exception { message = (Message)theMessage; notifyAll (); } public synchronized Message waitForMessage () throws InterruptedException { if (message == null) wait (5000); if (message == null) fail ("No message received"); return message; } @SuppressWarnings("unchecked") public synchronized T waitForMessage (Class type) throws InterruptedException { waitForMessage (); if (type.isAssignableFrom (message.getClass ())) return (T)message; else throw new AssertionFailedError ("Expected " + className (type) + ", was " + className (message)); } public synchronized void waitForClose (IoSession session) { if (session.isClosing ()) return; try { wait (5000); } catch (InterruptedException ex) { throw new Error (ex); } if (!session.isClosing ()) throw new AssertionFailedError ("Session not closed"); } @Override public synchronized void sessionClosed (IoSession session) throws Exception { notifyAll (); } @Override public void exceptionCaught (IoSession session, Throwable cause) throws Exception { alarm ("MINA IO exception", this, cause); } }avis-1.2.2/common/src/test/org/avis/io/JUTestFrameCodec.java0000644000175000017500000000423111147555544023447 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.HashMap; import java.util.Random; import org.junit.Test; import org.avis.io.messages.NotifyDeliver; import static org.junit.Assert.assertEquals; /** * Tests for FrameCodec. * * @author Matthew Phillips */ public class JUTestFrameCodec { @Test public void bigFrames () throws Exception { AcceptorConnectorSetup testSetup = new AcceptorConnectorSetup (); TestingIoHandler acceptorListener = new TestingIoHandler (); testSetup.connect (acceptorListener, new TestingIoHandler ()); HashMap attributes = new HashMap (); attributes.put ("string", bigString ()); attributes.put ("blob", new byte [1024 * 1024]); NotifyDeliver notifyDeliver = new NotifyDeliver (attributes, new long [] {1}, new long [0]); testSetup.session.write (notifyDeliver); NotifyDeliver message = (NotifyDeliver)acceptorListener.waitForMessage (); assertEquals (attributes.get ("string"), message.attributes.get ("string")); assertEquals (((byte [])attributes.get ("blob")).length, ((byte [])message.attributes.get ("blob")).length); testSetup.close (); } private static String bigString () { StringBuilder str = new StringBuilder (); Random random = new Random (42); for (int i = 0; i < 800 * 1024; i++) str.append ((char)random.nextInt (Character.MAX_CODE_POINT) + 1); return str.toString (); } } avis-1.2.2/common/src/test/org/avis/io/AcceptorConnectorSetup.java0000644000175000017500000000615111147555544025017 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.concurrent.ExecutorService; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.DefaultIoFilterChainBuilder; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.ThreadModel; import org.apache.mina.transport.socket.nio.SocketAcceptor; import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import static java.util.concurrent.Executors.newCachedThreadPool; /** * Test setup that creates a connected SocketAcceptor/SocketConnector * pair. * * @author Matthew Phillips */ public class AcceptorConnectorSetup { public SocketAcceptor acceptor; public SocketAcceptorConfig acceptorConfig; public IoSession session; public ExecutorService executor; public SocketConnector connector; public SocketConnectorConfig connectorConfig; public AcceptorConnectorSetup () throws IOException { executor = newCachedThreadPool (); // listener acceptor = new SocketAcceptor (1, executor); acceptorConfig = new SocketAcceptorConfig (); acceptorConfig.setReuseAddress (true); acceptorConfig.setThreadModel (ThreadModel.MANUAL); DefaultIoFilterChainBuilder filterChainBuilder = acceptorConfig.getFilterChain (); filterChainBuilder.addLast ("codec", ClientFrameCodec.FILTER); // connector connector = new SocketConnector (1, executor); connectorConfig = new SocketConnectorConfig (); connector.setWorkerTimeout (0); connectorConfig.setThreadModel (ThreadModel.MANUAL); connectorConfig.setConnectTimeout (20); connectorConfig.getFilterChain ().addLast ("codec", ClientFrameCodec.FILTER); } public void connect (IoHandler acceptorListener, IoHandler connectorListener) throws IOException { InetSocketAddress remoteAddress = new InetSocketAddress ("127.0.0.1", 29170); acceptor.bind (remoteAddress, acceptorListener, acceptorConfig); ConnectFuture future = connector.connect (remoteAddress, connectorListener, connectorConfig); future.join (); session = future.getSession (); } public void close () { session.close (); acceptor.unbindAll (); executor.shutdown (); } } avis-1.2.2/common/src/test/org/avis/io/JUTestRequestTrackingFilter.java0000644000175000017500000000532111147555544025741 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoHandlerAdapter; import org.avis.io.messages.ConnRqst; import org.avis.io.messages.RequestTimeoutMessage; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class JUTestRequestTrackingFilter { /** * Send a message with reply, check that RequestTracker generates a * timeout message. */ @Test public void requestTimeout () throws Exception { AcceptorConnectorSetup testSetup = new AcceptorConnectorSetup (); RequestTrackingFilter filter = new RequestTrackingFilter (2000); testSetup.connectorConfig.getFilterChain ().addLast ("tracker", filter); TestingIoHandler connectListener = new TestingIoHandler (); testSetup.connect (new NullAcceptorListener (), connectListener); // send message, wait for timeout message connectListener.message = null; ConnRqst fedConnRqst = new ConnRqst (1, 0); testSetup.session.write (fedConnRqst); connectListener.waitForMessage (); assertNotNull (connectListener.message); assertEquals (RequestTimeoutMessage.ID, connectListener.message.typeId ()); assertSame (fedConnRqst, ((RequestTimeoutMessage)connectListener.message).request); // try again, with two in the pipe connectListener.message = null; fedConnRqst = new ConnRqst (1, 0); testSetup.session.write (fedConnRqst); Thread.sleep (1000); testSetup.session.write (new ConnRqst (1, 0)); connectListener.waitForMessage (); assertNotNull (connectListener.message); assertSame (fedConnRqst, ((RequestTimeoutMessage)connectListener.message).request); testSetup.close (); assertTrue (SharedExecutor.sharedExecutorDisposed ()); } static class NullAcceptorListener extends IoHandlerAdapter implements IoHandler { // zip } } avis-1.2.2/common/src/test/org/avis/util/0000755000175000017500000000000011147555544020063 5ustar dpocockdpocockavis-1.2.2/common/src/test/org/avis/util/JUTestText.java0000644000175000017500000001054211147555544022753 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.Arrays; import org.junit.Test; import static org.avis.util.Text.bytesToHex; import static org.avis.util.Text.dataToBytes; import static org.avis.util.Text.expandBackslashes; import static org.avis.util.Text.stripBackslashes; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class JUTestText { @Test public void stringToValue () throws Exception { assertEquals ("hello", Text.stringToValue ("'hello'")); assertEquals ("he\"llo", Text.stringToValue ("'he\"llo'")); assertEquals ("he\"llo", Text.stringToValue ("'he\\\"llo'")); assertEquals ("he\\\"llo", Text.stringToValue ("'he\\\\\\\"llo'")); assertEquals ("hello", Text.stringToValue ("\"hello\"")); assertEquals (32, Text.stringToValue ("32")); assertEquals (42L, Text.stringToValue ("42L")); assertEquals (0.12, Text.stringToValue ("0.12")); assertEquals ("0e ff de", Text.bytesToHex ((byte [])Text.stringToValue ("[0e ff de]"))); assertStringValueInvalid ("'hello\""); assertStringValueInvalid ("\"hello'"); assertStringValueInvalid ("'hello"); assertStringValueInvalid ("\"hello"); assertStringValueInvalid (".2"); } private static void assertStringValueInvalid (String string) { try { Text.stringToValue (string); fail (); } catch (InvalidFormatException ex) { // ok } } @Test public void parseHexBytes () throws Exception { assertEquals (bytesToHex (new byte [] {00, (byte)0xA1, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF}), bytesToHex (Text.hexToBytes ("00A1DEADBEEF"))); } /** * Test handling of backslash escapes in strings and identifiers. */ @Test public void escapeHandling () throws Exception { String expanded; expanded = expandBackslashes ("\\n\\t\\b\\r\\f\\v\\a"); assertEquals (new String (new byte [] {'\n', '\t', '\b', '\r', '\f', 11, 7}), expanded); expanded = expandBackslashes ("\\x32"); assertEquals (new String (new byte [] {0x32}), expanded); expanded = expandBackslashes ("\\xf:"); assertEquals (new String (new byte [] {0xF, ':'}), expanded); expanded = expandBackslashes ("\\x424"); assertEquals (new String (new byte [] {0x42, '4'}), expanded); expanded = expandBackslashes ("\\034"); assertEquals (new String (new byte [] {034}), expanded); expanded = expandBackslashes ("\\34:"); assertEquals (new String (new byte [] {034, ':'}), expanded); expanded = expandBackslashes ("\\7:"); assertEquals (new String (new byte [] {07, ':'}), expanded); expanded = expandBackslashes ("\\1234"); assertEquals (new String (new byte [] {0123, '4'}), expanded); expanded = stripBackslashes ("a \\test\\ string\\:\\\\!"); assertEquals ("a test string:\\!", expanded); try { expanded = stripBackslashes ("invalid\\"); fail (); } catch (InvalidFormatException ex) { // ok } } @Test public void dataToByte () throws Exception { byte [] bytes = dataToBytes ("[00 01 0a ff] ".getBytes ("UTF-8")); assertTrue (Arrays.equals (new byte [] {00, 01, 0x0a, (byte)0xff}, bytes)); bytes = dataToBytes ("\"hello \u0234 world\"\n".getBytes ("UTF-8")); assertEquals ("hello \u0234 world", new String (bytes, "UTF-8")); bytes = dataToBytes (new byte [] {'#', (byte)0xde, (byte)0xad}); assertTrue (Arrays.equals (new byte [] {(byte)0xde, (byte)0xad}, bytes)); } } avis-1.2.2/common/src/test/org/avis/util/JUTestWildcardFilter.java0000644000175000017500000000301511147555544024723 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class JUTestWildcardFilter { @Test public void wildcards () throws Exception { assertFalse (new WildcardFilter ().matches ("")); assertTrue (new WildcardFilter ("").matches ("")); assertFalse (new WildcardFilter ("").matches ("a")); assertTrue (new WildcardFilter ("abc").matches ("abc")); assertFalse (new WildcardFilter ("abc").matches ("abcd")); assertTrue (new WildcardFilter ("abc*").matches ("abcd")); assertTrue (new WildcardFilter ("abc*z").matches ("abcdefgz")); assertTrue (new WildcardFilter ("def", "abc*").matches ("abcd")); assertTrue (new WildcardFilter ("abc?").matches ("abcd")); assertFalse (new WildcardFilter ("abc?").matches ("abcde")); } } avis-1.2.2/common/src/test/org/avis/util/JUTestStreams.java0000644000175000017500000000431611147555544023447 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.ArrayList; import java.util.List; import java.io.IOException; import java.io.StringReader; import org.junit.Test; import static org.junit.Assert.assertEquals; public class JUTestStreams { @Test public void readLine () throws Exception { List lines = readLines ("line 0\nline 1\r\nline 2\n\rline 3"); assertEquals (4, lines.size ()); for (int i = 0; i < lines.size (); i++) assertEquals ("line " + i, lines.get (i)); lines = readLines ("line 0"); assertEquals (1, lines.size ()); for (int i = 0; i < lines.size (); i++) assertEquals ("line " + i, lines.get (i)); lines = readLines ("\nline 0\r\n"); assertEquals (2, lines.size ()); assertEquals ("", lines.get (0)); assertEquals ("line 0", lines.get (1)); lines = readLines ("\n\nline 2\r\n\n\r\r\nline 5\r\r"); assertEquals (7, lines.size ()); assertEquals ("", lines.get (0)); assertEquals ("", lines.get (1)); assertEquals ("line 2", lines.get (2)); assertEquals ("", lines.get (3)); assertEquals ("", lines.get (4)); assertEquals ("line 5", lines.get (5)); assertEquals ("", lines.get (6)); } private static List readLines (String source) throws IOException { List lines = new ArrayList (); StringReader in = new StringReader (source); String line; while ((line = Streams.readLine (in)) != null) lines.add (line); return lines; } } avis-1.2.2/common/src/test/org/avis/util/LogFailTester.java0000644000175000017500000000506211147555544023435 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.ArrayList; import java.util.List; import org.avis.logging.LogEvent; import org.avis.logging.LogListener; import static org.avis.logging.Log.ALARM; import static org.avis.logging.Log.WARNING; import static org.avis.logging.Log.addLogListener; import static org.avis.logging.Log.enableLogging; import static org.avis.logging.Log.removeLogListener; import static org.avis.logging.Log.shouldLog; import static org.avis.logging.Log.toLogString; import static org.junit.Assert.fail; /** * Automatically fails a test if an error or warning is logged. * * @author Matthew Phillips */ public class LogFailTester implements LogListener { private List errors; private volatile boolean paused; private boolean wasLoggingErrors; public LogFailTester () { this.errors = new ArrayList (); addLogListener (this); } /** * Pause logging and detecting errors/warnings. */ public void pause () { if (paused) return; paused = true; wasLoggingErrors = shouldLog (ALARM); enableLogging (ALARM, false); enableLogging (WARNING, false); } /** * Reverse the effect of pause. */ public void unpause () { if (!paused) return; paused = false; enableLogging (ALARM, wasLoggingErrors); enableLogging (WARNING, wasLoggingErrors); } /** * Assert there were no errors/warnings logged and dispose. */ public void assertOkAndDispose () { removeLogListener (this); if (!errors.isEmpty ()) { StringBuilder errorMessages = new StringBuilder (); for (LogEvent e : errors) errorMessages.append (toLogString (e)).append ('\n'); fail ("Errors/warnings in log: " + errorMessages); } } public void messageLogged (LogEvent e) { if (!paused && e.type >= WARNING) errors.add (e); } }avis-1.2.2/common/src/main/0000755000175000017500000000000011147555544015322 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/0000755000175000017500000000000011147555544016111 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/0000755000175000017500000000000011147555544017053 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/common/0000755000175000017500000000000011147555544020343 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/common/RuntimeInterruptedException.java0000644000175000017500000000213111147555544026733 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.common; /** * Like InterruptedException, but less annoying. Classes that use * Thread.wait () etc and want to avoid everyhing up the chain * declaring InterruptedException, should catch it and re-throw this. * * @author Matthew Phillips */ public class RuntimeInterruptedException extends RuntimeException { public RuntimeInterruptedException (InterruptedException cause) { super (cause); } } avis-1.2.2/common/src/main/org/avis/common/ElvinURI.java0000644000175000017500000002667111147555544022657 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.common; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.Integer.parseInt; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static org.avis.common.Common.CLIENT_VERSION_MAJOR; import static org.avis.common.Common.CLIENT_VERSION_MINOR; import static org.avis.common.Common.DEFAULT_PORT; import static org.avis.util.Collections.join; import static org.avis.util.Collections.list; /** * An Elvin URI identifying an Elvin router as described in the "Elvin * URI Scheme" specification at * http://elvin.org/specs/draft-elvin-uri-prelim-02.txt. The most * common Elvin URI for a TCP endpoint is of the form (sections in in [] * are optional): * *
 *   elvin:[version]/[protocol]/hostname[:port][;options]
 *   
 *   version:  protocol version major.minor form e.g. "4.0"
 *   protocol: protocol stack in transport,security,marshalling order
 *             e.g. "tcp,none,xdr". Alternatively the alias "secure"
 *             can be used to denote the default secure stack
 *             ("ssl,none,xdr").
 *   options:  name1=value1[;name2=value2]* e.g. foo=bar;black=white
 * 
* *

* * Example URI 1: elvin://localhost:2917 *

* Example URI 2: elvin://192.168.0.2:2917;foo=true *

* Example URI 3: elvin:4.0/ssl,none,xdr/localhost:443 * * @author Matthew Phillips */ public class ElvinURI { private static final List DEFAULT_PROTOCOL = list ("tcp", "none", "xdr"); /* * Note: you might expect "tcp,ssl,xdr" as the logical secure stack, * but Mantara Elvin uses "ssl,none,xdr" and spec also indicates * this is correct, so we comply. */ private static final List SECURE_PROTOCOL = list ("ssl", "none", "xdr"); /** * Basic matcher for URI's. Key sections pulled out here: detail * parsing is done as a separate pass. */ private static final Pattern URL_PATTERN = // NB: key sections of regexp are marked with | below // |scheme|ver |protocol|host:port |options Pattern.compile ("(\\w+):([^/]+)?/([^/]+)?/([^;/][^;]*)(;.*)?"); /** * The original URI string as passed into the constructor. */ public String uriString; /** * The URI scheme (i.e the part before the ":"). This must be * "elvin" for URI's referring to Elvin routers. */ public String scheme; /** * Major protocol version. Default is {@link Common#CLIENT_VERSION_MAJOR}. */ public int versionMajor; /** * Minor protocol version. Default is {@link Common#CLIENT_VERSION_MINOR}. */ public int versionMinor; /** * The stack of protocol modules in (transport,security,marshalling) * order. e.g. "tcp", "none", "xdr". See also * {@link #defaultProtocol()} */ public List protocol; /** * The host name. */ public String host; /** * The port. Default is {@link Common#DEFAULT_PORT}. */ public int port; /** * The URI options. e.g. elvin://host:port;option1=value1;option2=value2 */ public Map options; private int hash; /** * Create a new instance. * * @param uriString The URI. * * @throws InvalidURIException if the URI is not valid. */ public ElvinURI (String uriString) throws InvalidURIException { init (); this.uriString = uriString; parseUri (); validate (); this.hash = computeHash (); } /** * Create a new instance from a host and port using defaults for others. * * @param host Host name or IP address * @param port Port number. */ public ElvinURI (String host, int port) { init (); this.uriString = "elvin://" + host + ':' + port; this.scheme = "elvin"; this.host = host; this.port = port; this.hash = computeHash (); } /** * Create a new instance using an existing URI for defaults. * * @param uriString The URI string. * @param defaultUri The URI to use for any values that are not * specified by uriString. * @throws InvalidURIException if the URI is not valid. */ public ElvinURI (String uriString, ElvinURI defaultUri) throws InvalidURIException { init (defaultUri); this.uriString = uriString; parseUri (); validate (); this.hash = computeHash (); } /** * Create a copy of a URI. * * @param defaultUri The URI to copy. */ public ElvinURI (ElvinURI defaultUri) { init (defaultUri); validate (); } protected void init (ElvinURI defaultUri) { this.uriString = defaultUri.uriString; this.scheme = defaultUri.scheme; this.versionMajor = defaultUri.versionMajor; this.versionMinor = defaultUri.versionMinor; this.protocol = defaultUri.protocol; this.host = defaultUri.host; this.port = defaultUri.port; this.options = defaultUri.options; this.hash = defaultUri.hash; } protected void init () { this.scheme = null; this.versionMajor = CLIENT_VERSION_MAJOR; this.versionMinor = CLIENT_VERSION_MINOR; this.protocol = DEFAULT_PROTOCOL; this.host = null; this.port = DEFAULT_PORT; this.options = emptyMap (); } private void validate () { if (!validScheme (scheme)) throw new InvalidURIException (uriString, "Invalid scheme: " + scheme); } /** * Check if scheme is valid. May be extended. */ protected boolean validScheme (String schemeToCheck) { return schemeToCheck.equals ("elvin"); } @Override public String toString () { return uriString; } /** * Generate a canonical text version of this URI. */ public String toCanonicalString () { StringBuilder str = new StringBuilder (); str.append (scheme).append (':'); str.append (versionMajor).append ('.').append (versionMinor); str.append ('/'); join (str, protocol, ','); str.append ('/').append (host).append (':').append (port); // NB: options is a sorted map, canonical order is automatic for (Entry option : options.entrySet ()) { str.append (';'); str.append (option.getKey ()).append ('=').append (option.getValue ()); } return str.toString (); } @Override public int hashCode () { return hash; } @Override public boolean equals (Object obj) { return obj instanceof ElvinURI && equals ((ElvinURI)obj); } public boolean equals (ElvinURI uri) { return hash == uri.hash && scheme.equals (uri.scheme) && host.equals (uri.host) && port == uri.port && versionMajor == uri.versionMajor && versionMinor == uri.versionMinor && options.equals (uri.options) && protocol.equals (uri.protocol); } private int computeHash () { return scheme.hashCode () ^ host.hashCode () ^ port ^ protocol.hashCode (); } private void parseUri () throws InvalidURIException { Matcher matcher = URL_PATTERN.matcher (uriString); if (!matcher.matches ()) throw new InvalidURIException (uriString, "Not a valid Elvin URI"); scheme = matcher.group (1); // version if (matcher.group (2) != null) parseVersion (matcher.group (2)); // protocol if (matcher.group (3) != null) parseProtocol (matcher.group (3)); // endpoint (host/port) parseEndpoint (matcher.group (4)); // options if (matcher.group (5) != null) parseOptions (matcher.group (5)); } private void parseVersion (String versionExpr) throws InvalidURIException { Matcher versionMatch = Pattern.compile ("(\\d+)(?:\\.(\\d+))?").matcher (versionExpr); if (versionMatch.matches ()) { try { versionMajor = parseInt (versionMatch.group (1)); if (versionMatch.group (2) != null) versionMinor = parseInt (versionMatch.group (2)); } catch (NumberFormatException ex) { throw new InvalidURIException (uriString, "Number too large in version string: \"" + versionExpr + "\""); } } else { throw new InvalidURIException (uriString, "Invalid version string: \"" + versionExpr + "\""); } } private void parseProtocol (String protocolExpr) throws InvalidURIException { Matcher protocolMatch = Pattern.compile ("(?:(\\w+),(\\w+),(\\w+))|secure").matcher (protocolExpr); if (protocolMatch.matches ()) { if (protocolMatch.group (1) != null) protocol = asList (protocolExpr.split (",")); else protocol = SECURE_PROTOCOL; } else { throw new InvalidURIException (uriString, "Invalid protocol: \"" + protocolExpr + "\""); } } private void parseEndpoint (String endpoint) throws InvalidURIException { Pattern pattern; // choose between IPv6 and IPv4 address scheme if (endpoint.charAt (0) == '[') pattern = Pattern.compile ("(\\[[^\\]]+\\])(?::(\\d+))?"); else pattern = Pattern.compile ("([^:]+)(?::(\\d+))?"); Matcher endpointMatch = pattern.matcher (endpoint); if (endpointMatch.matches ()) { host = endpointMatch.group (1); if (endpointMatch.group (2) != null) port = parseInt (endpointMatch.group (2)); } else { throw new InvalidURIException (uriString, "Invalid port number"); } } private void parseOptions (String optionsExpr) throws InvalidURIException { Matcher optionMatch = Pattern.compile (";([^=;]+)=([^=;]*)").matcher (optionsExpr); options = new TreeMap (); int index = 0; while (optionMatch.lookingAt ()) { options.put (optionMatch.group (1), optionMatch.group (2)); index = optionMatch.end (); optionMatch.region (index, optionsExpr.length ()); } if (index != optionsExpr.length ()) throw new InvalidURIException (uriString, "Invalid options: \"" + optionsExpr + "\""); } /** * The default URI protocol stack: "tcp", "none", "xdr" */ public static List defaultProtocol () { return DEFAULT_PROTOCOL; } /** * The secure URI protocol stack: "ssl", "none", "xdr" */ public static List secureProtocol () { return SECURE_PROTOCOL; } /** * True if this URI specifies secure TLS transport (protocol.equals * (secureProtocol ())). */ public boolean isSecure () { return protocol.equals (secureProtocol ()); } } avis-1.2.2/common/src/main/org/avis/common/InvalidURIException.java0000644000175000017500000000224711147555544025040 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.common; import java.net.URISyntaxException; /** * Unchecked invalid URI exception. This is used in places that it * would be irritating to use the checked Java {@link URISyntaxException}. * * @author Matthew Phillips */ public class InvalidURIException extends RuntimeException { public InvalidURIException (String uri, String message) { super ("Invalid URI \"" + uri + "\": " + message); } public InvalidURIException (URISyntaxException ex) { super (ex); } } avis-1.2.2/common/src/main/org/avis/common/Common.java0000644000175000017500000000220711147555544022437 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.common; /** * Common Avis definitions. * * @author Matthew Phillips */ public final class Common { public static final int K = 1024; public static final int MB = 1024 * 1024; public static final int MAX = Integer.MAX_VALUE; public static final int DEFAULT_PORT = 2917; public static final int CLIENT_VERSION_MAJOR = 4; public static final int CLIENT_VERSION_MINOR = 0; private Common () { // cannot be instantiated } } avis-1.2.2/common/src/main/org/avis/security/0000755000175000017500000000000011147555544020722 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/security/DualKeySet.java0000644000175000017500000000705011147555544023601 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; import java.util.HashSet; import java.util.Set; import static java.util.Collections.emptySet; import static org.avis.util.Collections.difference; import static org.avis.security.DualKeyScheme.Subset.PRODUCER; /** * A pair of key sets (producer/consumer) used for dual key schemes. * * @see Keys * @see DualKeyScheme * * @author Matthew Phillips */ class DualKeySet implements KeySet { public final Set producerKeys; public final Set consumerKeys; DualKeySet () { this.producerKeys = new HashSet (); this.consumerKeys = new HashSet (); } /** * Create an immutable empty instance. * * @param immutable Used as a flag. */ DualKeySet (boolean immutable) { this.producerKeys = emptySet (); this.consumerKeys = emptySet (); } DualKeySet (Set producerKeys, Set consumerKeys) { this.producerKeys = producerKeys; this.consumerKeys = consumerKeys; } /** * Get the keys for a producer or consumer. * * @param subset One of PRODUCER or CONSUMER. */ public Set keysFor (DualKeyScheme.Subset subset) { if (subset == PRODUCER) return producerKeys; else return consumerKeys; } public boolean isEmpty () { return producerKeys.isEmpty () && consumerKeys.isEmpty (); } public int size () { return producerKeys.size () + consumerKeys.size (); } public void add (KeySet theKeys) throws IllegalArgumentException { DualKeySet keys = (DualKeySet)theKeys; producerKeys.addAll (keys.producerKeys); consumerKeys.addAll (keys.consumerKeys); } public void remove (KeySet theKeys) throws IllegalArgumentException { DualKeySet keys = (DualKeySet)theKeys; producerKeys.removeAll (keys.producerKeys); consumerKeys.removeAll (keys.consumerKeys); } public boolean add (Key key) throws IllegalArgumentException, UnsupportedOperationException { throw new UnsupportedOperationException ("Cannot add to a dual key set"); } public boolean remove (Key key) throws IllegalArgumentException, UnsupportedOperationException { throw new UnsupportedOperationException ("Cannot remove from a dual key set"); } public KeySet subtract (KeySet theKeys) { DualKeySet keys = (DualKeySet)theKeys; return new DualKeySet (difference (producerKeys, keys.producerKeys), difference (consumerKeys, keys.consumerKeys)); } @Override public boolean equals (Object object) { return object instanceof DualKeySet && equals ((DualKeySet)object); } public boolean equals (DualKeySet keyset) { return producerKeys.equals (keyset.producerKeys) && consumerKeys.equals (keyset.consumerKeys); } @Override public int hashCode () { return producerKeys.hashCode () ^ consumerKeys.hashCode (); } } avis-1.2.2/common/src/main/org/avis/security/Key.java0000644000175000017500000000524211147555544022320 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; import static org.avis.io.XdrCoding.toUTF8; import java.util.Arrays; /** * A key value used to secure notifications. A key is simply an * immutable block of bytes. *

* Elvin defines two types of key, private (or raw) * keys, and public (or prime) keys. A public * key is a one-way hash (e.g. using SHA-1) of a private key. A * private key may be any random data, or simply a password. A private * key is defined to match a public key if the corresponding hash of * its data matches the public key's data, e.g. if * sha1 (privateKey.data) == publicKey.data. *

* Note that this is not a public key system in the RSA sense but * that, like RSA, public keys can be shared in the open without loss * of security. *

* This class precomputes a hash code for the key data to accelerate * equals () and hashCode (). * * @author Matthew Phillips */ public final class Key { /** * The key's data block. */ public final byte [] data; private int hash; /** * Create a key from a password by using the password's UTF-8 * representation as the data block. */ public Key (String password) { this (toUTF8 (password)); } /** * Create a key from a block of data. */ public Key (byte [] data) { if (data == null || data.length == 0) throw new IllegalArgumentException ("Key data cannot be empty"); this.data = data; this.hash = Arrays.hashCode (data); } /** * Shortcut to generate the public (prime) key for a given scheme. * * @see KeyScheme#publicKeyFor(Key) */ public Key publicKeyFor (KeyScheme scheme) { return scheme.publicKeyFor (this); } @Override public boolean equals (Object object) { return object instanceof Key && equals ((Key)object); } public boolean equals (Key key) { return hash == key.hash && Arrays.equals (data, key.data); } @Override public int hashCode () { return hash; } } avis-1.2.2/common/src/main/org/avis/security/KeyScheme.java0000644000175000017500000001651411147555544023451 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; import java.util.Set; import static org.avis.security.SecureHash.SHA1; import static org.avis.util.Collections.set; /** * An enumeration of supported Elvin security schemes. A key scheme * defines a mode of sending or receiving notifications securely. * *

The Producer Scheme

* * In the producer scheme, consumers of notifications ensure that a * notification producer is known to them. The producer uses the * private key, and consumers use the public key. If the producer * keeps its private key secure, consumers can be assured they are * receiving notifications from a trusted producer. * *

The Consumer Scheme

* * In the consumer scheme, producers of notifications ensure that a * notification consumer is known to them, i.e. the producer controls * who can receive its notifications. In this scheme -- the reverse of * the producer scheme -- the consumer uses the private key, and * producers use the public key. If the consumer keeps its private key * secure, then the producer can be assured that only the trusted * consumer can receive its notifications. * *

The Dual Scheme

* * The dual scheme combines both the producer and consumer schemes, so * that both ends can send and receive securely. Typically both ends * exchange public keys, and each end then emits notifications with * both its private key and the public key(s) of its intended * consumer(s) attached. Similarly, each end would subscribe using its * private key and the public key(s) of its intended producer(s). * *

Avis Key Scheme API

* * The Elvin Producer and Consumer schemes both use a single set of * keys, whereas the Dual scheme requires both a consumer key set and * a producer key set. The schemes that require a single set of keys * are defined by an instance of {@link SingleKeyScheme}, the Dual * scheme is defined by an instance of {@link DualKeyScheme}. *

* Each key scheme also defines a {@link #keyHash secure hash} for * generating its public keys: see the documentation on * {@linkplain Key security keys} for more information on public and * private keys used in key schemes. * *

Supported Schemes

* * Avis currently supports just the SHA-1 secure hash as defined in * version 4.0 of the Elvin protocol. As such, three schemes are * available: {@link #SHA1_CONSUMER SHA1-Consumer}, * {@link #SHA1_PRODUCER SHA1-Producer} and * {@link #SHA1_DUAL SHA1-Dual}. * * @author Matthew Phillips */ public abstract class KeyScheme { /** * The SHA-1 Dual key scheme. */ public static final DualKeyScheme SHA1_DUAL = new DualKeyScheme (1, SHA1); /** * The SHA-1 Producer key scheme. */ public static final SingleKeyScheme SHA1_PRODUCER = new SingleKeyScheme (2, SHA1, true, false); /** * The SHA-1 Consumer key scheme. */ public static final SingleKeyScheme SHA1_CONSUMER = new SingleKeyScheme (3, SHA1, false, true); private static final Set SCHEMES = set (SHA1_CONSUMER, SHA1_PRODUCER, SHA1_DUAL); /** * The unique ID of the scheme. This is the same as the on-the-wire * ID used by Elvin. */ public final int id; /** * True if this scheme is a producer scheme. */ public final boolean producer; /** * True of this scheme is a consumer scheme. */ public final boolean consumer; /** * The secure hash used in this scheme. */ public final SecureHash keyHash; /** * The unique, human-readable name of this scheme. */ public final String name; KeyScheme (int id, SecureHash keyHash, boolean producer, boolean consumer) { this.id = id; this.producer = producer; this.consumer = consumer; this.keyHash = keyHash; this.name = createName (); } /** * True if the scheme requires dual key sets. */ public boolean isDual () { return producer && consumer; } /** * Create the public (aka prime) key for a given private (aka raw) * key using this scheme's hash. */ public Key publicKeyFor (Key privateKey) { return new Key (keyHash.hash (privateKey.data)); } /** * Match a producer/consumer keyset in the current scheme. * * @param producerKeys The producer keys. * @param consumerKeys The consumer keys. * @return True if a consumer using consumerKeys could receive a * notification from a producer with producerKeys in this * scheme. */ boolean match (KeySet producerKeys, KeySet consumerKeys) { if (isDual ()) { DualKeySet keys1 = (DualKeySet)producerKeys; DualKeySet keys2 = (DualKeySet)consumerKeys; return matchKeys (keys1.producerKeys, keys2.producerKeys) && matchKeys (keys2.consumerKeys, keys1.consumerKeys); } else if (producer) { return matchKeys ((SingleKeySet)producerKeys, (SingleKeySet)consumerKeys); } else { return matchKeys ((SingleKeySet)consumerKeys, (SingleKeySet)producerKeys); } } /** * Match a set of private keys with a set of public keys. * * @param privateKeys A set of private (aka raw) keys. * @param publicKeys A set of public (aka prime) keys. * @return True if at least one private key mapped to its public * version (using this scheme's hash) was in the given * public key set. */ private boolean matchKeys (Set privateKeys, Set publicKeys) { for (Key privateKey : privateKeys) { if (publicKeys.contains (privateKey)) return true; } return false; } @Override public boolean equals (Object object) { return object == this; } @Override public int hashCode () { return id; } @Override public String toString () { return name; } private String createName () { StringBuilder str = new StringBuilder (); str.append (keyHash.name ()).append ('-'); if (isDual ()) str.append ("dual"); else if (producer) str.append ("producer"); else str.append ("consumer"); return str.toString (); } /** * Look up the scheme for a given ID. * * @throws IllegalArgumentException if id is not a known scheme ID. */ public static KeyScheme schemeFor (int id) throws IllegalArgumentException { switch (id) { case 1: return SHA1_DUAL; case 2: return SHA1_PRODUCER; case 3: return SHA1_CONSUMER; default: throw new IllegalArgumentException ("Invalid key scheme ID: " + id); } } /** * The set of all supported schemes. */ public static Set schemes () { return SCHEMES; } } avis-1.2.2/common/src/main/org/avis/security/KeySet.java0000644000175000017500000000301311147555544022766 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; /** * A polymorphic key set stored as part of a {@link Keys} key * collection: may be either a single set of Key items or a dual set * for the dual key schemes. Clients should not generally need to * access key sets directly: use the {@link Keys} class instead. * * @author Matthew Phillips */ interface KeySet { public int size (); public boolean isEmpty (); public void add (KeySet keys) throws IllegalArgumentException; public void remove (KeySet keys) throws IllegalArgumentException; public boolean add (Key key) throws IllegalArgumentException, UnsupportedOperationException; public boolean remove (Key key) throws IllegalArgumentException, UnsupportedOperationException; /** * Return this key with the given set removed. */ public KeySet subtract (KeySet keys); } avis-1.2.2/common/src/main/org/avis/security/Keys.java0000644000175000017500000004177311147555544022514 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.DualKeyScheme.Subset; import static org.avis.io.XdrCoding.getBytes; import static org.avis.io.XdrCoding.putBytes; import static org.avis.security.DualKeyScheme.Subset.CONSUMER; import static org.avis.security.DualKeyScheme.Subset.PRODUCER; import static org.avis.security.KeyScheme.schemeFor; import static org.avis.security.Keys.Delta.EMPTY_DELTA; /** * A key collection used to secure notifications. A key collection * contains zero or more mappings from a {@linkplain KeyScheme key * scheme} to the {@linkplain Key keys} registered for that scheme. *

* Once in use, key collections should be treated as immutable * i.e. never be modified directly after construction. *

* See also section 7.4 of the client protocol spec. * * @author Matthew Phillips */ public class Keys { /** An empty, immutable key collection. */ public static final Keys EMPTY_KEYS = new EmptyKeys (); private static final DualKeySet EMPTY_DUAL_KEYSET = new DualKeySet (true); private static final SingleKeySet EMPTY_SINGLE_KEYSET = new EmptySingleKeys (); /** * NB: this set must be kept normalised i.e. if there is a key * scheme in the map, then there must be a non-empty key set * associated with it. */ private Map keySets; public Keys () { // todo opt: since schemes are static, could use a more optimized map here keySets = new HashMap (4); } public Keys (Keys keys) { this (); add (keys); } /** * True if no keys are in the collection. */ public boolean isEmpty () { return keySets.isEmpty (); } /** * Return the total number of keys in this key collection. */ public int size () { if (isEmpty ()) return 0; int size = 0; for (KeySet keyset : keySets.values ()) size += keyset.size (); return size; } /** * Shortcut to efficiently generate a key collection that represents * this key collection's union with another. * * @param keys The keys to add. * * @return If keys is empty, this method will simply return this * collection. If this collection is empty, keys will be * returned. Otherwise a new collection instance is created * as the union of both. */ public Keys addedTo (Keys keys) { if (keys.isEmpty ()) { return this; } else if (isEmpty ()) { return keys; } else { Keys newKeys = new Keys (this); newKeys.add (keys); return newKeys; } } /** * Add a key for single key scheme. * * @param scheme The key scheme. * @param key The key to add. * * @see #remove(SingleKeyScheme, Key) */ public void add (SingleKeyScheme scheme, Key key) { newKeysetFor (scheme).add (key); } /** * Remove a key for single key scheme. * * @param scheme The key scheme. * @param key The key to remove. * * @see #add(SingleKeyScheme, Key) */ public void remove (SingleKeyScheme scheme, Key key) throws IllegalArgumentException { KeySet keys = keySets.get (scheme); if (keys != null) { keys.remove (key); if (keys.isEmpty ()) keySets.remove (scheme); } } /** * Add a key for dual key scheme. * * @param scheme The key scheme. * @param subset The key subset (PRODUCER or CONSUMER) to add the key to. * @param key The key to add. * * @see #remove(DualKeyScheme, org.avis.security.DualKeyScheme.Subset, Key) */ public void add (DualKeyScheme scheme, DualKeyScheme.Subset subset, Key key) throws IllegalArgumentException { newKeysetFor (scheme).keysFor (subset).add (key); } /** * Remove a key for dual key scheme. * * @param scheme The key scheme. * @param subset The key subset (PRODUCER or CONSUMER) to remove the * key from. * @param key The key to remove. * * @see #add(DualKeyScheme, org.avis.security.DualKeyScheme.Subset, * Key) */ public void remove (DualKeyScheme scheme, DualKeyScheme.Subset subset, Key key) { DualKeySet keySet = (DualKeySet)keySets.get (scheme); if (keySet != null) { keySet.keysFor (subset).remove (key); if (keySet.isEmpty ()) keySets.remove (scheme); } } /** * Add all keys in a collection. * * @param keys The keys to add. * * @see #remove(Keys) */ public void add (Keys keys) { if (keys == this) throw new IllegalArgumentException ("Cannot add key collection to itself"); for (KeyScheme scheme: keys.keySets.keySet ()) add (scheme, keys.keySets.get (scheme)); } private void add (KeyScheme scheme, KeySet keys) { if (!keys.isEmpty ()) newKeysetFor (scheme).add (keys); } /** * Remove all keys in a collection. * * @param keys The keys to remove. * * @see #add(Keys) */ public void remove (Keys keys) { if (keys == this) throw new IllegalArgumentException ("Cannot remove key collection from itself"); for (KeyScheme scheme: keys.keySets.keySet ()) { KeySet myKeys = keySets.get (scheme); if (myKeys != null) { myKeys.remove (keys.keysetFor (scheme)); if (myKeys.isEmpty ()) keySets.remove (scheme); } } } /** * Create a new key collection with some keys added/removed. This * does not modify the current collection. * * @param toAdd Keys to add. * @param toRemove Keys to remove * * @return A new key set with keys added/removed. If both add/remove * key sets are empty, this returns the current instance. * * @see #deltaFrom(Keys) */ public Keys delta (Keys toAdd, Keys toRemove) { if (toAdd.isEmpty () && toRemove.isEmpty ()) { return this; } else { Keys keys = new Keys (this); keys.add (toAdd); keys.remove (toRemove); return keys; } } /** * Compute the changes between one key collection and another. * * @param keys The target key collection. * @return The delta (i.e. key sets to be added and removed) * required to change this collection into the target. * * @see #delta(Keys, Keys) */ public Delta deltaFrom (Keys keys) { if (keys == this) return EMPTY_DELTA; Keys addedKeys = new Keys (); Keys removedKeys = new Keys (); for (KeyScheme scheme : KeyScheme.schemes ()) { KeySet existingKeyset = keysetFor (scheme); KeySet otherKeyset = keys.keysetFor (scheme); addedKeys.add (scheme, otherKeyset.subtract (existingKeyset)); removedKeys.add (scheme, existingKeyset.subtract (otherKeyset)); } return new Delta (addedKeys, removedKeys); } /** * Get the key set for a given scheme. This set should not be * modified. * * @param scheme The scheme. * @return The key set for the scheme. Will be empty if no keys are * defined for the scheme. * * @see #keysetFor(DualKeyScheme) * @see #keysetFor(SingleKeyScheme) */ private KeySet keysetFor (KeyScheme scheme) { KeySet keys = keySets.get (scheme); if (keys == null) return scheme.isDual () ? EMPTY_DUAL_KEYSET : EMPTY_SINGLE_KEYSET; else return keys; } /** * Get the key set for a dual scheme. This set should not be * modified. * * @param scheme The scheme. * @return The key set for the scheme. Will be empty if no keys are * defined for the scheme. * * @see #keysetFor(KeyScheme) * @see #keysetFor(SingleKeyScheme) */ DualKeySet keysetFor (DualKeyScheme scheme) { DualKeySet keys = (DualKeySet)keySets.get (scheme); if (keys == null) return EMPTY_DUAL_KEYSET; else return keys; } /** * Get the key set for a single scheme. This set should not be * modified. * * @param scheme The scheme. * @return The key set for the scheme. Will be empty if no keys are * defined for the scheme. * * @see #keysetFor(KeyScheme) * @see #keysetFor(DualKeyScheme) */ SingleKeySet keysetFor (SingleKeyScheme scheme) { SingleKeySet keys = (SingleKeySet)keySets.get (scheme); if (keys == null) return EMPTY_SINGLE_KEYSET; else return keys; } /** * Lookup/create a key set for a scheme. */ private KeySet newKeysetFor (KeyScheme scheme) { if (scheme.isDual ()) return newKeysetFor ((DualKeyScheme)scheme); else return newKeysetFor ((SingleKeyScheme)scheme); } /** * Lookup/create a key set for a single key scheme. */ private SingleKeySet newKeysetFor (SingleKeyScheme scheme) { SingleKeySet keys = (SingleKeySet)keySets.get (scheme); if (keys == null) { keys = new SingleKeySet (); keySets.put (scheme, keys); } return keys; } /** * Lookup/create a key set for a single key scheme. */ private DualKeySet newKeysetFor (DualKeyScheme scheme) { DualKeySet keys = (DualKeySet)keySets.get (scheme); if (keys == null) { keys = new DualKeySet (); keySets.put (scheme, keys); } return keys; } /** * Turn all private keys for a given role into their public versions * by hashing them in place. Clients should never need to use this. * * @param role PRODUCER (producer keys are hashed) or CONSUMER * (consumer keys are hashed). */ public void hashPrivateKeysForRole (Subset role) { if (isEmpty ()) return; for (Map.Entry entry : keySets.entrySet ()) { KeyScheme scheme = entry.getKey (); if (scheme.isDual ()) { hashKeys (scheme, ((DualKeySet)entry.getValue ()).keysFor (role)); } else if (scheme.consumer && role == CONSUMER || scheme.producer && role == PRODUCER) { hashKeys (scheme, (SingleKeySet)entry.getValue ()); } } } private void hashKeys (KeyScheme scheme, Set keys) { if (!keys.isEmpty ()) { Collection publicKeys = new ArrayList (keys.size ()); for (Key key : keys) publicKeys.add (key.publicKeyFor (scheme)); keys.clear (); keys.addAll (publicKeys); } } /** * Test whether a given key collection matches this one for the * purpose of notification delivery. Clients should never need to use * this. The keys are all assumed to be public (prime) keys. * * @param producerKeys The producer keys to match against this * (consumer) key collection. * @return True if a consumer using this key collection could * receive a notification from a producer with the given * producer key collection. */ public boolean match (Keys producerKeys) { if (isEmpty () || producerKeys.isEmpty ()) return false; for (Entry entry : producerKeys.keySets.entrySet ()) { KeyScheme scheme = entry.getKey (); KeySet keyset = entry.getValue (); if (keySets.containsKey (scheme) && scheme.match (keyset, keySets.get (scheme))) { return true; } } return false; } public void encode (ByteBuffer out) { // number of key schemes in the list out.putInt (keySets.size ()); for (Entry entry : keySets.entrySet ()) { KeyScheme scheme = entry.getKey (); KeySet keySet = entry.getValue (); // scheme ID out.putInt (scheme.id); if (scheme.isDual ()) { DualKeySet dualKeySet = (DualKeySet)keySet; out.putInt (2); encodeKeys (out, dualKeySet.producerKeys); encodeKeys (out, dualKeySet.consumerKeys); } else { out.putInt (1); encodeKeys (out, (SingleKeySet)keySet); } } } public static Keys decode (ByteBuffer in) throws ProtocolCodecException { int length = in.getInt (); if (length == 0) return EMPTY_KEYS; try { Keys keys = new Keys (); for ( ; length > 0; length--) { KeyScheme scheme = schemeFor (in.getInt ()); int keySetCount = in.getInt (); if (scheme.isDual ()) { if (keySetCount != 2) throw new ProtocolCodecException ("Dual key scheme with " + keySetCount + " key sets"); DualKeySet keyset = keys.newKeysetFor ((DualKeyScheme)scheme); decodeKeys (in, keyset.producerKeys); decodeKeys (in, keyset.consumerKeys); } else { if (keySetCount != 1) throw new ProtocolCodecException ("Single key scheme with " + keySetCount + " key sets"); decodeKeys (in, keys.newKeysetFor ((SingleKeyScheme)scheme)); } } return keys; } catch (IllegalArgumentException ex) { // most likely an invalid KeyScheme ID throw new ProtocolCodecException (ex); } } private static void encodeKeys (ByteBuffer out, Set keys) { out.putInt (keys.size ()); for (Key key : keys) putBytes (out, key.data); } private static void decodeKeys (ByteBuffer in, Set keys) throws ProtocolCodecException { for (int keysetCount = in.getInt (); keysetCount > 0; keysetCount--) keys.add (new Key (getBytes (in))); } @Override public boolean equals (Object object) { return object instanceof Keys && equals ((Keys)object); } public boolean equals (Keys keys) { if (keySets.size () != keys.keySets.size ()) return false; for (KeyScheme scheme: keys.keySets.keySet ()) { if (!keysetFor (scheme).equals (keys.keysetFor (scheme))) return false; } return true; } @Override public int hashCode () { // todo opt get a better hash code? int hash = 0; for (KeyScheme scheme : keySets.keySet ()) hash ^= 1 << scheme.id; return hash; } /** * Represents a delta (diff) between two key sets. */ public static class Delta { public static final Delta EMPTY_DELTA = new Delta (EMPTY_KEYS, EMPTY_KEYS); public final Keys added; public final Keys removed; Delta (Keys added, Keys removed) { this.added = added; this.removed = removed; } public boolean isEmpty () { return added.isEmpty () && removed.isEmpty (); } } static class EmptySingleKeys extends SingleKeySet { @Override public boolean add (Key key) throws IllegalArgumentException { throw new UnsupportedOperationException (); } @Override public void add (KeySet keys) throws IllegalArgumentException, UnsupportedOperationException { throw new UnsupportedOperationException (); } @Override public boolean remove (Key key) throws IllegalArgumentException, UnsupportedOperationException { return false; } @Override public void remove (KeySet keys) throws IllegalArgumentException { // zip } } static class EmptyKeys extends Keys { @Override public void add (Keys keys) { throw new UnsupportedOperationException (); } @Override public void remove (Keys keys) { throw new UnsupportedOperationException (); } @Override public void add (SingleKeyScheme scheme, Key key) { throw new UnsupportedOperationException (); } @Override public void remove (SingleKeyScheme scheme, Key key) { throw new UnsupportedOperationException (); } @Override public void add (DualKeyScheme scheme, DualKeyScheme.Subset subset, Key key) { throw new UnsupportedOperationException (); } @Override public void remove (DualKeyScheme scheme, DualKeyScheme.Subset subset, Key key) { throw new UnsupportedOperationException (); } } } avis-1.2.2/common/src/main/org/avis/security/DualKeyScheme.java0000644000175000017500000000226111147555544024251 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; /** * A key scheme that requires a pair of keys. e.g. SHA-1 Dual. * * @author Matthew Phillips */ public final class DualKeyScheme extends KeyScheme { /** * Specifies which of the two subsets of a dual scheme a key is part * of: the producer subset (for sending notifications) or consumer * subset (for receiving notifications). */ public enum Subset {PRODUCER, CONSUMER} DualKeyScheme (int id, SecureHash keyHash) { super (id, keyHash, true, true); } } avis-1.2.2/common/src/main/org/avis/security/SHA1.java0000644000175000017500000010247211147555544022267 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; /* * Modified version of SHA1 class from URL below. Removed dependence on Sun * JCE. * * http://www.limewire.org/fisheye/viewrep/~raw,r=1.2/limecvs/core/com/limegroup/gnutella/SHA1.java * * @author Matthew Phillips */ /* * @(#)SHA1.java 1.9 2004-04-22 * This file was freely contributed to the LimeWire project and is covered * by its existing GPL licence, but it may be used individually as a public * domain implementation of a published algorithm (see below for references). * It was also freely contributed to the Bitzi public domain sources. * @author Philippe Verdy */ /** * Implements the SHA-1 secure hash algorithm. *

* The FIPS PUB 180-2 standard specifies four secure hash algorithms (SHA-1, * SHA-256, SHA-384 and SHA-512) for computing a condensed representation of * electronic data (message). When a message of any length < 2^^64 bits (for * SHA-1 and SHA-256) or < 2^^128 bits (for SHA-384 and SHA-512) is input to an * algorithm, the result is an output called a message digest. The message * digests range in length from 160 to 512 bits, depending on the algorithm. * Secure hash algorithms are typically used with other cryptographic * algorithms, such as digital signature algorithms and keyed-hash message * authentication codes, or in the generation of random numbers (bits). *

* *

* The four hash algorithms specified in this "SHS" standard are called secure * because, for a given algorithm, it is computationally infeasible 1) to find a * message that corresponds to a given message digest, or 2) to find two * different messages that produce the same message digest. Any change to a * message will, with a very high probability, result in a different message * digest. This will result in a verification failure when the secure hash * algorithm is used with a digital signature algorithm or a keyed-hash message * authentication algorithm. *

* *

* A "SHS change notice" adds a SHA-224 algorithm for interoperability, which, * like SHA-1 and SHA-256, operates on 512-bit blocks and 32-bit words, *

* *

* References: *

*
    *
  1. NIST FIPS PUB 180-2, "Secure Hash Signature Standard (SHS) with change * notice", National Institute of Standards and Technology (NIST), 2002 August * 1, and U.S. Department of Commerce, August 26.
    * * http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html *
  2. NIST FIPS PUB 180-1, "Secure Hash Standard", U.S. Department of * Commerce, May 1993.
    * * http://www.itl.nist.gov/div897/pubs/fip180-1.htm
  3. *
  4. Bruce Schneier, "Section 18.7 Secure Hash Algorithm (SHA)", * Applied Cryptography, 2nd edition ,
    * John Wiley & Sons, 1996
  5. *
*/ public final class SHA1 implements Cloneable { /** * This implementation returns a fixed-size digest. */ private static final int SHA1_LENGTH = 20; // bytes == 160 bits /** * Private context for incomplete blocks and padding bytes. INVARIANT: padding * must be in 0..63. When the padding reaches 64, a new block is computed, and * the 56 last bytes are kept in the padding history. */ private byte [] pad; private int padding; /** * Private contextual byte count, sent in the next block, after the ending * padding block. */ private long bytes; /** * Private context that contains the current digest key. */ private int hA, hB, hC, hD, hE; /** * Creates a SHA1 object with default initial state. */ public SHA1 () { pad = new byte [64]; engineReset (); } /** * Creates a SHA1 object with state (for cloning). * * @param state * the existing object to clone */ public SHA1 (final SHA1 state) { this (); pad = state.pad.clone (); padding = state.padding; bytes = state.bytes; hA = state.hA; hB = state.hB; hC = state.hC; hD = state.hD; hE = state.hE; } /** * Clones this object. */ @Override public Object clone () { SHA1 that = null; try { that = (SHA1)super.clone (); that.pad = this.pad.clone (); that.padding = this.padding; that.bytes = this.bytes; that.hA = this.hA; that.hB = this.hB; that.hC = this.hC; that.hD = this.hD; that.hE = this.hE; } catch (CloneNotSupportedException e) { // not possible } return that; } /** * Returns the digest length in bytes. * * Can be used to allocate your own output buffer when computing multiple * digests. * * Overrides the protected abstract method of * java.security.MessageDigestSpi. * @return the digest length in bytes. */ public int engineGetDigestLength () { return SHA1_LENGTH; } /** * Initialize or reset the digest context. * * Overrides the protected abstract method of * java.security.MessageDigestSpi. */ protected void engineReset () { int i = 60; do { pad [i] = (byte)0x00; pad [i + 1] = (byte)0x00; pad [i + 2] = (byte)0x00; pad [i + 3] = (byte)0x00; } while ((i -= 4) >= 0); padding = 0; bytes = 0; hA = 0x67452301; hB = 0xefcdab89; hC = 0x98badcfe; hD = 0x10325476; hE = 0xc3d2e1f0; } /** * Updates the digest using the specified byte. Requires internal buffering, * and may be slow. * * Overrides the protected abstract method of java.security.MessageDigestSpi. * @param input * the byte to use for the update. */ public void engineUpdate (byte input) { bytes++; pad [padding] = input; if (++padding == 64) { computeBlock (pad, 0); padding = 0; } } /** * Updates the digest using the specified array of bytes, starting at the * specified offset. * * Input length can be any size. May require internal buffering, if input * blocks are not multiple of 64 bytes. * * Overrides the protected abstract method of java.security.MessageDigestSpi. * @param input * the array of bytes to use for the update. * @param offset * the offset to start from in the array of bytes. * @param len * the number of bytes to use, starting at offset. */ public void engineUpdate (byte [] input, int offset, int len) { if (offset >= 0 && len >= 0 && offset + len <= input.length) { bytes += len; int padlen; if (padding > 0 && len >= (padlen = 64 - padding)) { System.arraycopy (input, offset, pad, padding, padlen); computeBlock (pad, 0); padding = 0; offset += padlen; len -= padlen; } while (len >= 64) { computeBlock (input, offset); offset += 64; len -= 64; } if (len > 0) { System.arraycopy (input, offset, pad, padding, len); padding += len; } return; } throw new ArrayIndexOutOfBoundsException (); } /** * Completes the hash computation by performing final operations such as * padding. Computes the final hash and returns the final value as a byte[20] * array. Once engineDigest has been called, the engine will be automatically * reset as specified in the JavaSecurity MessageDigest specification. * * For faster operations with multiple digests, allocate your own array and * use engineDigest(byte[], int offset, int len). * * Overrides the protected abstract method of java.security.MessageDigestSpi. * @return the length of the digest stored in the output buffer. */ public byte [] engineDigest () { byte hashvalue[] = new byte [SHA1_LENGTH]; engineDigest (hashvalue, 0, hashvalue.length); return hashvalue; } /** * Completes the hash computation by performing final operations such as * padding. Once engineDigest has been called, the engine will be * automatically reset (see engineReset). * * Overrides the protected abstract method of java.security.MessageDigestSpi. * @param hashvalue * the output buffer in which to store the digest. * @param offset * offset to start from in the output buffer * @param len * number of bytes within buf allotted for the digest. Both this * default implementation and the SUN provider do not return partial * digests. The presence of this parameter is solely for consistency * in our API's. If the value of this parameter is less than the * actual digest length, the method will throw a DigestException. * This parameter is ignored if its value is greater than or equal to * the actual digest length. * @return the length of the digest stored in the output buffer. */ public int engineDigest (byte [] hashvalue, int offset, int len) { if (len < SHA1_LENGTH) throw new IllegalArgumentException ("partial digests not returned"); if (hashvalue.length - offset < SHA1_LENGTH) throw new IllegalArgumentException ( "insufficient space in output buffer to store the digest"); // Flush the trailing bytes, adding padding bytes into last blocks. int temp; // Add padding null bytes but replace the last 8 padding bytes by // the little-endian 64-bit digested message bit-length. pad [temp = padding] = (byte)0x80; // required first padding byte value! // Check if 8 bytes available in pad to store the total message size switch (temp) { // INVARIANT: temp must be in [0..63] case 56: pad [57] = (byte)0x00; // no break; falls thru case 57: pad [58] = (byte)0x00; // no break; falls thru case 58: pad [59] = (byte)0x00; // no break; falls thru case 59: pad [60] = (byte)0x00; // no break; falls thru case 60: pad [61] = (byte)0x00; // no break; falls thru case 61: pad [62] = (byte)0x00; // no break; falls thru case 62: pad [63] = (byte)0x00; // no break; falls thru case 63: computeBlock (pad, 0); // Clear the 56 first bytes of pad[]. temp = 52; do { pad [temp] = (byte)0x00; pad [temp + 1] = (byte)0x00; pad [temp + 2] = (byte)0x00; pad [temp + 3] = (byte)0x00; } while ((temp -= 4) >= 0); break; case 52: pad [53] = (byte)0x00; // no break; falls thru case 53: pad [54] = (byte)0x00; // no break; falls thru case 54: pad [55] = (byte)0x00; // no break; falls thru case 55: break; default: // Clear the rest of 56 first bytes of pad[]. switch (temp & 3) { case 3: temp++; break; case 2: pad [(temp += 2) - 1] = (byte)0x00; break; case 1: pad [(temp += 3) - 2] = (byte)0x00; pad [temp - 1] = (byte)0x00; break; case 0: pad [(temp += 4) - 3] = (byte)0x00; pad [temp - 2] = (byte)0x00; pad [temp - 1] = (byte)0x00; } do { pad [temp] = (byte)0x00; pad [temp + 1] = (byte)0x00; pad [temp + 2] = (byte)0x00; pad [temp + 3] = (byte)0x00; } while ((temp += 4) < 56); } // Convert the message size from bytes to bits (big endian). pad [56] = (byte)((temp = (int)(bytes >>> 29)) >>> 24); pad [57] = (byte)(temp >>> 16); pad [58] = (byte)(temp >>> 8); pad [59] = (byte)temp; pad [60] = (byte)((temp = (int)bytes << 3) >>> 24); pad [61] = (byte)(temp >>> 16); pad [62] = (byte)(temp >>> 8); pad [63] = (byte)temp; computeBlock (pad, 0); // Return the computed digest in big-endian byte order. hashvalue [offset] = (byte)(hA >>> 24); hashvalue [offset + 1] = (byte)(hA >>> 16); hashvalue [offset + 2] = (byte)(hA >>> 8); hashvalue [offset + 3] = (byte)hA; hashvalue [offset + 4] = (byte)(hB >>> 24); hashvalue [offset += 5] = (byte)(hB >>> 16); hashvalue [offset + 1] = (byte)(hB >>> 8); hashvalue [offset + 2] = (byte)hB; hashvalue [offset + 3] = (byte)(hC >>> 24); hashvalue [offset + 4] = (byte)(hC >>> 16); hashvalue [offset += 5] = (byte)(hC >>> 8); hashvalue [offset + 1] = (byte)hC; hashvalue [offset + 2] = (byte)(hD >>> 24); hashvalue [offset + 3] = (byte)(hD >>> 16); hashvalue [offset + 4] = (byte)(hD >>> 8); hashvalue [offset += 5] = (byte)hD; hashvalue [offset + 1] = (byte)(hE >>> 24); hashvalue [offset + 2] = (byte)(hE >>> 16); hashvalue [offset + 3] = (byte)(hE >>> 8); hashvalue [offset + 4] = (byte)hE; engineReset (); // clear the evidence return SHA1_LENGTH; } /** * Updates the digest using the specified array of bytes, starting at the * specified offset, but an implied length of exactly 64 bytes. * * Requires no internal buffering, but assumes a fixed input size, in which * the required padding bytes may have been added. * * @param input * the array of bytes to use for the update. * @param offset * the offset to start from in the array of bytes. */ private void computeBlock (final byte [] input, int offset) { /* * Local temporary work variables for intermediate digests. */ int a, b, c, d, e; /* * Cache the input block into the local working set of 64-bit values, in * big-endian byte order. Be careful when widening bytes or integers due to * sign extension ! */ int i00, i01, i02, i03, i04, i05, i06, i07, i08, i09, i10, i11, i12, i13, i14, i15; /* * Use hash schedule function Ch (rounds 0..19): Ch(x,y,z) = (x & y) ^ (~x & * z) = (x & (y ^ z)) ^ z, and K00 = .... = K19 = 0x5a827999. */ /* * First pass, on big endian input (rounds 0..15). */ e = hE + (((a = hA) << 5) | (a >>> 27)) + 0x5a827999 // K00 + (((b = hB) & ((c = hC) ^ (d = hD))) ^ d) // Ch(b,c,d) + (i00 = input [offset] << 24 | (input [offset + 1] & 0xff) << 16 | (input [offset + 2] & 0xff) << 8 | (input [offset + 3] & 0xff)); // W00 d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K01 + ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c) + (i01 = input [offset + 4] << 24 | (input [offset += 5] & 0xff) << 16 | (input [offset + 1] & 0xff) << 8 | (input [offset + 2] & 0xff)); // W01 c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K02 + ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b) + (i02 = input [offset + 3] << 24 | (input [offset + 4] & 0xff) << 16 | (input [offset += 5] & 0xff) << 8 | (input [offset + 1] & 0xff)); // W02 b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K03 + ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a) + (i03 = input [offset + 2] << 24 | (input [offset + 3] & 0xff) << 16 | (input [offset + 4] & 0xff) << 8 | (input [offset += 5] & 0xff)); // W03 a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K04 + ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e) + (i04 = input [offset + 1] << 24 | (input [offset + 2] & 0xff) << 16 | (input [offset + 3] & 0xff) << 8 | (input [offset + 4] & 0xff)); // W04 e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K05 + ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d) + (i05 = input [offset += 5] << 24 | (input [offset + 1] & 0xff) << 16 | (input [offset + 2] & 0xff) << 8 | (input [offset + 3] & 0xff)); // W05 d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K06 + ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c) + (i06 = input [offset + 4] << 24 | (input [offset += 5] & 0xff) << 16 | (input [offset + 1] & 0xff) << 8 | (input [offset + 2] & 0xff)); // W06 c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K07 + ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b) + (i07 = input [offset + 3] << 24 | (input [offset + 4] & 0xff) << 16 | (input [offset += 5] & 0xff) << 8 | (input [offset + 1] & 0xff)); // W07 b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K08 + ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a) + (i08 = input [offset + 2] << 24 | (input [offset + 3] & 0xff) << 16 | (input [offset + 4] & 0xff) << 8 | (input [offset += 5] & 0xff)); // W08 a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K09 + ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e) + (i09 = input [offset + 1] << 24 | (input [offset + 2] & 0xff) << 16 | (input [offset + 3] & 0xff) << 8 | (input [offset + 4] & 0xff)); // W09 e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K10 + ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d) + (i10 = input [offset += 5] << 24 | (input [offset + 1] & 0xff) << 16 | (input [offset + 2] & 0xff) << 8 | (input [offset + 3] & 0xff)); // W10 d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K11 + ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c) + (i11 = input [offset + 4] << 24 | (input [offset += 5] & 0xff) << 16 | (input [offset + 1] & 0xff) << 8 | (input [offset + 2] & 0xff)); // W11 c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K12 + ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b) + (i12 = input [offset + 3] << 24 | (input [offset + 4] & 0xff) << 16 | (input [offset += 5] & 0xff) << 8 | (input [offset + 1] & 0xff)); // W12 b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K13 + ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a) + (i13 = input [offset + 2] << 24 | (input [offset + 3] & 0xff) << 16 | (input [offset + 4] & 0xff) << 8 | (input [offset += 5] & 0xff)); // W13 a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K14 + ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e) + (i14 = input [offset + 1] << 24 | (input [offset + 2] & 0xff) << 16 | (input [offset + 3] & 0xff) << 8 | (input [offset + 4] & 0xff)); // W14 e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K15 + ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d) + (i15 = input [offset += 5] << 24 | (input [offset + 1] & 0xff) << 16 | (input [offset + 2] & 0xff) << 8 | (input [offset + 3] & 0xff)); // W15 /* * Second pass, on scheduled input (rounds 16..31). */ d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K16 + ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c) + (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W16 c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K17 + ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b) + (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W17 b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K18 + ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a) + (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W18 a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K19 + ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e) + (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W19 /* * Use hash schedule function Parity (rounds 20..39): Parity(x,y,z) = x ^ y ^ * z, and K20 = .... = K39 = 0x6ed9eba1. */ e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K20 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W20 d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K21 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W21 c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K22 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W22 b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K23 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W23 a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K24 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W24 e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K25 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W25 d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K26 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W26 c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K27 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W27 b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K28 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W28 a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K29 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W29 e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K30 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W30 d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K31 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W31 /* * Third pass, on scheduled input (rounds 32..47). */ c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K32 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W32 b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K33 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W33 a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K34 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W34 e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K35 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W35 d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K36 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W36 c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K37 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W37 b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K38 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W38 a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K39 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W39 /* * Use hash schedule function Maj (rounds 40..59): Maj(x,y,z) = (x & y)^(x & * z)^(y & z) = ((x & y)|((x | y)& z)), and K40 = .... = K59 = 0x8f1bbcdc. */ e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K40 + ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) //Maj(b,c,d) + (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W40 d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K41 + ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) //Maj(a,b,c) + (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W41 c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K42 + ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) //Maj(e,a,b) + (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W42 b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K43 + ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) //Maj(d,e,a) + (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W43 a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K44 + ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) //Maj(c,d,e) + (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W44 e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K45 + ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) //Maj(b,c,d) + (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W45 d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K46 + ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) //Maj(a,b,c) + (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W46 c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K47 + ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) //Maj(e,a,b) + (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W47 /* * Fourth pass, on scheduled input (rounds 48..63). */ b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K48 + ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) //Maj(d,e,a) + (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W48 a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K49 + ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) //Maj(c,d,e) + (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W49 e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K50 + ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) //Maj(b,c,d) + (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W50 d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K51 + ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) //Maj(a,b,c) + (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W51 c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K52 + ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) //Maj(e,a,b) + (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W52 b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K53 + ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) //Maj(d,e,a) + (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W53 a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K54 + ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) //Maj(c,d,e) + (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W54 e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K55 + ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) //Maj(b,c,d) + (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W55 d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K56 + ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) //Maj(a,b,c) + (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W56 c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K57 + ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) //Maj(e,a,b) + (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W57 b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K58 + ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) //Maj(d,e,a) + (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W58 a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K59 + ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) //Maj(c,d,e) + (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W59 /* * Use hash schedule function Parity (rounds 60..79): Parity(x,y,z) = x ^ y ^ * z, and K60 = .... = K79 = 0xca62c1d6. */ e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K60 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W60 d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K61 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W61 c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K62 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W62 b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K63 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W63 /* * Fifth pass, on scheduled input (rounds 64..79). */ a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K64 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W64 e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K65 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W65 d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K66 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W66 c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K67 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W67 b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K68 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W68 a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K69 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W69 e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K70 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W70 d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K71 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W71 c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K72 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W72 b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K73 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W73 a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K74 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W74 e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K75 + (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d) + (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W75 d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K76 + (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c) + (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W76 c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K77 + (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b) + (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W77 /* * Terminate the last two rounds of fifth pass, feeding the final digest on * the fly. */ hB += b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K78 + (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a) + (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W78 hA += a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K79 + (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e) + (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W79 hE += e; hD += d; hC += /* c= */(c << 30) | (c >>> 2); } }avis-1.2.2/common/src/main/org/avis/security/SingleKeySet.java0000644000175000017500000000272311147555544024137 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; import java.util.HashSet; import java.util.Set; import static org.avis.util.Collections.difference; /** * A single set of keys. Can be used directly as a java.util.Set. * * @author Matthew Phillips */ class SingleKeySet extends HashSet implements KeySet, Set { SingleKeySet () { super (); } SingleKeySet (Set keys) { super (keys); } public void add (KeySet theKeys) throws IllegalArgumentException { addAll ((SingleKeySet)theKeys); } public void remove (KeySet theKeys) { removeAll ((SingleKeySet)theKeys); } public boolean remove (Key key) { return remove ((Object)key); } public KeySet subtract (KeySet keys) { return new SingleKeySet (difference (this, (SingleKeySet)keys)); } } avis-1.2.2/common/src/main/org/avis/security/SecureHash.java0000644000175000017500000000237011147555544023621 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; /** * An enumeration of supported secure hash algorithms. * * @author Matthew Phillips */ public enum SecureHash { SHA1 { @Override public byte [] hash (byte [] input) { SHA1 sha1 = new SHA1 (); sha1.engineUpdate (input, 0, input.length); return sha1.engineDigest (); } }; /** * Perform the hash scheme on an input byte array. * * @param input The data to hash. * @return The hashed result. Length depends on the hash scheme. */ public abstract byte [] hash (byte [] input); } avis-1.2.2/common/src/main/org/avis/security/SingleKeyScheme.java0000644000175000017500000000201311147555544024600 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.security; /** * A key scheme that requires a single key set. e.g. SHA-1 Consumer. * * @author Matthew Phillips */ public final class SingleKeyScheme extends KeyScheme { SingleKeyScheme (int id, SecureHash keyHash, boolean producer, boolean consumer) { super (id, keyHash, producer, consumer); } } avis-1.2.2/common/src/main/org/avis/io/0000755000175000017500000000000011147555544017462 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/io/FrameTooLargeException.java0000644000175000017500000000207211147555544024674 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.io.IOException; /** * Throws by the frame codec when a frame is too big to be decoded. * * @author Matthew Phillips */ public class FrameTooLargeException extends IOException { public FrameTooLargeException (int maxLength, int actualLength) { super ("Frame size of " + actualLength + " bytes is larger than maximum " + maxLength); } } avis-1.2.2/common/src/main/org/avis/io/ClientFrameCodec.java0000644000175000017500000000733011147555544023457 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import org.apache.mina.common.IoFilter; import org.apache.mina.filter.codec.ProtocolCodecException; import org.apache.mina.filter.codec.ProtocolCodecFactory; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.ProtocolDecoder; import org.apache.mina.filter.codec.ProtocolEncoder; import org.avis.io.messages.ConfConn; import org.avis.io.messages.ConnRply; import org.avis.io.messages.ConnRqst; import org.avis.io.messages.Disconn; import org.avis.io.messages.DisconnRply; import org.avis.io.messages.DisconnRqst; import org.avis.io.messages.DropWarn; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.NotifyDeliver; import org.avis.io.messages.NotifyEmit; import org.avis.io.messages.QuenchPlaceHolder; import org.avis.io.messages.SecRply; import org.avis.io.messages.SecRqst; import org.avis.io.messages.SubAddRqst; import org.avis.io.messages.SubDelRqst; import org.avis.io.messages.SubModRqst; import org.avis.io.messages.SubRply; import org.avis.io.messages.TestConn; import org.avis.io.messages.UNotify; /** * Codec for Elvin client protocol message frames. * * @author Matthew Phillips */ public class ClientFrameCodec extends FrameCodec implements ProtocolCodecFactory { public static final ClientFrameCodec INSTANCE = new ClientFrameCodec (); public static final IoFilter FILTER = new ProtocolCodecFilter (INSTANCE); public ProtocolEncoder getEncoder () throws Exception { return INSTANCE; } public ProtocolDecoder getDecoder () throws Exception { return INSTANCE; } @Override protected Message newMessage (int messageType, int frameSize) throws ProtocolCodecException { switch (messageType) { case ConnRqst.ID: return new ConnRqst (); case ConnRply.ID: return new ConnRply (); case DisconnRqst.ID: return new DisconnRqst (); case DisconnRply.ID: return new DisconnRply (); case Disconn.ID: return new Disconn (); case SubAddRqst.ID: return new SubAddRqst (); case SubRply.ID: return new SubRply (); case SubModRqst.ID: return new SubModRqst (); case SubDelRqst.ID: return new SubDelRqst (); case Nack.ID: return new Nack (); case NotifyDeliver.ID: return new NotifyDeliver (); case NotifyEmit.ID: return new NotifyEmit (); case TestConn.ID: return TestConn.INSTANCE; case ConfConn.ID: return ConfConn.INSTANCE; case SecRqst.ID: return new SecRqst (); case SecRply.ID: return new SecRply (); case UNotify.ID: return new UNotify (); case DropWarn.ID: return new DropWarn (); case QuenchPlaceHolder.ADD: case QuenchPlaceHolder.MODIFY: case QuenchPlaceHolder.DELETE: return new QuenchPlaceHolder (messageType, frameSize - 4); default: throw new ProtocolCodecException ("Unknown message type: ID = " + messageType); } } } avis-1.2.2/common/src/main/org/avis/io/LivenessFilter.java0000644000175000017500000002203611147555544023266 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import org.apache.mina.common.IoFilter; import org.apache.mina.common.IoFilterAdapter; import org.apache.mina.common.IoFilterChain; import org.apache.mina.common.IoSession; import org.avis.io.messages.ConfConn; import org.avis.io.messages.LivenessFailureMessage; import org.avis.io.messages.Message; import org.avis.io.messages.TestConn; import static java.lang.Math.max; import static java.lang.Math.random; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.avis.logging.Log.trace; /** * A MINA I/O filter that adds liveness checking using * TestConn/ConfConn. Generates LivenessFailureMessage's when * livenessTimeout passes and no response to a TestConn is seen within * receiveTimeout seconds. * * @author Matthew Phillips */ public class LivenessFilter extends IoFilterAdapter implements IoFilter { protected long receiveTimeout; protected long livenessTimeout; protected String filterName; protected ScheduledExecutorService executor; /** * Create a new instance. Uses a {@link SharedExecutor}. * * @param livenessTimeout The time (in millis) that must pass before * a liveness check is issued. * @param receiveTimeout The amount of time (in millis) to wait for * a reply. */ public LivenessFilter (long livenessTimeout, long receiveTimeout) { this (null, livenessTimeout, receiveTimeout); } /** * Create a new instance. * * @param executor The executor to use for timed callbacks. * @param livenessTimeout The time (in millis) that must pass before * a liveness check is issued. * @param receiveTimeout The amount of time (in millis) to wait for * a reply. */ public LivenessFilter (ScheduledExecutorService executor, long livenessTimeout, long receiveTimeout) { this.executor = executor; this.livenessTimeout = livenessTimeout; this.receiveTimeout = receiveTimeout; } /** * Force dispose the tracker for a given session. */ public static void dispose (IoSession session) { Tracker tracker = trackerFor (session); if (tracker != null) tracker.dispose (); } public static LivenessFilter filterFor (IoSession session) { return trackerFor (session).filter (); } public long livenessTimeout () { return livenessTimeout; } public static void setLivenessTimeoutFor (IoSession session, long newTimeout) { if (newTimeout < 1000) throw new IllegalArgumentException ("Timeout cannot be < 1000: " + newTimeout); Tracker tracker = trackerFor (session); tracker.filter ().livenessTimeout = newTimeout; tracker.timeoutUpdated (); } public static void setReceiveTimeoutFor (IoSession session, long newTimeout) { if (newTimeout < 0) throw new IllegalArgumentException ("Timeout cannot be < 0: " + newTimeout); Tracker tracker = trackerFor (session); tracker.filter ().receiveTimeout = newTimeout; tracker.timeoutUpdated (); } @Override public void onPreAdd (IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { this.filterName = name; if (executor == null) executor = SharedExecutor.acquire (); } @Override public void filterClose (NextFilter nextFilter, IoSession session) throws Exception { if (SharedExecutor.release (executor)) executor = null; nextFilter.filterClose (session); } @Override public void sessionOpened (NextFilter nextFilter, IoSession session) throws Exception { session.setAttribute ("livenessTracker", new Tracker (session)); nextFilter.sessionOpened (session); } static Tracker trackerFor (IoSession session) { return (Tracker)session.getAttribute ("livenessTracker"); } @Override public void sessionClosed (NextFilter nextFilter, IoSession session) throws Exception { Tracker tracker = trackerFor (session); if (tracker != null) tracker.dispose (); nextFilter.sessionClosed (session); } @Override public void messageReceived (NextFilter nextFilter, IoSession session, Object message) throws Exception { trackerFor (session).connectionIsLive (); // handle TestConn and ConfConn in this filter, pass on others if (message == ConfConn.INSTANCE) { trace ("Liveness confirmed: received ConfConn", this); } else if (message == TestConn.INSTANCE) { if (session.getScheduledWriteRequests () == 0) { session.write (ConfConn.INSTANCE); trace ("Sent ConfConn in response to TestConn liveness check", this); } else { trace ("Ignored TestConn: outgoing messages already in queue", this); } } else { nextFilter.messageReceived (session, message); } } /** * An instance of this is attached to each session to track liveness. */ class Tracker { private IoSession session; private ScheduledFuture livenessFuture; private long lastLive; private long lastTestConnCheck; public Tracker (IoSession session) { this.session = session; this.lastLive = currentTimeMillis (); /* * Run a liveness check with a randomised delay offset. Helps to * avoid two hosts syncing their checks and doubling up on * messages. */ scheduleLivenessCheck (livenessTimeout - (long)(random () * livenessTimeout)); } public LivenessFilter filter () { return LivenessFilter.this; } public synchronized void dispose () { cancelLivenessCheck (); session = null; } public boolean isDisposed () { return livenessFuture == null && session == null; } /** * Call to reset liveness timeout. */ public synchronized void connectionIsLive () { lastLive = currentTimeMillis (); } public void timeoutUpdated () { cancelLivenessCheck (); lastLive = currentTimeMillis (); scheduleLivenessCheck (); } private void scheduleLivenessCheck () { scheduleLivenessCheck (max (0, livenessTimeout - (currentTimeMillis () - lastLive))); } private void scheduleLivenessCheck (long delay) { if (livenessFuture == null) { livenessFuture = executor.schedule (new Runnable () { public void run () { checkLiveness (); } }, delay, MILLISECONDS); } } private void cancelLivenessCheck () { if (livenessFuture != null) { livenessFuture.cancel (false); livenessFuture = null; } } /** * Check if liveness timeout has expired: if so send TestConn and * schedule checkConnReply (). */ protected synchronized void checkLiveness () { livenessFuture = null; if (currentTimeMillis () - lastLive >= livenessTimeout) { trace ("Liveness timeout: sending TestConn", this); lastTestConnCheck = currentTimeMillis (); session.write (TestConn.INSTANCE); livenessFuture = executor.schedule (new Runnable () { public void run () { checkConnReply (); } }, receiveTimeout, MILLISECONDS); } else { scheduleLivenessCheck (); } } /** * If no response seen to TestConn within replyTimeout, send * LivenessTimeoutMessage. */ protected synchronized void checkConnReply () { livenessFuture = null; if (lastLive < lastTestConnCheck) { trace ("No reply to TestConn: signaling liveness failure", this); injectMessage (new LivenessFailureMessage ()); } else { scheduleLivenessCheck (); } } private void injectMessage (Message message) { NextFilter filter = session.getFilterChain ().getNextFilter (filterName); filter.messageReceived (session, message); } } } avis-1.2.2/common/src/main/org/avis/io/LegacyConnectionOptions.java0000644000175000017500000001056611147555544025135 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.HashMap; import java.util.Map; /** * Defines legacy client connection option compatibility. Provides a * two-way mapping between old-style connection options and the new * scheme defined in the 4.0 client specification. * * Compatibility: * *
 *      Standard Name               | Compatibility Name
 *      ----------------------------+------------------------------------
 *      Attribute.Max-Count         | router.attribute.max-count
 *      Attribute.Name.Max-Length   | router.attribute.name.max-length
 *      Attribute.Opaque.Max-Length | router.attribute.opaque.max-length
 *      Attribute.String.Max-Length | router.attribute.string.max-length
 *      Packet.Max-Length           | router.packet.max-length
 *      Receive-Queue.Drop-Policy   | router.recv-queue.drop-policy
 *      Receive-Queue.Max-Length    | router.recv-queue.max-length
 *      Send-Queue.Drop-Policy      | router.send-queue.drop-policy
 *      Send-Queue.Max-Length       | router.send-queue.max-length
 *      Subscription.Max-Count      | router.subscription.max-count
 *      Subscription.Max-Length     | router.subscription.max-length
 *      Supported-Key-Schemes       | router.supported-keyschemes
 *      Vendor-Identification       | router.vendor-identification
 *      ----------------------------+------------------------------------
 * 
*/ public final class LegacyConnectionOptions { private static final Map legacyToNew; private static final Map newToLegacy; static { legacyToNew = new HashMap (); newToLegacy = new HashMap (); addLegacy ("router.attribute.max-count", "Attribute.Max-Count"); addLegacy ("router.attribute.name.max-length", "Attribute.Name.Max-Length"); addLegacy ("router.attribute.opaque.max-length", "Attribute.Opaque.Max-Length"); addLegacy ("router.attribute.string.max-length", "Attribute.String.Max-Length"); addLegacy ("router.packet.max-length", "Packet.Max-Length"); addLegacy ("router.recv-queue.drop-policy", "Receive-Queue.Drop-Policy"); addLegacy ("router.recv-queue.max-length", "Receive-Queue.Max-Length"); addLegacy ("router.send-queue.drop-policy", "Send-Queue.Drop-Policy"); addLegacy ("router.send-queue.max-length", "Send-Queue.Max-Length"); addLegacy ("router.subscription.max-count", "Subscription.Max-Count"); addLegacy ("router.subscription.max-length", "Subscription.Max-Length"); addLegacy ("router.supported-keyschemes", "Supported-Key-Schemes"); addLegacy ("router.vendor-identification", "Vendor-Identification"); addLegacy ("router.coalesce-delay", "TCP.Send-Immediately"); } private LegacyConnectionOptions () { // zip } private static void addLegacy (String oldOption, String newOption) { legacyToNew.put (oldOption, newOption); newToLegacy.put (newOption, oldOption); } public static String legacyToNew (String option) { if (legacyToNew.containsKey (option)) return legacyToNew.get (option); else return option; } public static String newToLegacy (String option) { if (newToLegacy.containsKey (option)) return newToLegacy.get (option); else return option; } public static void setWithLegacy (Map options, String option, Object value) { options.put (option, value); options.put (newToLegacy (option), value); } } avis-1.2.2/common/src/main/org/avis/io/ExceptionMonitorLogger.java0000644000175000017500000000240611147555544024775 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import org.apache.mina.common.ExceptionMonitor; import static org.avis.logging.Log.internalError; /** * MINA exception monitor that routes exceptions to the log. * * @author Matthew Phillips */ public class ExceptionMonitorLogger extends ExceptionMonitor { public static final ExceptionMonitorLogger INSTANCE = new ExceptionMonitorLogger (); private ExceptionMonitorLogger () { // zip } @Override public void exceptionCaught (Throwable cause) { internalError ("Unexpected exception during IO", this, cause); } } avis-1.2.2/common/src/main/org/avis/io/Net.java0000644000175000017500000001624211147555544021060 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import org.apache.mina.common.IoSession; import org.apache.mina.transport.socket.nio.SocketSessionConfig; import org.avis.common.ElvinURI; import org.avis.common.InvalidURIException; import static java.util.Arrays.asList; /** * General networking utilities. * * @author Matthew Phillips */ public final class Net { private Net () { // zip } /** * Get local host name. * * @return The canonical host name. * * @throws IOException if no host name can be found. */ public static String localHostName () throws IOException { return InetAddress.getLocalHost ().getCanonicalHostName (); /* * todo select "best" host name here if InetAddress.localHostName () * doesn't produce anything useful. maybe choose address with most * "false" values from output from DumpHostAddresses. * * host name: hex.dsto.defence.gov.au * loopback: false * link local: false * multicast: false * site local: false * ------- * host name: hex.local * loopback: falselink local: true * multicast: false * site local: false * ------- */ // for (Enumeration i = // NetworkInterface.getNetworkInterfaces (); i.hasMoreElements (); ) // { // NetworkInterface ni = i.nextElement (); // // for (Enumeration j = ni.getInetAddresses (); // j.hasMoreElements (); ) // { // InetAddress address = j.nextElement (); // // if (!address.isLoopbackAddress () && !address.isSiteLocalAddress ()) // return address.getCanonicalHostName (); // } // } // // throw new IOException ("Cannot determine a valid local host name"); } /** * Generate a set of socket addresses for a given set of URI's. This * method allows interface names to be used rather than host names * by prefixing the host name with "!". * * @param uris The URI's to turn into addresses. * * @return The corresponding set of InetSocketAddress's for the URI's. * * @throws IOException * @throws SocketException * @throws UnknownHostException */ public static Set addressesFor (Set uris) throws IOException, SocketException, UnknownHostException { Set addresses = new HashSet (); for (ElvinURI uri : uris) addAddressFor (addresses, uri); return addresses; } /** * Generate the addresses for a given URI. This method allows * interface names to be used rather than host names by prefixing * the host name with "!". * * @param uri The URI. * @return The set of network addresses that correspond to the URI. * * @throws IOException * @throws SocketException * @throws UnknownHostException */ public static Set addressesFor (ElvinURI uri) throws IOException, SocketException, UnknownHostException { Set addresses = new HashSet (); addAddressFor (addresses, uri); return addresses; } private static void addAddressFor (Set addresses, ElvinURI uri) throws SocketException, IOException, UnknownHostException { Collection inetAddresses; if (uri.host.startsWith ("!")) inetAddresses = addressesForInterface (uri.host.substring (1)); else inetAddresses = addressesForHost (uri.host); for (InetAddress address : inetAddresses) { if (address.isAnyLocalAddress ()) addresses.add (new InetSocketAddress (uri.port)); else if (!address.isLinkLocalAddress ()) addresses.add (new InetSocketAddress (address, uri.port)); } } private static Collection addressesForHost (String host) throws UnknownHostException { return asList (InetAddress.getAllByName (host)); } private static Collection addressesForInterface (String name) throws SocketException, IOException { NetworkInterface netInterface = NetworkInterface.getByName (name); if (netInterface == null) { throw new IOException ("Unknown interface name \"" + name + "\""); } HashSet addresses = new HashSet (); for (Enumeration i = netInterface.getInetAddresses (); i.hasMoreElements (); ) { addresses.add (i.nextElement ()); } return addresses; } /** * Find the host address for an InetSocketAddress session. */ public static InetAddress remoteHostAddressFor (IoSession session) { if (session.getRemoteAddress () instanceof InetSocketAddress) { return ((InetSocketAddress)session.getRemoteAddress ()).getAddress (); } else { throw new Error ("Can't get host name for address type " + session.getRemoteAddress ().getClass ()); } } /** * Generate a standard host ID for a session using host name and IP. */ public static String hostIdFor (IoSession session) { InetAddress address = remoteHostAddressFor (session); return address.getCanonicalHostName () + '/' + address.getHostAddress (); } /** * Set TCP no-delay flag for socket connections. * * @param session The IO session. * @param noDelay The new setting for TCP_NODELAY. * * @return true if the setting could be changed. */ public static boolean enableTcpNoDelay (IoSession session, boolean noDelay) { if (session.getConfig () instanceof SocketSessionConfig) { ((SocketSessionConfig)session.getConfig ()).setTcpNoDelay (noDelay); return true; } else { return false; } } /** * Create a new URI without the annoying checked exception. * * @param uriString The URI string. * @return A new URI. * @throws InvalidURIException if uriString is invalid. */ public static URI uri (String uriString) throws InvalidURIException { try { return new URI (uriString); } catch (URISyntaxException ex) { throw new InvalidURIException (ex); } } } avis-1.2.2/common/src/main/org/avis/io/XdrCoding.java0000644000175000017500000002475311147555544022221 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.nio.BufferUnderflowException; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import static java.nio.CharBuffer.wrap; import static java.util.Collections.emptyMap; /** * Encoding/decoding helpers for the Elvin XDR wire format. * * @author Matthew Phillips */ public final class XdrCoding { /** * Type codes from client protocol spec. * enum { * int32_tc = 1, * int64_tc = 2, * real64_tc = 3, * string_tc = 4, * opaque_tc = 5 * } value_typecode; */ public static final int TYPE_INT32 = 1; public static final int TYPE_INT64 = 2; public static final int TYPE_REAL64 = 3; public static final int TYPE_STRING = 4; public static final int TYPE_OPAQUE = 5; private static final byte [] EMPTY_BYTES = new byte [0]; /** * Per thread UTF-8 decoder. */ private static final ThreadLocal UTF8_DECODER = new ThreadLocal () { @Override protected CharsetDecoder initialValue () { return Charset.forName ("UTF-8").newDecoder (); } }; /** * Per thread UTF-8 encoder. */ private static final ThreadLocal UTF8_ENCODER = new ThreadLocal () { @Override protected CharsetEncoder initialValue () { return Charset.forName ("UTF-8").newEncoder (); } }; private XdrCoding () { // zip } public static byte [] toUTF8 (String string) { try { if (string.length () == 0) return EMPTY_BYTES; else return UTF8_ENCODER.get ().encode (wrap (string)).array (); } catch (CharacterCodingException ex) { // shouldn't be possible to get an error encoding from UTF-16 to UTF-8. throw new Error ("Internal error", ex); } } /** * Turn a UTF-8 byte array into a string. * * @param utf8Bytes The bytes. * @param offset The offset into bytes. * @param length The number of bytes to use. * @return The string. * * @throws CharacterCodingException if the bytes do not represent a * UTF-8 string. */ public static String fromUTF8 (byte [] utf8Bytes, int offset, int length) throws CharacterCodingException { if (utf8Bytes.length == 0) return ""; else return UTF8_DECODER.get ().decode (java.nio.ByteBuffer.wrap (utf8Bytes, offset, length)).toString (); } /** * Read a length-delimited 4-byte-aligned UTF-8 string. */ public static String getString (ByteBuffer in) throws BufferUnderflowException, ProtocolCodecException { try { int length = getPositiveInt (in); if (length == 0) { return ""; } else { String string = in.getString (length, UTF8_DECODER.get ()); in.skip (paddingFor (length)); return string; } } catch (CharacterCodingException ex) { throw new ProtocolCodecException ("Invalid UTF-8 string", ex); } } /** * Write a length-delimited 4-byte-aligned UTF-8 string. */ public static void putString (ByteBuffer out, String string) { try { if (string.length () == 0) { out.putInt (0); } else { int start = out.position (); out.skip (4); out.putString (string, UTF8_ENCODER.get ()); // write length int byteCount = out.position () - start - 4; out.putInt (start, byteCount); putPadding (out, byteCount); } } catch (CharacterCodingException ex) { // shouldn't be possible to get an error encoding from UTF-16 to UTF-8. throw new Error ("Internal error", ex); } } /** * Generate null padding to 4-byte pad out a block of a given length */ public static void putPadding (ByteBuffer out, int length) { for (int count = paddingFor (length); count > 0; count--) out.put ((byte)0); } /** * Calculate the padding needed for the size of a block of bytes to * be a multiple of 4. */ public static int paddingFor (int length) { // tricky eh? this is equivalent to (4 - length % 4) % 4 return (4 - (length & 3)) & 3; } /** * Write a name/value set. */ public static void putNameValues (ByteBuffer out, Map nameValues) throws ProtocolCodecException { out.putInt (nameValues.size ()); for (Entry entry : nameValues.entrySet ()) { putString (out, entry.getKey ()); putObject (out, entry.getValue ()); } } /** * Read a name/value set. */ public static Map getNameValues (ByteBuffer in) throws ProtocolCodecException { int pairs = getPositiveInt (in); if (pairs == 0) return emptyMap (); HashMap nameValues = new HashMap (); for ( ; pairs > 0; pairs--) nameValues.put (getString (in), getObject (in)); return nameValues; } public static void putObjects (ByteBuffer out, Object [] objects) throws ProtocolCodecException { out.putInt (objects.length); for (Object object : objects) putObject (out, object); } public static Object [] getObjects (ByteBuffer in) throws ProtocolCodecException { Object [] objects = new Object [getPositiveInt (in)]; for (int i = 0; i < objects.length; i++) objects [i] = getObject (in); return objects; } /** * Put an object value in type_id/value format. */ public static void putObject (ByteBuffer out, Object value) throws ProtocolCodecException { if (value instanceof String) { out.putInt (TYPE_STRING); putString (out, (String)value); } else if (value instanceof Integer) { out.putInt (TYPE_INT32); out.putInt ((Integer)value); } else if (value instanceof Long) { out.putInt (TYPE_INT64); out.putLong ((Long)value); } else if (value instanceof Double) { out.putInt (TYPE_REAL64); out.putDouble ((Double)value); } else if (value instanceof byte []) { out.putInt (TYPE_OPAQUE); putBytes (out, (byte [])value); } else if (value == null) { throw new IllegalArgumentException ("Value cannot be null"); } else { throw new IllegalArgumentException ("Don't know how to encode " + value.getClass ()); } } /** * Read an object in type_id/value format. */ public static Object getObject (ByteBuffer in) throws ProtocolCodecException { int type = in.getInt (); switch (type) { case TYPE_INT32: return in.getInt (); case TYPE_INT64: return in.getLong (); case TYPE_REAL64: return in.getDouble (); case TYPE_STRING: return getString (in); case TYPE_OPAQUE: return getBytes (in); default: throw new ProtocolCodecException ("Unknown type code: " + type); } } /** * Write a length-delimited, 4-byte-aligned byte array. */ public static void putBytes (ByteBuffer out, byte [] bytes) { out.putInt (bytes.length); out.put (bytes); putPadding (out, bytes.length); } /** * Read a length-delimited, 4-byte-aligned byte array. * * @throws ProtocolCodecException */ public static byte [] getBytes (ByteBuffer in) throws ProtocolCodecException { return getBytes (in, getPositiveInt (in)); } /** * Read a length-delimited, 4-byte-aligned byte array with a given length. */ public static byte [] getBytes (ByteBuffer in, int length) { byte [] bytes = new byte [length]; in.get (bytes); in.skip (paddingFor (length)); return bytes; } public static void putBool (ByteBuffer out, boolean value) { out.putInt (value ? 1 : 0); } public static boolean getBool (ByteBuffer in) throws ProtocolCodecException { int value = in.getInt (); if (value == 0) return false; else if (value == 1) return true; else throw new ProtocolCodecException ("Cannot interpret " + value + " as boolean"); } /** * Read a length-demlimited array of longs. */ public static long [] getLongArray (ByteBuffer in) throws ProtocolCodecException { long [] longs = new long [getPositiveInt (in)]; for (int i = 0; i < longs.length; i++) longs [i] = in.getLong (); return longs; } /** * Write a length-delimted array of longs. */ public static void putLongArray (ByteBuffer out, long [] longs) { out.putInt (longs.length); for (long l : longs) out.putLong (l); } /** * Read a length-demlimited array of strings. */ public static String [] getStringArray (ByteBuffer in) throws BufferUnderflowException, ProtocolCodecException { String [] strings = new String [getPositiveInt (in)]; for (int i = 0; i < strings.length; i++) strings [i] = getString (in); return strings; } /** * Write a length-delimted array of strings. */ public static void putStringArray (ByteBuffer out, String [] strings) { out.putInt (strings.length); for (String s : strings) putString (out, s); } /** * Read an int >= 0 or generate an exception. */ private static int getPositiveInt (ByteBuffer in) throws ProtocolCodecException { int value = in.getInt (); if (value >= 0) return value; else throw new ProtocolCodecException ("Length cannot be negative: " + value); } } avis-1.2.2/common/src/main/org/avis/io/InetAddressFilter.java0000644000175000017500000000267211147555544023707 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.net.InetAddress; import org.avis.util.Filter; import org.avis.util.WildcardFilter; import static java.util.Arrays.asList; import static org.avis.util.Text.split; /** * A filter for internet addresses, matching against either the host * name or IP address. * * @author Matthew Phillips */ public class InetAddressFilter implements Filter { private WildcardFilter filter; public InetAddressFilter (String patterns) { this.filter = new WildcardFilter (asList (split (patterns))); } public boolean matches (InetAddress address) { return filter.matches (address.getHostName ()) || filter.matches (address.getHostAddress ()) || filter.matches (address.getCanonicalHostName ()); } } avis-1.2.2/common/src/main/org/avis/io/StreamReader.java0000644000175000017500000000322611147555544022706 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; /** * A buffered stream reader that hacks around lack of EOF flag on * standard Reader's and avoids blocking on EOF test. Only readLine () * is fixed in this version. * * @author Matthew Phillips */ public class StreamReader extends BufferedReader { private boolean eof; public StreamReader (Reader in) { super (in, 1); } public StreamReader (InputStream in) throws IOException { this (new InputStreamReader (in, "UTF-8")); } @Override public String readLine () throws IOException { if (eof) { return null; } else { String line = super.readLine (); if (line == null) eof = true; return line; } } /** * Test if EOF has been reached. Guaranteed not to block. */ public boolean eof () { return eof; } } avis-1.2.2/common/src/main/org/avis/io/SharedExecutor.java0000644000175000017500000000421611147555544023255 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.concurrent.ScheduledExecutorService; import static java.util.concurrent.Executors.newScheduledThreadPool; /** * Manages a single shared ScheduledExecutorService. * * @author Matthew Phillips */ public final class SharedExecutor { protected static int shareCount = 0; protected static ScheduledExecutorService sharedExecutor = null; private SharedExecutor () { // zip } /** * Acquire a reference to the shared executor. */ public static ScheduledExecutorService acquire () { synchronized (SharedExecutor.class) { if (shareCount++ == 0) sharedExecutor = newScheduledThreadPool (1); return sharedExecutor; } } /** * Release the shared exectutor. * * @param executor The executor. If this is not the shared instance, * nothing is done. */ public static boolean release (ScheduledExecutorService executor) { synchronized (SharedExecutor.class) { if (executor == sharedExecutor) { if (shareCount == 0) throw new IllegalStateException ("Too many release calls"); if (--shareCount == 0) { sharedExecutor.shutdown (); sharedExecutor = null; } return true; } } return false; } public static boolean sharedExecutorDisposed () { synchronized (SharedExecutor.class) { return shareCount == 0 && sharedExecutor == null; } } } avis-1.2.2/common/src/main/org/avis/io/TLS.java0000644000175000017500000001502611147555544020773 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** * Utilities for TLS/SSL. * * @author Matthew Phillips */ public final class TLS { private TLS () { // zip } /** * Generate an array of characters for a passphrase, or null if * passphrase is empty (i.e. allow empty string to stand for no * passphrase). */ public static char [] toPassphrase (String passphrase) { return passphrase.length () > 0 ? passphrase.toCharArray () : null; } /** * Create a default SSL context with the default keystore and trust * chain. * * @return A new SSL context. * * @throws IOException if an error occurred initialising TLS. */ public static SSLContext defaultSSLContext () throws IOException { return sslContextFor (null, null, false); } /** * Create a new SSL context. * * @param keystore The keystore to use for keys and trusted * certificates. This may be null to use the default * store. * @param keystorePassphrase The passphrase for the keystore. Null * if no keystore. * @param requireTrustedServer True if only servers that are in the * trusted certificate chain are acceptable. Has no * effect when used in server mode. * * @return The SSL context. * * @throws IllegalArgumentException if a keystore is specified and * no passphrase is set. * @throws IOException if an error occurred initialising TLS. */ public static SSLContext sslContextFor (KeyStore keystore, String keystorePassphrase, boolean requireTrustedServer) throws IOException, IllegalArgumentException { if (keystore != null && keystorePassphrase == null) { throw new IllegalArgumentException ("Passphrase must be set when using a keystore"); } try { KeyManager [] keyManagers = null; if (keystore != null) { KeyManagerFactory keyFactory = KeyManagerFactory.getInstance ("SunX509"); keyFactory.init (keystore, toPassphrase (keystorePassphrase)); keyManagers = keyFactory.getKeyManagers (); } CustomTrustManager trustManager = new CustomTrustManager (keystore, requireTrustedServer, false); SSLContext sslContext = SSLContext.getInstance ("TLS"); sslContext.init (keyManagers, new TrustManager [] {trustManager}, null); return sslContext; } catch (Exception ex) { throw new IOException ("Error initialising TLS: " + ex); } } /** * An X.509 trust manager that allows optional client/server trust * requirements. When trust is required, it falls back on the * platform's default trust manager. * * See also * http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html * #X509TrustManager * * @author Matthew Phillips */ private static class CustomTrustManager implements X509TrustManager { /* * The default X509TrustManager returned by SunX509. We'll delegate * decisions to it, and fall back to the logic in this class if the * default X509TrustManager doesn't trust it. */ private X509TrustManager sunX509TrustManager; private boolean requireTrustedServer; private boolean requireTrustedClient; public CustomTrustManager (KeyStore keystore, boolean requireTrustedServer, boolean requireTrustedClient) throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException { this.requireTrustedServer = requireTrustedServer; this.requireTrustedClient = requireTrustedClient; this.sunX509TrustManager = initTrustManager (keystore); } private X509TrustManager initTrustManager (KeyStore keystore) throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException { TrustManagerFactory sunManagerFactories = TrustManagerFactory.getInstance ("SunX509", "SunJSSE"); sunManagerFactories.init (keystore); /* * Iterate over the returned trustmanagers, look for an instance * of X509TrustManager. If found, use that as our "default" trust * manager. */ for (TrustManager trustManager : sunManagerFactories.getTrustManagers ()) { if (trustManager instanceof X509TrustManager) return (X509TrustManager)trustManager; } throw new NoSuchProviderException ("No default X509 trust manager"); } /* * Delegate to the default trust manager. */ public void checkClientTrusted (X509Certificate [] chain, String authType) throws CertificateException { if (requireTrustedClient) sunX509TrustManager.checkClientTrusted (chain, authType); } /* * Delegate to the default trust manager. */ public void checkServerTrusted (X509Certificate [] chain, String authType) throws CertificateException { if (requireTrustedServer) sunX509TrustManager.checkServerTrusted (chain, authType); } /* * Merely pass this through. */ public X509Certificate [] getAcceptedIssuers () { return sunX509TrustManager.getAcceptedIssuers (); } } } avis-1.2.2/common/src/main/org/avis/io/FrameCodec.java0000644000175000017500000001512411147555544022320 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.nio.BufferUnderflowException; import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.IoSession; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; import org.apache.mina.filter.codec.ProtocolCodecException; import org.apache.mina.filter.codec.ProtocolDecoderOutput; import org.apache.mina.filter.codec.ProtocolEncoder; import org.apache.mina.filter.codec.ProtocolEncoderOutput; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.Message; import org.avis.io.messages.XidMessage; /** * Base class for Elvin XDR frame codecs. Reads/writes messages * to/from the Elvin XDR frame format with the help of * {@link Message#decode(ByteBuffer)} and * {@link Message#encode(ByteBuffer)}. Understood message sets are * effectively defined by the subclasses' implementation of * {@link #newMessage(int, int)}. * * @author Matthew Phillips */ public abstract class FrameCodec extends CumulativeProtocolDecoder implements ProtocolEncoder { public void encode (IoSession session, Object messageObject, ProtocolEncoderOutput out) throws Exception { // buffer is auto deallocated ByteBuffer buffer = ByteBuffer.allocate (4096); buffer.setAutoExpand (true); // leave room for frame size buffer.position (4); Message message = (Message)messageObject; // write frame type buffer.putInt (message.typeId ()); message.encode (buffer); int frameSize = buffer.position () - 4; // write frame size buffer.putInt (0, frameSize); // if (isEnabled (TRACE) && buffer.limit () <= MAX_BUFFER_DUMP) // trace ("Codec output: " + buffer.getHexDump (), this); // sanity check frame is 4-byte aligned if (frameSize % 4 != 0) throw new ProtocolCodecException ("Frame length not 4 byte aligned for " + message.getClass ()); int maxLength = maxFrameLengthFor (session); if (frameSize <= maxLength) { // write out whole frame buffer.flip (); out.write (buffer); } else { throw new FrameTooLargeException (maxLength, frameSize); } } @Override protected boolean doDecode (IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception { // if (isEnabled (TRACE) && in.limit () <= MAX_BUFFER_DUMP) // trace ("Codec input: " + in.getHexDump (), this); // if in protocol violation mode, do not try to read any further if (session.getAttribute ("protocolViolation") != null) return false; if (!haveFullFrame (session, in)) return false; int maxLength = maxFrameLengthFor (session); int frameSize = in.getInt (); int dataStart = in.position (); Message message = null; try { int messageType = in.getInt (); message = newMessage (messageType, frameSize); if (frameSize % 4 != 0) throw new ProtocolCodecException ("Frame length not 4 byte aligned"); if (frameSize > maxLength) throw new FrameTooLargeException (maxLength, frameSize); message.decode (in); int bytesRead = in.position () - dataStart; if (bytesRead != frameSize) { throw new ProtocolCodecException ("Some input not read for " + message.name () + ": " + "Frame header said " + frameSize + " bytes, but only read " + bytesRead); } out.write (message); return true; } catch (Exception ex) { if (ex instanceof ProtocolCodecException || ex instanceof BufferUnderflowException || ex instanceof FrameTooLargeException) { /* * Mark session in violation and handle once: codec will only * generate one error message, it's up to consumer to try to * recover or close connection. */ session.setAttribute ("protocolViolation"); session.suspendRead (); ErrorMessage error = new ErrorMessage (ex, message); // fill in XID if possible if (message instanceof XidMessage && in.limit () >= 12) { int xid = in.getInt (8); if (xid > 0) ((XidMessage)message).xid = xid; } out.write (error); return true; } else { throw (RuntimeException)ex; } } } /** * Create a new instance of a message given a message type code and * frame length. */ protected abstract Message newMessage (int messageType, int frameSize) throws ProtocolCodecException; private static boolean haveFullFrame (IoSession session, ByteBuffer in) { // need frame size and type before we do anything if (in.remaining () < 8) return false; boolean haveFrame; int start = in.position (); int frameSize = in.getInt (); if (frameSize > maxFrameLengthFor (session)) { // when frame too big, OK it and let doDecode () generate error haveFrame = true; } else if (in.remaining () < frameSize) { if (in.capacity () < frameSize + 4) { // need to save and restore limit int limit = in.limit (); in.expand (frameSize); in.limit (limit); } haveFrame = false; } else { haveFrame = true; } in.position (start); return haveFrame; } @Override public void finishDecode (IoSession session, ProtocolDecoderOutput out) throws Exception { // zip } public static void setMaxFrameLengthFor (IoSession session, int length) { session.setAttribute ("maxFrameLength", length); } private static int maxFrameLengthFor (IoSession session) { Integer length = (Integer)session.getAttribute ("maxFrameLength"); if (length == null) return Integer.MAX_VALUE; else return length; } }avis-1.2.2/common/src/main/org/avis/io/RequestTrackingFilter.java0000644000175000017500000001662711147555544024622 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import org.apache.mina.common.IoFilter; import org.apache.mina.common.IoFilterAdapter; import org.apache.mina.common.IoFilterChain; import org.apache.mina.common.IoSession; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.Message; import org.avis.io.messages.RequestMessage; import org.avis.io.messages.RequestTimeoutMessage; import org.avis.io.messages.XidMessage; import static java.lang.Math.min; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.MILLISECONDS; /** * A MINA I/O filter that adds tracking for XID-based * RequestMessage's. * *
    *
  • Automatically fills in the {@link XidMessage#request} field of * reply XidMessage's
  • *
  • Generates ErrorMessage's in place of XID-based replies that * have no associated request
  • *
  • Generates TimeoutMessage's for requests that do not receive a * reply within a given timeout.
  • *
      * * @author Matthew Phillips */ public class RequestTrackingFilter extends IoFilterAdapter implements IoFilter { protected ScheduledExecutorService executor; protected long replyTimeout; protected String filterName; /** * Create a new instance. Uses a {@link SharedExecutor}. * * @param replyTimeout The amount of time (in millis) to wait for * a reply. */ public RequestTrackingFilter (long replyTimeout) { this (null, replyTimeout); } /** * Create a new instance. * * @param executor The executor to use for timed callbacks. * @param replyTimeout The amount of time (in millis) to wait for * a reply. */ public RequestTrackingFilter (ScheduledExecutorService executor, long replyTimeout) { this.executor = executor; this.replyTimeout = replyTimeout; } @Override public void onPreAdd (IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { if (executor == null) executor = SharedExecutor.acquire (); this.filterName = name; } @Override public void filterClose (NextFilter nextFilter, IoSession session) throws Exception { nextFilter.filterClose (session); if (SharedExecutor.release (executor)) executor = null; } @Override public void sessionCreated (NextFilter nextFilter, IoSession session) throws Exception { nextFilter.sessionCreated (session); } @Override public void sessionOpened (NextFilter nextFilter, IoSession session) throws Exception { session.setAttribute ("requestTracker", new Tracker (session)); nextFilter.sessionOpened (session); } private static Tracker trackerFor (IoSession session) { return (Tracker)session.getAttribute ("requestTracker"); } @Override public void sessionClosed (NextFilter nextFilter, IoSession session) throws Exception { Tracker tracker = trackerFor (session); if (tracker != null) tracker.dispose (); nextFilter.sessionClosed (session); } @Override public void filterWrite (NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { Object message = writeRequest.getMessage (); if (message instanceof RequestMessage) trackerFor (session).add ((RequestMessage)message); nextFilter.filterWrite (session, writeRequest); } @Override public void messageReceived (NextFilter nextFilter, IoSession session, Object message) throws Exception { if (message instanceof XidMessage && !(message instanceof RequestMessage)) { XidMessage reply = (XidMessage)message; try { reply.request = trackerFor (session).remove (reply); } catch (IllegalArgumentException ex) { message = new ErrorMessage (ex, reply); } } nextFilter.messageReceived (session, message); } /** * An instance of this is attached to each session to track requests. */ class Tracker implements Runnable { private IoSession session; private Map xidToRequest; private ScheduledFuture replyFuture; public Tracker (IoSession session) { this.session = session; this.xidToRequest = new HashMap (); } public synchronized void dispose () { cancelReplyCheck (); } public synchronized void add (RequestMessage request) { xidToRequest.put (request.xid, new Request (request)); scheduleReplyCheck (replyTimeout); } public synchronized RequestMessage remove (XidMessage reply) { Request request = xidToRequest.remove (reply.xid); if (request == null) throw new IllegalArgumentException ("Reply with unknown XID " + reply.xid); else return request.message; } private void cancelReplyCheck () { if (replyFuture != null) { replyFuture.cancel (false); replyFuture = null; } } private void scheduleReplyCheck (long delay) { if (replyFuture == null) replyFuture = executor.schedule (this, delay, MILLISECONDS); } /** * Called periodically to check for timed-out requests. */ public synchronized void run () { replyFuture = null; long now = currentTimeMillis (); long earliestTimeout = now; for (Iterator> i = xidToRequest.entrySet ().iterator (); i.hasNext (); ) { Request request = i.next ().getValue (); if (now - request.sentAt >= replyTimeout) { i.remove (); injectMessage (new RequestTimeoutMessage (request.message)); } else { earliestTimeout = min (earliestTimeout, request.sentAt); } } if (!xidToRequest.isEmpty ()) scheduleReplyCheck (replyTimeout - (now - earliestTimeout)); } private void injectMessage (Message message) { NextFilter filter = session.getFilterChain ().getNextFilter (filterName); filter.messageReceived (session, message); } } static class Request { public RequestMessage message; public long sentAt; public Request (RequestMessage request) { this.message = request; this.sentAt = currentTimeMillis (); } } } avis-1.2.2/common/src/main/org/avis/io/messages/0000755000175000017500000000000011147555544021271 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/io/messages/SubRply.java0000644000175000017500000000272211147555544023537 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; public class SubRply extends XidMessage { public static final int ID = 61; public long subscriptionId; public SubRply () { // zip } public SubRply (XidMessage inReplyTo, long subscriptionId) { super (inReplyTo); this.subscriptionId = subscriptionId; } @Override public int typeId () { return ID; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putLong (subscriptionId); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); subscriptionId = in.getLong (); } } avis-1.2.2/common/src/main/org/avis/io/messages/SecRqst.java0000644000175000017500000000433111147555544023521 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.Keys; import static org.avis.security.Keys.EMPTY_KEYS; public class SecRqst extends RequestMessage { public static final int ID = 54; public Keys addNtfnKeys; public Keys delNtfnKeys; public Keys addSubKeys; public Keys delSubKeys; public SecRqst () { // make it easier for client to create and assign keys later this (EMPTY_KEYS, EMPTY_KEYS, EMPTY_KEYS, EMPTY_KEYS); } public SecRqst (Keys addNtfnKeys, Keys delNtfnKeys, Keys addSubKeys, Keys delSubKeys) { super (nextXid ()); this.addNtfnKeys = addNtfnKeys; this.delNtfnKeys = delNtfnKeys; this.addSubKeys = addSubKeys; this.delSubKeys = delSubKeys; } @Override public int typeId () { return ID; } @Override public Class replyType () { return SecRply.class; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); addNtfnKeys.encode (out); delNtfnKeys.encode (out); addSubKeys.encode (out); delSubKeys.encode (out); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); addNtfnKeys = Keys.decode (in); delNtfnKeys = Keys.decode (in); addSubKeys = Keys.decode (in); delSubKeys = Keys.decode (in); } } avis-1.2.2/common/src/main/org/avis/io/messages/RequestTimeoutMessage.java0000644000175000017500000000222011147555544026434 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; /** * Synthetic message generated when a request timeout has elapsed. * * @author Matthew Phillips */ public class RequestTimeoutMessage extends SyntheticMessage { public static final int ID = -2; /** * The request that timed out. */ public final RequestMessage request; public RequestTimeoutMessage (RequestMessage request) { this.request = request; } @Override public int typeId () { return ID; } } avis-1.2.2/common/src/main/org/avis/io/messages/DropWarn.java0000644000175000017500000000221411147555544023667 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; public class DropWarn extends Message { public static final int ID = 62; @Override public int typeId () { return ID; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { // zip } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { // zip } } avis-1.2.2/common/src/main/org/avis/io/messages/TestConn.java0000644000175000017500000000237011147555544023673 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; public class TestConn extends Message { public static final int ID = 63; public static final TestConn INSTANCE = new TestConn (); private TestConn () { // zip } @Override public int typeId () { return ID; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { // zip } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { // zip } } avis-1.2.2/common/src/main/org/avis/io/messages/SecRply.java0000644000175000017500000000173711147555544023525 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; public class SecRply extends XidMessage { public static final int ID = 55; public SecRply () { // zip } public SecRply (SecRqst inReplyTo) { super (inReplyTo); } @Override public int typeId () { return ID; } } avis-1.2.2/common/src/main/org/avis/io/messages/NotifyEmit.java0000644000175000017500000000253611147555544024231 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.Map; import org.avis.security.Keys; import static org.avis.security.Keys.EMPTY_KEYS; public class NotifyEmit extends Notify { public static final int ID = 56; public NotifyEmit () { super (); } public NotifyEmit (Object... attributes) { super (attributes); } public NotifyEmit (Map attributes) { this (attributes, true, EMPTY_KEYS); } public NotifyEmit (Map attributes, boolean deliverInsecure, Keys keys) { super (attributes, deliverInsecure, keys); } @Override public int typeId () { return ID; } } avis-1.2.2/common/src/main/org/avis/io/messages/DisconnRqst.java0000644000175000017500000000200611147555544024401 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; public class DisconnRqst extends RequestMessage { public static final int ID = 51; public DisconnRqst () { super (nextXid ()); } @Override public int typeId () { return ID; } @Override public Class replyType () { return DisconnRply.class; } } avis-1.2.2/common/src/main/org/avis/io/messages/ErrorMessage.java0000644000175000017500000000316011147555544024532 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import static org.avis.util.Text.shortException; /** * Synthetic message used to signal protocol errors. * * @author Matthew Phillips */ public class ErrorMessage extends SyntheticMessage { public static final int ID = -1; public Throwable error; public Message cause; public ErrorMessage (Throwable error, Message cause) { this.error = error; this.cause = cause; } /** * Generate an error message suitable for presentation as a * debugging aid. */ public String formattedMessage () { StringBuilder message = new StringBuilder (); if (cause == null) message.append ("Error decoding XDR frame"); else message.append ("Error decoding ").append (cause.name ()); if (error != null) message.append (": ").append (shortException (error)); return message.toString (); } @Override public int typeId () { return ID; } } avis-1.2.2/common/src/main/org/avis/io/messages/XidMessage.java0000644000175000017500000000447311147555544024175 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.concurrent.atomic.AtomicInteger; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.io.RequestTrackingFilter; /** * Base class for messages that use a transaction id to identify replies. * * @author Matthew Phillips */ public abstract class XidMessage extends Message { private static final AtomicInteger xidCounter = new AtomicInteger (); public int xid; /** * The request message that triggered this reply. This is for the * convenience of message processing, not part of the serialized * format: you need to add a {@link RequestTrackingFilter} to the * filter chain if you want this automatically filled in. */ public transient RequestMessage request; public XidMessage () { xid = -1; } public XidMessage (XidMessage inReplyTo) { this (inReplyTo.xid); } public XidMessage (int xid) { if (xid <= 0) throw new IllegalArgumentException ("Invalid XID: " + xid); this.xid = xid; } public boolean hasValidXid () { return xid > 0; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { xid = in.getInt (); if (xid <= 0) throw new ProtocolCodecException ("XID must be >= 0: " + xid); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { if (xid == -1) throw new ProtocolCodecException ("No XID"); out.putInt (xid); } protected static int nextXid () { // NOTE: XID must not be zero (sec 7.4) return xidCounter.incrementAndGet (); } } avis-1.2.2/common/src/main/org/avis/io/messages/SubModRqst.java0000644000175000017500000000553711147555544024211 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.Keys; import static org.avis.io.XdrCoding.getBool; import static org.avis.io.XdrCoding.getString; import static org.avis.io.XdrCoding.putBool; import static org.avis.io.XdrCoding.putString; import static org.avis.security.Keys.EMPTY_KEYS; public class SubModRqst extends RequestMessage { public static final int ID = 59; public long subscriptionId; public String subscriptionExpr; public boolean acceptInsecure; public Keys addKeys; public Keys delKeys; public SubModRqst () { // zip } public SubModRqst (long subscriptionId, String subscriptionExpr, boolean acceptInsecure) { this (subscriptionId, subscriptionExpr, EMPTY_KEYS, EMPTY_KEYS, acceptInsecure); } public SubModRqst (long subscriptionId, Keys addKeys, Keys delKeys, boolean acceptInsecure) { this (subscriptionId, "", addKeys, delKeys, acceptInsecure); } public SubModRqst (long subscriptionId, String subscriptionExpr, Keys addKeys, Keys delKeys, boolean acceptInsecure) { super (nextXid ()); this.subscriptionExpr = subscriptionExpr; this.subscriptionId = subscriptionId; this.acceptInsecure = acceptInsecure; this.addKeys = addKeys; this.delKeys = delKeys; } @Override public int typeId () { return ID; } @Override public Class replyType () { return SubRply.class; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putLong (subscriptionId); putString (out, subscriptionExpr); putBool (out, acceptInsecure); addKeys.encode (out); delKeys.encode (out); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); subscriptionId = in.getLong (); subscriptionExpr = getString (in); acceptInsecure = getBool (in); addKeys = Keys.decode (in); delKeys = Keys.decode (in); } } avis-1.2.2/common/src/main/org/avis/io/messages/ConfConn.java0000644000175000017500000000236611147555544023646 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; public class ConfConn extends Message { public static final int ID = 64; public static final ConfConn INSTANCE = new ConfConn (); private ConfConn () { // zip } @Override public int typeId () { return ID; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { // zip } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { // zip } } avis-1.2.2/common/src/main/org/avis/io/messages/NotifyDeliver.java0000644000175000017500000000374211147555544024725 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import static org.avis.io.XdrCoding.getLongArray; import static org.avis.io.XdrCoding.getNameValues; import static org.avis.io.XdrCoding.putLongArray; import static org.avis.io.XdrCoding.putNameValues; public class NotifyDeliver extends Message { public static final int ID = 57; public Map attributes; public long [] secureMatches; public long [] insecureMatches; public NotifyDeliver () { // zip } public NotifyDeliver (Map attributes, long [] secureMatches, long [] insecureMatches) { this.attributes = attributes; this.secureMatches = secureMatches; this.insecureMatches = insecureMatches; } @Override public int typeId () { return ID; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { putNameValues (out, attributes); putLongArray (out, secureMatches); putLongArray (out, insecureMatches); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { attributes = getNameValues (in); secureMatches = getLongArray (in); insecureMatches = getLongArray (in); } } avis-1.2.2/common/src/main/org/avis/io/messages/Nack.java0000644000175000017500000001051111147555544023006 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import static org.avis.io.XdrCoding.getObjects; import static org.avis.io.XdrCoding.getString; import static org.avis.io.XdrCoding.putObjects; import static org.avis.io.XdrCoding.putString; public class Nack extends XidMessage { public static final int ID = 48; // selected NACK codes: see sec 7.4.2 of client spec public static final int PROT_INCOMPAT = 0001; public static final int PROT_ERROR = 1001; public static final int NO_SUCH_SUB = 1002; public static final int IMPL_LIMIT = 2006; public static final int NOT_IMPL = 2007; public static final int PARSE_ERROR = 2101; public static final int EXP_IS_TRIVIAL = 2110; public static final Object [] EMPTY_ARGS = new Object [0]; public int error; public String message; public Object [] args; public Nack () { // zip } public Nack (XidMessage inReplyTo, int error, String message) { this (inReplyTo, error, message, EMPTY_ARGS); } public Nack (XidMessage inReplyTo, int error, String message, Object ...args) { super (inReplyTo); if (message == null) throw new NullPointerException ("Message cannot be null"); this.error = error; this.message = message; this.args = args; } @Override public int typeId () { return ID; } /** * Return the error text for the NACK error code. * * @see #errorTextFor(int) */ public String errorCodeText () { return errorTextFor (error); } /** * Generate a formatted message from the message template returned * by the router. e.g. expand the %1 and %2 in "%1: Expression '%2' * does not refer to a name" to the values in arg [0] and * arg [1]. */ public String formattedMessage () { if (args.length == 0) { return message; } else { StringBuilder str = new StringBuilder (message); for (int i = 0; i < args.length; i++) replace (str, i + 1, args [i]); return str.toString (); } } /** * Replace embedded arg reference(s) with a value. * * @param str The string builder to modify. * @param argNumber The argument number (1..) * @param arg The arg value. */ private static void replace (StringBuilder str, int argNumber, Object arg) { String tag = "%" + argNumber; int index; while ((index = str.indexOf (tag)) != -1) str.replace (index, index + tag.length (), arg.toString ()); } /** * Return the error text for a given NACK error code. */ public static String errorTextFor (int error) { switch (error) { case PROT_INCOMPAT: return "Incompatible protocol"; case PROT_ERROR: return "Communication protocol error"; case NO_SUCH_SUB: return "Unknown subscription ID"; case IMPL_LIMIT: return "Exceeded client connection resource limit"; case NOT_IMPL: return "Feature not implemented"; case PARSE_ERROR: return "Subscription parse error"; case EXP_IS_TRIVIAL: return "Expression is trivial (constant)"; default: return "Error code " + error; } } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putInt (error); putString (out, message); putObjects (out, args); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); error = in.getInt (); message = getString (in); args = getObjects (in); } } avis-1.2.2/common/src/main/org/avis/io/messages/RequestMessage.java0000644000175000017500000000211711147555544025072 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; /** * A XID-based request message that has a defined reply message. * * @author Matthew Phillips */ public abstract class RequestMessage extends XidMessage { public RequestMessage () { super (); } public RequestMessage (int xid) { super (xid); } /** * The type of a successful reply. */ public abstract Class replyType (); } avis-1.2.2/common/src/main/org/avis/io/messages/DisconnRply.java0000644000175000017500000000172711147555544024407 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; public class DisconnRply extends XidMessage { public static final int ID = 52; public DisconnRply () { // zip } public DisconnRply (DisconnRqst inReplyTo) { super (inReplyTo); } @Override public int typeId () { return ID; } } avis-1.2.2/common/src/main/org/avis/io/messages/Message.java0000644000175000017500000000404111147555544023517 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import static org.avis.util.Text.className; /** * Base class for all message types. * * @author Matthew Phillips */ public abstract class Message { /** * The message's unique type ID.

      * * Message ID's (from Elvin Client Protocol 4.0 draft): * *

         * UNotify        = 32,
         *   
         * Nack           = 48,   ConnRqst       = 49,
         * ConnRply       = 50,   DisconnRqst    = 51,
         * DisconnRply    = 52,   Disconn        = 53,
         * SecRqst        = 54,   SecRply        = 55,
         * NotifyEmit     = 56,   NotifyDeliver  = 57,
         * SubAddRqst     = 58,   SubModRqst     = 59,
         * SubDelRqst     = 60,   SubRply        = 61,
         * DropWarn       = 62,   TestConn       = 63,
         * ConfConn       = 64,
         *   
         * QnchAddRqst    = 80,   QnchModRqst    = 81,
         * QnchDelRqst    = 82,   QnchRply       = 83,
         * SubAddNotify   = 84,   SubModNotify   = 85,
         * SubDelNotify   = 86
         * 
      */ public abstract int typeId (); public abstract void encode (ByteBuffer out) throws ProtocolCodecException; public abstract void decode (ByteBuffer in) throws ProtocolCodecException; @Override public String toString () { return name (); } public String name () { return className (this); } } avis-1.2.2/common/src/main/org/avis/io/messages/ConnRqst.java0000644000175000017500000000522511147555544023707 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.Keys; import static java.util.Collections.emptyMap; import static org.avis.io.XdrCoding.getNameValues; import static org.avis.io.XdrCoding.putNameValues; import static org.avis.security.Keys.EMPTY_KEYS; /** * * @author Matthew Phillips */ public class ConnRqst extends RequestMessage { public static final Map EMPTY_OPTIONS = emptyMap (); public static final int ID = 49; public int versionMajor; public int versionMinor; public Map options; public Keys notificationKeys; public Keys subscriptionKeys; public ConnRqst () { // zip } public ConnRqst (int major, int minor) { this (major, minor, EMPTY_OPTIONS, EMPTY_KEYS, EMPTY_KEYS); } public ConnRqst (int major, int minor, Map options, Keys notificationKeys, Keys subscriptionKeys) { super (nextXid ()); this.versionMajor = major; this.versionMinor = minor; this.options = options; this.notificationKeys = notificationKeys; this.subscriptionKeys = subscriptionKeys; } @Override public int typeId () { return ID; } @Override public Class replyType () { return ConnRply.class; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putInt (versionMajor); out.putInt (versionMinor); putNameValues (out, options); notificationKeys.encode (out); subscriptionKeys.encode (out); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); versionMajor = in.getInt (); versionMinor = in.getInt (); options = getNameValues (in); notificationKeys = Keys.decode (in); subscriptionKeys = Keys.decode (in); } } avis-1.2.2/common/src/main/org/avis/io/messages/Notify.java0000644000175000017500000000523311147555544023407 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.HashMap; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.Keys; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; import static org.avis.io.XdrCoding.getBool; import static org.avis.io.XdrCoding.getNameValues; import static org.avis.io.XdrCoding.putBool; import static org.avis.io.XdrCoding.putNameValues; import static org.avis.security.Keys.EMPTY_KEYS; /** * Base class for notify messages. * * @author Matthew Phillips */ public abstract class Notify extends Message { public Map attributes; public boolean deliverInsecure; public Keys keys; protected Notify () { this.attributes = emptyMap (); this.deliverInsecure = true; this.keys = EMPTY_KEYS; } protected Notify (Object... attributes) { this (asAttributes (attributes), true, EMPTY_KEYS); } protected Notify (Map attributes, boolean deliverInsecure, Keys keys) { this.attributes = attributes; this.deliverInsecure = deliverInsecure; this.keys = keys; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { attributes = getNameValues (in); deliverInsecure = getBool (in); keys = Keys.decode (in); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { putNameValues (out, attributes); putBool (out, deliverInsecure); keys.encode (out); } public static Map asAttributes (Object... pairs) { if (pairs.length % 2 != 0) throw new IllegalArgumentException ("Items must be a set of pairs"); HashMap map = new HashMap (); for (int i = 0; i < pairs.length; i += 2) map.put ((String)pairs [i], pairs [i + 1]); return unmodifiableMap (map); } } avis-1.2.2/common/src/main/org/avis/io/messages/SyntheticMessage.java0000644000175000017500000000223711147555544025417 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; public abstract class SyntheticMessage extends Message { @Override public void decode (ByteBuffer in) throws ProtocolCodecException { throw new ProtocolCodecException ("Synthetic message"); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { throw new ProtocolCodecException ("Synthetic message"); } } avis-1.2.2/common/src/main/org/avis/io/messages/SubDelRqst.java0000644000175000017500000000305311147555544024165 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; public class SubDelRqst extends RequestMessage { public static final int ID = 60; public long subscriptionId; public SubDelRqst () { // zip } public SubDelRqst (long subscriptionId) { super (nextXid ()); this.subscriptionId = subscriptionId; } @Override public int typeId () { return ID; } @Override public Class replyType () { return SubRply.class; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putLong (subscriptionId); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); subscriptionId = in.getLong (); } } avis-1.2.2/common/src/main/org/avis/io/messages/ConnRply.java0000644000175000017500000000321011147555544023674 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import static org.avis.io.XdrCoding.getNameValues; import static org.avis.io.XdrCoding.putNameValues; public class ConnRply extends XidMessage { public static final int ID = 50; /** Options requested by client that are supported. */ public Map options; public ConnRply () { // zip } public ConnRply (ConnRqst inReplyTo, Map options) { super (inReplyTo); this.options = options; } @Override public int typeId () { return ID; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); putNameValues (out, options); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); options = getNameValues (in); } } avis-1.2.2/common/src/main/org/avis/io/messages/LivenessFailureMessage.java0000644000175000017500000000171411147555544026544 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; /** * Synthetic message sent when a liveness check fails. * * @author Matthew Phillips */ public class LivenessFailureMessage extends SyntheticMessage { public static final int ID = -3; @Override public int typeId () { return ID; } } avis-1.2.2/common/src/main/org/avis/io/messages/UNotify.java0000644000175000017500000000417411147555544023537 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.Keys; import static org.avis.security.Keys.EMPTY_KEYS; public class UNotify extends Notify { public static final int ID = 32; public int clientMajorVersion; public int clientMinorVersion; public UNotify () { // zip } public UNotify (int clientMajorVersion, int clientMinorVersion, Map attributes) { this (clientMajorVersion, clientMinorVersion, attributes, true, EMPTY_KEYS); } public UNotify (int clientMajorVersion, int clientMinorVersion, Map attributes, boolean deliverInsecure, Keys keys) { super (attributes, deliverInsecure, keys); this.clientMajorVersion = clientMajorVersion; this.clientMinorVersion = clientMinorVersion; } @Override public int typeId () { return ID; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { clientMajorVersion = in.getInt (); clientMinorVersion = in.getInt (); super.decode (in); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { out.putInt (clientMajorVersion); out.putInt (clientMinorVersion); super.encode (out); } } avis-1.2.2/common/src/main/org/avis/io/messages/QuenchPlaceHolder.java0000644000175000017500000000337311147555544025470 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; /** * Placeholder for QnchAddRqst, QnchModRqst and QnchDelRqst that * allows them to be decoded and sent to server. Server will currently * NACK. * * @author Matthew Phillips */ public class QuenchPlaceHolder extends XidMessage { public static final int ID = -2; public static final int ADD = 80; public static final int MODIFY = 81; public static final int DELETE = 82; public int messageType; public int length; public QuenchPlaceHolder (int messageType, int length) { this.messageType = messageType; this.length = length; } @Override public int typeId () { return ID; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); in.skip (length - 4); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { throw new UnsupportedOperationException ("This is just a quench placeholder for now"); } } avis-1.2.2/common/src/main/org/avis/io/messages/Disconn.java0000644000175000017500000000360111147555544023531 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import static org.avis.io.XdrCoding.getString; import static org.avis.io.XdrCoding.putString; public class Disconn extends Message { public static final int ID = 53; public static final int REASON_SHUTDOWN = 1; public static final int REASON_SHUTDOWN_REDIRECT = 2; public static final int REASON_PROTOCOL_VIOLATION = 4; public int reason; public String args; public Disconn () { this (-1, ""); } public Disconn (int reason) { this (reason, ""); } public Disconn (int reason, String args) { this.reason = reason; this.args = args; } @Override public int typeId () { return ID; } public boolean hasArgs () { return args.length () > 0; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { reason = in.getInt (); args = getString (in); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { if (reason == -1) throw new ProtocolCodecException ("Reason not set"); out.putInt (reason); putString (out, args); } } avis-1.2.2/common/src/main/org/avis/io/messages/SubAddRqst.java0000644000175000017500000000423111147555544024150 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.security.Keys; import static org.avis.io.XdrCoding.getBool; import static org.avis.io.XdrCoding.getString; import static org.avis.io.XdrCoding.putBool; import static org.avis.io.XdrCoding.putString; import static org.avis.security.Keys.EMPTY_KEYS; public class SubAddRqst extends RequestMessage { public static final int ID = 58; public String subscriptionExpr; public boolean acceptInsecure; public Keys keys; public SubAddRqst () { // zip } public SubAddRqst (String subExpr) { this (subExpr, EMPTY_KEYS, true); } public SubAddRqst (String subExpr, Keys keys, boolean acceptInsecure) { super (nextXid ()); this.subscriptionExpr = subExpr; this.acceptInsecure = acceptInsecure; this.keys = keys; } @Override public int typeId () { return ID; } @Override public Class replyType () { return SubRply.class; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); putString (out, subscriptionExpr); putBool (out, acceptInsecure); keys.encode (out); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); subscriptionExpr = getString (in); acceptInsecure = getBool (in); keys = Keys.decode (in); } } avis-1.2.2/common/src/main/org/avis/util/0000755000175000017500000000000011147555544020030 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/util/Collections.java0000644000175000017500000000750411147555544023157 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; /** * General collection utilities. * * @author Matthew Phillips */ public final class Collections { private Collections () { // cannot instantiate } /** * Create an immutable set from a number of items. */ public static Set set (E... items) { return unmodifiableSet (new HashSet (asList (items))); } /** * Create an immutable list from a number of items. */ public static List list (E... items) { return unmodifiableList (asList (items)); } /** * Create an immutable map from a number of item pairs: even items * are keys, their adjacent items are values. */ public static Map map (E... pairs) { if (pairs.length % 2 != 0) throw new IllegalArgumentException ("Items must be a set of pairs"); HashMap map = new HashMap (); for (int i = 0; i < pairs.length; i += 2) map.put (pairs [i], pairs [i + 1]); return unmodifiableMap (map); } /** * Join a collection of items with a separator and append to a * string builder. */ public static void join (StringBuilder str, Iterable items, char separator) { boolean first = true; for (Object item : items) { if (!first) str.append (separator); first = false; str.append (item); } } /** * Compute the difference between set1 and set2. This is not * guaranteed to generate a new set instance. */ public static Set difference (Set set1, Set set2) { if (set1.isEmpty () || set2.isEmpty ()) return set1; HashSet diff = new HashSet (); for (T item : set1) { if (!set2.contains (item)) diff.add (item); } return diff; } /** * Compute the union of set1 and set2. This is not guaranteed to * generate a new set instance: it will return set1 or set2 directly * if the other set is empty. */ public static Set union (Set set1, Set set2) { if (set1.isEmpty ()) return set2; else if (set2.isEmpty ()) return set1; HashSet union = new HashSet (); union.addAll (set1); union.addAll (set2); return union; } /** * Compute the union of map1 and map2. This is not guaranteed to * generate a new map instance: it will return map1 or map2 directly * if the other map is empty. If there is a key/value overlap, map2 * wins. */ public static Map union (Map map1, Map map2) { if (map1.isEmpty ()) { return map2; } else if (map2.isEmpty ()) { return map1; } else { HashMap union = new HashMap (); union.putAll (map1); union.putAll (map2); return union; } } } avis-1.2.2/common/src/main/org/avis/util/Pair.java0000644000175000017500000000164011147555544021567 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * A pair items. * * @author Matthew Phillips */ public class Pair { public T1 item1; public T2 item2; public Pair (T1 item1, T2 item2) { this.item1 = item1; this.item2 = item2; } } avis-1.2.2/common/src/main/org/avis/util/Filter.java0000644000175000017500000000254311147555544022124 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * A filter that selects values of the generic type T. * * @author Matthew Phillips */ public interface Filter { /** * Matches nothing. */ public static final Filter MATCH_NONE = new Filter () { public boolean matches (Object value) { return false; } }; /** * Matches everything. */ public static final Filter MATCH_ALL = new Filter () { public boolean matches (Object value) { return true; } }; /** * Test if the filter matches. * * @param value The value to test. * * @return True if the fiLter matches. */ public boolean matches (T value); } avis-1.2.2/common/src/main/org/avis/util/IllegalConfigOptionException.java0000644000175000017500000000215211147555544026442 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * Thrown when an illegal option name or value is used in a * configuration. * * @author Matthew Phillips */ public class IllegalConfigOptionException extends IllegalArgumentException { public IllegalConfigOptionException (String message) { super (message); } public IllegalConfigOptionException (String option, String message) { this ("Error in option \"" + option + "\": " + message); } } avis-1.2.2/common/src/main/org/avis/util/ConcurrentHashSet.java0000644000175000017500000000477211147555544024307 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.Collection; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Concurrent hash set built on {@link ConcurrentHashMap}. Not sure * why Java doesn't ship with one of these already. Shamelessly * cribbed from JBoss's Shotoku project. * * @author Adam Warski (adamw@aster.pl) */ public class ConcurrentHashSet implements Set { private ConcurrentMap map; public ConcurrentHashSet () { map = new ConcurrentHashMap (); } public int size () { return map.size (); } public boolean isEmpty () { return map.isEmpty (); } public boolean contains (Object o) { return map.containsKey (o); } public Iterator iterator () { return map.keySet ().iterator (); } public Object [] toArray () { return map.keySet ().toArray (); } public T [] toArray (T [] a) { return map.keySet ().toArray (a); } public boolean add (K o) { return map.putIfAbsent (o, Boolean.TRUE) == null; } public boolean remove (Object o) { return map.keySet ().remove (o); } public boolean containsAll (Collection c) { return map.keySet ().containsAll (c); } public boolean addAll (Collection c) { boolean ret = false; for (K o : c) { ret |= add (o); } return ret; } public boolean retainAll (Collection c) { boolean ret = false; for (Object o : c) { if (!map.containsKey (o)) { map.remove (o); } } return ret; } public boolean removeAll (Collection c) { boolean ret = false; for (Object o : c) { ret |= remove (o); } return ret; } public void clear () { map.clear (); } } avis-1.2.2/common/src/main/org/avis/util/WildcardFilter.java0000644000175000017500000000361511147555544023577 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import static java.util.Arrays.asList; import static java.util.Collections.singleton; import static java.util.regex.Pattern.CASE_INSENSITIVE; import static org.avis.util.Wildcard.toPattern; /** * A filter that matches strings against wildcard patterns. * * @author Matthew Phillips */ public class WildcardFilter implements Filter { private List patterns; public WildcardFilter (String wildcardPattern) { this (singleton (wildcardPattern)); } public WildcardFilter (String... wildcardPatterns) { this (asList (wildcardPatterns)); } public WildcardFilter (Collection wildcardPatterns) { this.patterns = new ArrayList (wildcardPatterns.size ()); for (String wildcardExpr : wildcardPatterns) patterns.add (toPattern (wildcardExpr, CASE_INSENSITIVE)); } public boolean isNull () { return patterns.isEmpty (); } public boolean matches (String string) { for (Pattern pattern : patterns) { if (pattern.matcher (string).matches ()) return true; } return false; } } avis-1.2.2/common/src/main/org/avis/util/Text.java0000644000175000017500000004140411147555544021622 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.List; import java.util.Map; import java.nio.charset.CharacterCodingException; import static java.lang.Integer.toHexString; import static java.lang.String.CASE_INSENSITIVE_ORDER; import static java.lang.System.arraycopy; import static java.lang.System.identityHashCode; import static java.util.Arrays.asList; import static java.util.Arrays.sort; import static org.avis.io.XdrCoding.fromUTF8; import static org.avis.io.XdrCoding.toUTF8; /** * General text formatting utilities. * * @author Matthew Phillips */ public final class Text { private static final char [] HEX_TABLE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final String [] EMPTY_STRING_ARRAY = new String [0]; private Text () { // cannot be instantiated } /** * Return just the name (minus the package) of an object's class. */ public static String className (Object object) { return className (object.getClass ()); } /** * Return just the name (minus the package) of a class. */ public static String className (Class type) { String name = type.getName (); return name.substring (name.lastIndexOf ('.') + 1); } /** * Generate a short exception message without package name and * message (if null). */ public static String shortException (Throwable ex) { if (ex.getMessage () == null) return className (ex.getClass ()); else return className (ex.getClass ()) + ": " + ex.getMessage (); } /** * Generate a hex ID for an object. */ public static String idFor (Object instance) { return toHexString (identityHashCode (instance)); } /** * Generate a string value of the notification. * * @param attributes The attribute name/value pairs. * * @return The string formatted version of the notification attributes. */ public static String formatNotification (Map attributes) { String [] names = new String [attributes.size ()]; attributes.keySet ().toArray (names); sort (names, CASE_INSENSITIVE_ORDER); StringBuilder str = new StringBuilder (names.length * 16); boolean first = true; for (String name : names) { if (!first) str.append ('\n'); first = false; appendEscaped (str, name, " :"); str.append (": "); appendValue (str, attributes.get (name)); } return str.toString (); } private static void appendValue (StringBuilder str, Object value) { if (value instanceof String) { str.append ('"'); appendEscaped (str, (String)value, '"'); str.append ('"'); } else if (value instanceof Number) { str.append (value); if (value instanceof Long) str.append ('L'); } else { str.append ('['); appendHexBytes (str, (byte [])value); str.append (']'); } } /** * Append a string to a builder, escaping (with '\') any instances * of a special character. */ public static void appendEscaped (StringBuilder builder, String string, char charToEscape) { for (int i = 0; i < string.length (); i++) { char c = string.charAt (i); if (c == charToEscape) builder.append ('\\'); builder.append (c); } } /** * Append a string to a builder, escaping (with '\') any instances * of a set of special characters. */ public static void appendEscaped (StringBuilder builder, String string, String charsToEscape) { for (int i = 0; i < string.length (); i++) { char c = string.charAt (i); if (charsToEscape.indexOf (c) != -1) builder.append ('\\'); builder.append (c); } } /** * Append a byte array to a builder in form: 01 e2 fe ff ... */ public static void appendHexBytes (StringBuilder str, byte [] bytes) { boolean first = true; for (byte b : bytes) { if (!first) str.append (' '); first = false; appendHex (str, b); } } /** * Append the hex form of a byte to a builder. */ public static void appendHex (StringBuilder str, byte b) { str.append (HEX_TABLE [(b >>> 4) & 0x0F]); str.append (HEX_TABLE [(b >>> 0) & 0x0F]); } /** * Parse a string expression as a hex-coded unsigned byte. * * @return A byte in the range 0 - 255 if sign is ignored. */ public static byte hexToByte (String byteExpr) throws InvalidFormatException { if (byteExpr.length () == 0) { throw new InvalidFormatException ("Byte value cannot be empty"); } else if (byteExpr.length () > 2) { throw new InvalidFormatException ("Byte value too long: \"" + byteExpr + "\""); } int value = 0; for (int i = 0; i < byteExpr.length (); i++) value = (value << 4) | hexValue (byteExpr.charAt (i)); return (byte)value; } /** * Parse a string expression as a value. Values may be quoted * strings ("string"), numbers (0.1, 3, 123456789L), or byte arrays * ([0a ff de ad]). * * @param expr The string expression. * * @return The value. * * @throws InvalidFormatException if expr is not parseable. * * @see #stringToNumber(String) * @see #stringToOpaque(String) * @see #quotedStringToString(String) */ public static Object stringToValue (String expr) throws InvalidFormatException { char firstChar = expr.charAt (0); if (firstChar == '"' || firstChar == '\'') return quotedStringToString (expr); else if (firstChar >= '0' && firstChar <= '9') return stringToNumber (expr); else if (firstChar == '[') return stringToOpaque (expr); else throw new InvalidFormatException ("Unrecognised value expression: \"" + expr + "\""); } /** * Parse a numeric int, long or double value. e.g. 32L, 3.14, 42. */ public static Number stringToNumber (String valueExpr) throws InvalidFormatException { try { if (valueExpr.indexOf ('.') != -1) return Double.valueOf (valueExpr); else if (valueExpr.endsWith ("L") || valueExpr.endsWith ("l")) return Long.decode (valueExpr.substring (0, valueExpr.length () - 1)); else return Integer.decode (valueExpr); } catch (NumberFormatException ex) { throw new InvalidFormatException ("Invalid number: " + valueExpr); } } /** * Parse a string value in the format "string", allowing escaped "'s * inside the string. */ public static String quotedStringToString (String valueExpr) throws InvalidFormatException { if (valueExpr.length () == 0) throw new InvalidFormatException ("Empty string"); char quote = valueExpr.charAt (0); if (quote != '\'' && quote != '"') throw new InvalidFormatException ("String must start with a quote"); int last = findFirstNonEscaped (valueExpr, 1, quote); if (last == -1) throw new InvalidFormatException ("Missing terminating quote in string"); else if (last != valueExpr.length () - 1) throw new InvalidFormatException ("Extra characters following string"); return stripBackslashes (valueExpr.substring (1, last)); } /** * Parse an opaque value expression e.g. [00 0f 01]. */ public static byte [] stringToOpaque (String valueExpr) throws InvalidFormatException { if (valueExpr.length () < 2) throw new InvalidFormatException ("Opaque value too short"); else if (valueExpr.charAt (0) != '[') throw new InvalidFormatException ("Missing '[' at start of opaque"); int closingBrace = valueExpr.indexOf (']'); if (closingBrace == -1) throw new InvalidFormatException ("Missing closing \"]\""); else if (closingBrace != valueExpr.length () - 1) throw new InvalidFormatException ("Junk at end of opaque value"); return hexToBytes (valueExpr.substring (1, closingBrace)); } /** * Parse a series of hex pairs as a sequence of unsigned bytes. * Pairs may be separated by optional whitespace. e.g. "0A FF 00 01" * or "deadbeef". */ public static byte [] hexToBytes (String string) throws InvalidFormatException { string = string.replaceAll ("\\s+", ""); if (string.length () % 2 != 0) throw new InvalidFormatException ("Hex bytes must be a set of hex pairs"); byte [] bytes = new byte [string.length () / 2]; for (int i = 0; i < string.length (); i += 2) bytes [i / 2] = hexToByte (string.substring (i, i + 2)); return bytes; } /** * Turn an array of bytes into a hex-encoded string e.g. "00 01 aa de". */ public static String bytesToHex (byte [] bytes) { StringBuilder str = new StringBuilder (bytes.length * 3); appendHexBytes (str, bytes); return str.toString (); } /** * Turn a data block expression into a block of bytes. * * Formats: *
         *   Hex pairs: [0a 02 ff 31]
         *   String:    "hello"
         *   Raw data:  #data
         * 
      * * @param expr The data block expression * @return The data. * * @throws InvalidFormatException if the expression was not valid. */ public static byte [] dataToBytes (byte [] expr) throws InvalidFormatException { if (expr.length == 0) throw new InvalidFormatException ("Expression cannot be empty"); try { switch (expr [0]) { case '[': return stringToOpaque (fromUTF8 (expr, 0, expr.length).trim ()); case '"': return toUTF8 (quotedStringToString (fromUTF8 (expr, 0, expr.length).trim ())); case '#': return slice (expr, 1, expr.length); default: throw new InvalidFormatException ("Unknown data block format"); } } catch (CharacterCodingException ex) { throw new InvalidFormatException ("Invalid UTF-8 string"); } } public static byte [] slice (byte [] bytes, int start, int end) { byte [] slice = new byte [end - start]; arraycopy (bytes, start, slice, 0, slice.length); return slice; } /** * Find the first index of the given character, skipping instances * that are escaped by '\'. */ public static int findFirstNonEscaped (String str, char toFind) { return findFirstNonEscaped (str, 0, toFind); } /** * Find the first index of the given character, skipping instances * that are escaped by '\'. */ public static int findFirstNonEscaped (String str, int start, char toFind) { boolean escaped = false; for (int i = start; i < str.length (); i++) { char c = str.charAt (i); if (c == '\\') { escaped = true; } else { if (!escaped && c == toFind) return i; escaped = false; } } return -1; } /** * Remove any \'s from a string. */ public static String stripBackslashes (String text) throws InvalidFormatException { if (text.indexOf ('\\') != -1) { StringBuilder buff = new StringBuilder (text.length ()); for (int i = 0; i < text.length (); i++) { char c = text.charAt (i); if (c != '\\') { buff.append (c); } else { i++; if (i < text.length ()) buff.append (text.charAt (i)); else throw new InvalidFormatException ("Invalid trailing \\"); } } text = buff.toString (); } return text; } /** * Shortcut to execute split on any whitespace character. */ public static String [] split (String text) { return split (text, "\\s+"); } /** * String.split ("") returns {""} rather than {} like you might * expect: this returns empty array on "". */ public static String [] split (String text, String regex) { if (text.length () == 0) return EMPTY_STRING_ARRAY; else return text.split (regex); } /** * Join a list of objects into a string. * * @param items The items to stringify. * * @return The stringified list. */ public static String join (Object [] items) { return join (items, ", "); } /** * Join a list of objects into a string. * * @param items The items to stringify. * @param separator The separator between items. * * @return The stringified list. */ public static String join (Object [] items, String separator) { return join (asList (items), separator); } /** * Join a list of objects into a string. * * @param items The items to stringify. * @param separator The separator between items. * * @return The stringified list. */ public static String join (List items, String separator) { StringBuilder str = new StringBuilder (); boolean first = true; for (Object item : items) { if (!first) str.append (separator); first = false; str.append (item); } return str.toString (); } /** * Generate human friendly string dump of a Map. */ public static String mapToString (Map map) { StringBuilder str = new StringBuilder (); boolean first = true; for (Map.Entry entry : map.entrySet ()) { if (!first) str.append (", "); first = false; str.append ('{'); str.append (entry.getKey ()).append (" = ").append (entry.getValue ()); str.append ('}'); } return str.toString (); } /** * Expand C-like backslash codes such as \n \x90 etc into their * literal values. * @throws InvalidFormatException */ public static String expandBackslashes (String text) throws InvalidFormatException { if (text.indexOf ('\\') != -1) { StringBuilder buff = new StringBuilder (text.length ()); for (int i = 0; i < text.length (); i++) { char c = text.charAt (i); if (c == '\\') { c = text.charAt (++i); switch (c) { case 'n': c = '\n'; break; case 't': c = '\t'; break; case 'b': c = '\b'; break; case 'r': c = '\r'; break; case 'f': c = '\f'; break; case 'a': c = 7; break; case 'v': c = 11; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': int value = c - '0'; int end = Math.min (text.length (), i + 3); while (i + 1 < end && octDigit (text.charAt (i + 1))) { c = text.charAt (++i); value = value * 8 + (c - '0'); } c = (char)value; break; case 'x': value = 0; end = Math.min (text.length (), i + 3); do { c = text.charAt (++i); value = value * 16 + hexValue (c); } while (i + 1 < end && hexDigit (text.charAt (i + 1))); c = (char)value; break; } } buff.append (c); } text = buff.toString (); } return text; } private static boolean octDigit (char c) { return c >= '0' && c <= '7'; } private static boolean hexDigit (char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } private static int hexValue (char c) throws InvalidFormatException { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') return c - 'a' + 10; else if (c >= 'A' && c <= 'F') return c - 'A' + 10; else throw new InvalidFormatException ("Not a valid hex character: " + c); } } avis-1.2.2/common/src/main/org/avis/util/Wildcard.java0000644000175000017500000000470211147555544022427 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.regex.Pattern; import static java.lang.Character.isLetterOrDigit; import static java.lang.Character.isWhitespace; import static java.util.regex.Pattern.compile; /** * Generates regex patterns that match Unix-style wildcards * and ?. * Currently handles * and ? by generating regex equivalents. */ public final class Wildcard { private Wildcard () { // zip } /** * Generate a patten matcher for a wildcard expression. * * @param wildcard The wildcard expression. * * @return A pattern that matches the wildcard. */ public static Pattern toPattern (String wildcard) { return toPattern (wildcard, 0); } /** * Generate a patten matcher for a wildcard expression. * * @param wildcard The wildcard expression. * @param flags The patten flags (e.g Pattern.CASE_INSENSITIVE). * * @return A pattern that matches the wildcard. */ public static Pattern toPattern (String wildcard, int flags) { StringBuilder regex = new StringBuilder (wildcard.length () * 2); for (int i = 0; i < wildcard.length (); i++) { char c = wildcard.charAt (i); switch (c) { case '*': regex.append (".*"); break; case '?': regex.append ('.'); break; case '\\': if (++i < wildcard.length ()) { regex.append ('\\'); regex.append (wildcard.charAt (i)); } else { regex.append ("\\\\"); } break; default: if (isLetterOrDigit (c) || isWhitespace (c)) regex.append (c); else regex.append ('\\').append (c); } } return compile (regex.toString (), flags); } } avis-1.2.2/common/src/main/org/avis/util/Numbers.java0000644000175000017500000000425211147555544022311 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * General utility functions for messing with numbers. * * @author Matthew Phillips */ public final class Numbers { private Numbers () { // cannot instantiate } /** * Convert a numeric value upwards to a given type. * * @param value A numeric value. * @param type The target type: either Long or Double. * * @return value upconverted to the target type. * * @throws IllegalArgumentException if type is not valid. */ public static Number upconvert (Number value, Class type) { if (type == Long.class) return value.longValue (); else if (type == Double.class) return value.doubleValue (); else throw new IllegalArgumentException ("Cannot upconvert to " + type); } /** * Return the highest precision (class with the largest range) of * two classes. * * @throws IllegalArgumentException if class1 or class2 is not a number. */ public static Class highestPrecision (Class class1, Class class2) { if (precision (class1) >= precision (class2)) return class1; else return class2; } private static int precision (Class type) { if (type == Integer.class) return 0; else if (type == Long.class) return 1; else if (type == Double.class) return 2; else throw new IllegalArgumentException ("Unknown number type " + type); } } avis-1.2.2/common/src/main/org/avis/util/InvalidFormatException.java0000644000175000017500000000171311147555544025313 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * Thrown when a parsing process detects some sort of invalid format * in its input. * * @author Matthew Phillips */ public class InvalidFormatException extends Exception { public InvalidFormatException (String message) { super (message); } } avis-1.2.2/common/src/main/org/avis/util/Util.java0000644000175000017500000000315111147555544021610 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * General Avis utility functions. * * @author Matthew Phillips */ public final class Util { private Util () { // zip } /** * Test if two objects are equal, handling null values and type differences. */ public static boolean valuesEqual (Object value1, Object value2) { if (value1 == value2) return true; else if (value1 == null || value2 == null) return false; else if (value1.getClass () == value2.getClass ()) return value1.equals (value2); else return false; } /** * Check a value is non-null or throw an IllegalArgumentException. * * @param value The value to test. * @param name The name of the value to be used in the exception. */ public static void checkNotNull (Object value, String name) throws IllegalArgumentException { if (value == null) throw new IllegalArgumentException (name + " cannot be null"); } } avis-1.2.2/common/src/main/org/avis/util/IndentingWriter.java0000644000175000017500000000620511147555544024012 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; /** * A PrintWriter that provides auto indented output. */ public class IndentingWriter extends PrintWriter { private int indent; private int indentIncr; public IndentingWriter (OutputStream str) { this (new OutputStreamWriter (str)); } public IndentingWriter (Writer writer) { super (writer); this.indent = 0; this.indentIncr = 2; } /** * Make it easier for StringWriter users. */ @Override public String toString () { return out.toString (); } /** * Set the amount of spaces to increment the indent level by when using * {@link #indent()}. */ public void setIndentIncr (int newValue) { this.indentIncr = newValue; } /** * Get the amount of spaces to increment the indent level by when using * {@link #indent()}. */ public int getIndentIncr () { return indentIncr; } /** * Set the current indent level. You probably want to use {@link #indent()} * and {@link #unindent()} instead. */ public void setIndent (int indent) { this.indent = indent; } /** * Get the current indent level. */ public int getIndent () { return indent; } /** * Increment indent and start a new line. */ public void indentln () { indent (); println (); } /** * Increment the indent level by {@link #getIndentIncr()} spaces. * * @see #unindent() * @see #setIndentIncr(int) */ public void indent () { indent += indentIncr; } /** * Reverse the effect of an {@link #indent()}. */ public void unindent () { indent -= indentIncr; } public void indent (int spaces) { indent += spaces; } public void unindent (int spaces) { indent -= spaces; } @Override public void write (int c) { super.write (c); if (c == '\n') writeIndent (); } @Override public void write (char buf [], int off, int len) { int index = off; for (int i = 0; i < len; i++) write (buf [index++]); } @Override public void write (String s, int off, int len) { write (s.toCharArray (), off, len); } @Override public void println () { super.println (); writeIndent (); flush (); } private void writeIndent () { for (int i = 0; i < indent; i++) write (' '); } } avis-1.2.2/common/src/main/org/avis/util/ListenerList.java0000644000175000017500000001072411147555544023320 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.ArrayList; import java.util.List; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; /** * A generic event listener list. The list is thread safe, but does * not guarantee immediate visibility of changes: i.e. if thread1 * executes a remove () and thread2 executes a fire () immediately * after, thread2 will not necessarily see the result of the remove (). * * @author Matthew Phillips */ public class ListenerList { private List listeners; private Method listenerMethod; /** * Create a new instance. * * @param listenerType The type of the listener interface. * @param method The name of the method to call on the interface. * @param eventType The type of the single event parameter. * * @throws IllegalArgumentException if the listener method could not * be found. */ public ListenerList (Class listenerType, String method, Class eventType) throws IllegalArgumentException { this.listenerMethod = lookupMethod (listenerType, method, eventType); this.listeners = emptyList (); } /** * Create a new instance. * * @param listenerType The type of the listener interface. * @param method The name of the method to call on the interface. * @param paramTypes The paramter types of the listener method. * * @throws IllegalArgumentException if the listener method could not * be found. */ public ListenerList (Class listenerType, String method, Class... paramTypes) throws IllegalArgumentException { this.listenerMethod = lookupMethod (listenerType, method, paramTypes); this.listeners = emptyList (); } public List asList () { return unmodifiableList (listeners); } public void add (E listener) { if (listener == null) throw new IllegalArgumentException ("Listener cannot be null"); List newListeners = new ArrayList (listeners.size () + 4); newListeners.addAll (listeners); newListeners.add (listener); listeners = newListeners; } public void remove (E listener) { List newListeners = new ArrayList (listeners); newListeners.remove (listener); listeners = newListeners; } /** * Fire an event. * * @param event The event parameter. */ public void fire (Object event) { if (!listeners.isEmpty ()) fire (new Object [] {event}); } /** * Fire an event. * * @param args The event parameters. */ public void fire (Object... args) { List fireList = listeners; for (int i = fireList.size () - 1; i >= 0; i--) { E listener = fireList.get (i); try { listenerMethod.invoke (listener, args); } catch (InvocationTargetException ex) { throw new RuntimeException ("Error in listener method", ex.getCause ()); } catch (Exception ex) { // should not be possible throw new RuntimeException (ex); } } } /** * Test if any listeners are in this list. */ public boolean hasListeners () { return !listeners.isEmpty (); } private static Method lookupMethod (Class targetClass, String methodName, Class... paramTypes) { try { Method method = targetClass.getMethod (methodName, paramTypes); method.setAccessible (true); return method; } catch (Exception ex) { throw new IllegalArgumentException ("No method named " + methodName); } } } avis-1.2.2/common/src/main/org/avis/util/IllegalCommandLineOption.java0000644000175000017500000000176511147555544025555 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; public class IllegalCommandLineOption extends IllegalArgumentException { public IllegalCommandLineOption (String message) { super (message); } public IllegalCommandLineOption (String option, String message) { this ("Error in command line option \"" + option + "\": " + message); } } avis-1.2.2/common/src/main/org/avis/util/Streams.java0000644000175000017500000001452111147555544022314 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.Properties; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; /** * General utilities for messing with I/O streams. * * @author Matthew Phillips */ public final class Streams { private Streams () { // zio } /** * Generate a buffered reader wrapper for a reader, if it is not * already one. */ public static BufferedReader bufferedReaderFor (Reader reader) { return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader (reader); } /** * Read all the bytes from a stream and then close it. * * @param in The input stream to read. * @return The bytes read. * * @throws IOException if an error occurs reading stream. * * @see #stringFrom(InputStream) */ public static byte [] bytesFrom (InputStream in) throws IOException { try { ByteArrayOutputStream str = new ByteArrayOutputStream (4096); byte [] buffer = new byte [4096]; int length; while ((length = in.read (buffer)) != -1) str.write (buffer, 0, length); return str.toByteArray (); } finally { close (in); } } /** * Reader's and InputStream's (almost unbelievably) do not have a * way to tell when the stream is at eof without modifying it. This * uses mark () and read () to non-destructively test for eof. The * stream must support mark (). */ public static boolean eof (Reader in) throws IOException { in.mark (10); if (in.read () == -1) { return true; } else { in.reset (); return false; } } /** * Open an input stream on a file. */ public static InputStream fileStream (File file) throws FileNotFoundException { return fileStream (file.getPath ()); } /** * Open an input stream on a file. */ public static InputStream fileStream (String filename) throws FileNotFoundException { return new BufferedInputStream (new FileInputStream (filename)); } /** * Load a set of java.util.Properties from an input stream and close it. */ public static Properties propertiesFrom (InputStream in) throws IOException { try { Properties properties = new Properties (); properties.load (in); return properties; } finally { close (in); } } /** * Read a line from a Reader. The reader must support mark () and * reset (). Lines may be terminated by NL, CRLF/NL or NL/CRLF. * * @param in The reader. * @return The line read, or null if at EOF. * * @throws IOException if an error occurs reading data. */ public static String readLine (Reader in) throws IOException { int state = 0; // 0 = init, 1 = in line, 2 = in CR, 3 = in NL StringBuilder str = new StringBuilder (); for (;;) { in.mark (1); int c = in.read (); switch (c) { case -1: switch (state) { case 0: return null; default: return str.toString (); } case '\r': switch (state) { case 0: case 1: state = 2; break; case 2: in.reset (); // second \r: push back and return default: return str.toString (); } break; case '\n': switch (state) { case 0: case 1: state = 3; break; case 3: in.reset (); // second \n: push back and return default: return str.toString (); } break; default: switch (state) { case 2: case 3: in.reset (); return str.toString (); default: state = 1; str.append ((char)c); } } } } /** * Open an input stream on a resource. */ public static InputStream resourceStream (String resource) throws FileNotFoundException { InputStream in = Streams.class.getResourceAsStream (resource); if (in == null) throw new FileNotFoundException ("Missing resource: " + resource); else return in; } /** * Read the entirety of a UTF-8 encoded input stream into a string. */ public static String stringFrom (InputStream stream) throws IOException { return stringFrom (new InputStreamReader (stream, "UTF-8")); } /** * Read the entirety of stream into a string. * * @see #bytesFrom(InputStream) */ public static String stringFrom (Reader reader) throws IOException { try { StringBuilder str = new StringBuilder (); char [] buffer = new char [4096]; int length; while ((length = reader.read (buffer)) != -1) str.append (buffer, 0, length); return str.toString (); } finally { close (reader); } } /** * Close an input stream. Eats the (pointless, because there's * nothing we can do) IO exception and returns true/false instead. */ public static boolean close (Closeable in) { try { in.close (); return true; } catch (IOException ex) { return false; } } } avis-1.2.2/common/src/main/org/avis/util/CommandLineOptions.java0000644000175000017500000001470611147555544024445 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; import java.util.LinkedList; import java.util.Queue; import static java.lang.Integer.parseInt; import static java.util.Arrays.asList; /** * A set of command line options. Subclasses implement * {@link #handleArg(Queue)} and {@link #checkOptions()}. * * @author Matthew Phillips */ public abstract class CommandLineOptions { /** * Create a new instance. * * @see #handleOptions(String...) */ public CommandLineOptions () { // zip } /** * Create a new instance and immediately parse an array of command * line options and create an option set. * * @param argv The command line options. * * @throws IllegalConfigOptionException if an error is detected. * * @see #handleOptions(String...) */ public CommandLineOptions (String... argv) throws IllegalCommandLineOption { handleOptions (argv); } protected void handleOptions (String... argv) throws IllegalCommandLineOption { Queue args = new LinkedList (asList (argv)); while (!args.isEmpty ()) { if (!argHandled (args)) { String arg = args.peek (); if (arg.startsWith ("-")) throw new IllegalCommandLineOption (arg, "Unknown option"); else throw new IllegalCommandLineOption ("Unknown extra parameter: " + arg); } } checkOptions (); } /** * Handle an error in parsing command line options or starting the * command line application by printing an error on the console and * exiting the VM with an error code. * * @param appName The application's name. * @param usage The command line usage string. The app name will be * appended to this, so this should just include the * options summary plus any detail. * @param ex The error that triggered the exit. * IllegalCommandLineOption is handled specially by * printing a usage string. */ public static void handleError (String appName, String usage, Exception ex) { if (ex instanceof IllegalCommandLineOption) { System.err.println (appName + ": " + ex.getMessage ()); System.err.println (); System.err.println ("Usage:"); System.err.println (); System.err.print (" "); System.err.print (appName); System.err.print (' '); System.err.println (usage); System.exit (1); } else { System.err.println (appName + ": error on startup: " + ex.getMessage ()); System.err.println (); System.exit (2); } } private boolean argHandled (Queue args) { int size = args.size (); handleArg (args); return size != args.size (); } /** * If an argument is found at the head of the queue that can be * handled, handle it and remove (plus any parameters), otherwise do * nothing. * * @param args The commnd line queue. * * @throws IllegalConfigOptionException */ protected abstract void handleArg (Queue args) throws IllegalCommandLineOption; /** * Called at the end of parsing. Throw IllegalOptionException if the * command line options are not in a valid state e.g. a required * parameter not specified. */ protected void checkOptions () throws IllegalCommandLineOption { // zip } protected static String bareArg (Queue args) throws IllegalCommandLineOption { if (args.isEmpty ()) throw new IllegalCommandLineOption ("Missing parameter"); else return args.remove (); } /** * Take an option switch plus its string parameter (in the form of * {-s string}) off the queue. The string parameter may not begin * with "-" (i.e look like an option switch) * * @param args The args queue. * @return The parameter to the switch. * @throws IllegalConfigOptionException if no parameter is present. */ protected static String arg (Queue args) throws IllegalCommandLineOption { String option = args.remove (); if (args.isEmpty ()) throw new IllegalCommandLineOption (option, "Missing parameter"); else return args.remove (); } /** * Take an option switch plus its string parameter (in the form of * {-s string}) off the queue. This is the same as arg (), but the * string parameter may not begin with "-" (i.e look like an option * switch) * * @param args The args queue. * @return The parameter to the switch. * @throws IllegalConfigOptionException if no parameter is present. */ protected static String stringArg (Queue args) throws IllegalCommandLineOption { String option = args.peek (); String arg = arg (args); if (arg.startsWith ("-")) throw new IllegalCommandLineOption (option, "Missing parameter: was followed by option" + arg); return arg; } /** * Take an option switch plus its integer parameter (in the form of * {-i integer}) off the queue. * * @param args The args queue. * @return The parameter to the argument. * @throws IllegalConfigOptionException if no parameter is present or it * is not a number. */ protected static int intArg (Queue args) { String arg = args.peek (); String value = stringArg (args); try { return parseInt (value); } catch (NumberFormatException ex) { throw new IllegalCommandLineOption (arg, "Not a valid number: " + value); } } /** * Take an argument off the queue and return true if it matches the * given argument. */ protected static boolean takeArg (Queue args, String arg) { if (args.peek ().equals (arg)) { args.remove (); return true; } else { return false; } } } avis-1.2.2/common/src/main/org/avis/util/CommandLine.java0000644000175000017500000000411411147555544023061 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.util; /** * General command line utilities. * * @author Matthew Phillips */ public final class CommandLine { private CommandLine () { // cannot instantiate } /** * Get a string argument from a given index, throwing a descriptive * exception if the argument is missing. * * @param args The command line arguments. * @param arg The argument index to retrieve. * @return The argument at arg. * * @throws IllegalCommandLineOption if arg does not exist. */ public static String stringArg (String [] args, int arg) { try { return args [arg]; } catch (ArrayIndexOutOfBoundsException ex) { throw new IllegalCommandLineOption (args [arg - 1], "Missing parameter"); } } /** * Get an integer argument from a given index, throwing a * descriptive exception if the argument is missing or not a number. * * @param args The command line arguments. * @param arg The argument index to retrieve. * @return The argument at arg. * * @throws IllegalCommandLineOption if arg does not exist or is not * a number. */ public static int intArg (String [] args, int arg) { try { return Integer.parseInt (stringArg (args, arg)); } catch (NumberFormatException ex) { throw new IllegalCommandLineOption (args [arg - 1], "Not a valid number: " + args [arg]); } } } avis-1.2.2/common/src/main/org/avis/logging/0000755000175000017500000000000011147555544020501 5ustar dpocockdpocockavis-1.2.2/common/src/main/org/avis/logging/LogListener.java0000644000175000017500000000170411147555544023575 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.logging; /** * A listener to log messages. * * @see Log#addLogListener(LogListener) * * @author Matthew Phillips */ public interface LogListener { /** * Invoked when a message is posted to the log. */ public void messageLogged (LogEvent e); } avis-1.2.2/common/src/main/org/avis/logging/Log.java0000644000175000017500000001654211147555544022075 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.logging; import java.util.Date; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; import java.text.DateFormat; import java.text.SimpleDateFormat; import org.avis.util.ListenerList; /** * A simple logging facility. * * @author Matthew Phillips */ public final class Log { public static final int TRACE = 0; public static final int DIAGNOSTIC = 1; public static final int INFO = 2; public static final int WARNING = 3; public static final int ALARM = 4; public static final int INTERNAL_ERROR = 5; private static final String [] TYPE_NAMES = new String [] {"Trace", "Diagnostic", "Info", "Warning", "Alarm", "Internal Error"}; private static final ThreadLocal dateFormat = new ThreadLocal () { @Override protected DateFormat initialValue () { return new SimpleDateFormat ("MMM dd HH:mm:ss"); } }; private static String applicationName; private static PrintWriter stdout; private static PrintWriter stderr; private static boolean [] enabledTypes; private static ListenerList listeners; static { stdout = new PrintWriter (System.out); stderr = new PrintWriter (System.err); listeners = new ListenerList (LogListener.class, "messageLogged", LogEvent.class); enabledTypes = new boolean [6]; logAll (); enableLogging (TRACE, false); enableLogging (DIAGNOSTIC, false); } private Log () { // cannot instantiate } /** * Enable/disable logging for a given type (INFO, WARNING, etc). */ public static void enableLogging (int type, boolean isEnabled) { enabledTypes [type] = isEnabled; } /** * Enable logging for all types. */ public static void logAll () { for (int i = 0; i < enabledTypes.length; i++) enabledTypes [i] = true; } /** * Disable logging for all types. */ public static void logNone () { for (int i = 0; i < enabledTypes.length; i++) enabledTypes [i] = false; } /** * Test if we should be logging a given type. */ public static boolean shouldLog (int type) { return enabledTypes [type]; } /** * Set the global application name to be used in log messages. */ public static void setApplicationName (String name) { applicationName = name; } public static String applicationName () { return applicationName; } public static void trace (String message, Object source) { log (TRACE, message, source); } public static void info (String message, Object source) { log (INFO, message, source); } public static void diagnostic (String message, Object source) { log (DIAGNOSTIC, message, source); } public static void diagnostic (String message, Object source, Throwable ex) { log (DIAGNOSTIC, message, source, ex); } public static void alarm (String message, Object source) { log (ALARM, message, source); } public static void alarm (String message, Object source, Throwable ex) { log (ALARM, message, source, ex); } public static void warn (String message, Object source) { log (WARNING, message, source); } public static void warn (String message, Object source, Throwable ex) { log (WARNING, message, source, ex); } public static void internalError (String message, Object source) { log (INTERNAL_ERROR, message, source); } public static void internalError (String message, Object source, Throwable ex) { log (INTERNAL_ERROR, message, source, ex); } private static void log (int type, String message, Object source) { log (type, message, source, null); } private static void log (int type, String message, Object source, Throwable exception) { if (shouldLog (type)) { Date time = new Date (); StringBuilder str = new StringBuilder (); printMessage (str, type, time, message, exception); PrintWriter output = (type >= WARNING) ? stderr : stdout; output.println (str); if (exception != null) { output.println ("Exception trace:"); printExceptionTrace (output, exception); } output.flush (); } synchronized (listeners) { if (listeners.hasListeners ()) { listeners.fire (new LogEvent (source, new Date (), type, message, exception)); } } } private static void printMessage (StringBuilder str, int type, Date time, String messageStr, Throwable exception) { str.append (dateFormat.get ().format (time)); if (applicationName != null) str.append (": ").append (applicationName); str.append (": ").append (TYPE_NAMES [type]); str.append (": ").append (messageStr); if (exception != null && exception.getMessage () != null) str.append (": " + exception.getMessage ()); } private static void printExceptionTrace (PrintWriter str, Throwable exception) { exception.printStackTrace (str); Throwable legacyNestedException = legacyNestedException (exception); if (legacyNestedException != null) { str.println ("--------------------"); str.println ("Nested exception:"); printExceptionTrace (str, legacyNestedException); } } private static Throwable legacyNestedException (Throwable exception) { if (exception instanceof SQLException) return ((SQLException)exception).getNextException (); else if (exception instanceof InvocationTargetException) return ((InvocationTargetException)exception).getTargetException (); else return null; } /** * Add a listener to all log messages. */ public static void addLogListener (LogListener listener) { synchronized (listeners) { listeners.add (listener); } } /** * Remove a * {@linkplain #addLogListener(LogListener) previously added} * listener. */ public static void removeLogListener (LogListener listener) { synchronized (listeners) { listeners.remove (listener); } } /** * Generate the same string that would be sent to the console for a * given log event. */ public static String toLogString (LogEvent e) { StringBuilder str = new StringBuilder (); printMessage (str, e.type, e.time, e.message, e.exception); return str.toString (); } } avis-1.2.2/common/src/main/org/avis/logging/LogEvent.java0000644000175000017500000000253711147555544023076 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.logging; import java.util.Date; /** * An event sent by the log to * {@linkplain Log#addLogListener(LogListener) registered listeners} * when a message is logged. * * @see Log#addLogListener(LogListener) * * @author Matthew Phillips */ public class LogEvent { public final Date time; public final Object source; public final int type; public final String message; public final Throwable exception; public LogEvent (Object source, Date time, int type, String message, Throwable exception) { this.source = source; this.time = time; this.type = type; this.message = message; this.exception = exception; } } avis-1.2.2/common/.classpath0000644000175000017500000000150510750123566015565 0ustar dpocockdpocock avis-1.2.2/common/build.xml0000644000175000017500000000474510750123566015434 0ustar dpocockdpocock Avis common core avis-1.2.2/common/.project0000644000175000017500000000056210750123566015253 0ustar dpocockdpocock avis.common org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature avis-1.2.2/common/build-common.xml0000644000175000017500000000325310750123566016713 0ustar dpocockdpocock avis-1.2.2/common/.settings/0000755000175000017500000000000010764234112015512 5ustar dpocockdpocockavis-1.2.2/common/.settings/org.eclipse.jdt.core.prefs0000644000175000017500000001040410764234112022473 0ustar dpocockdpocock#Tue Jul 24 16:47:21 CST 2007 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.autoboxing=ignore org.eclipse.jdt.core.compiler.problem.deprecation=warning org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=warning org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=error org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore org.eclipse.jdt.core.compiler.problem.nullReference=warning org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled org.eclipse.jdt.core.compiler.problem.unusedImport=warning org.eclipse.jdt.core.compiler.problem.unusedLabel=warning org.eclipse.jdt.core.compiler.problem.unusedLocal=warning org.eclipse.jdt.core.compiler.problem.unusedParameter=warning org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.source=1.5 avis-1.2.2/common/.settings/org.eclipse.jdt.ui.prefs0000644000175000017500000000014010750123566022161 0ustar dpocockdpocock#Sat May 26 14:03:18 CST 2007 eclipse.preferences.version=1 internal.default.compliance=default avis-1.2.2/server/0000755000175000017500000000000011147555544013625 5ustar dpocockdpocockavis-1.2.2/server/bin/0000755000175000017500000000000012200662574014366 5ustar dpocockdpocockavis-1.2.2/server/bin/avisd0000755000175000017500000000322511147460224015421 0ustar dpocockdpocock#!/bin/sh set -e daemon=0 avis_opts="" # find avis-router.jar in either lib or libexec/avis base_dir=`dirname "$0"` avisd_jar="$base_dir/../lib/avis-router.jar" if [ ! -f "$avisd_jar" ]; then avisd_jar="$base_dir/../libexec/avis/avis-router.jar" fi if [ ! -f "$avisd_jar" ]; then echo "Cannot find avis-router.jar" exit 1 fi usage () { local NL=$'\x0a' local help="\ Usage: $0 [-h] [-v] [-vv] [-p port] [-c file] $NL\ [-daemon] [-pidfile file] [-logfile file] $NL\ -h : This text$NL\ -v and -vv : Increase verbosity$NL\ -p port : Set port to listen on$NL\ -c file : Load config from file$NL\ -daemon : Run as daemon$NL\ -pidfile file : Output process ID to file$NL\ -logfile file : Log output to file (only with -daemon)$NL" echo "$help" >&2 } while [ $# -gt 0 ]; do case $1 in -pidfile) pidfile=$2; shift 2;; -daemon) daemon=1; shift;; -logfile) logfile=$2; shift 2;; -v|-vv) avis_opts="$avis_opts $1"; shift;; -p|-c) avis_opts="$avis_opts $1 $2"; shift; if [ ! -z $1 ]; then shift; fi;; *) usage; exit 1;; esac done java_options="-server -Xms12M -Xmx96M -Xverify:none -XX:+UseParallelGC" command="java $java_options -jar $avisd_jar $avis_opts" if [ $daemon -eq 1 ]; then if [ -z $logfile ]; then logfile=/dev/null fi ( exec $command < /dev/null >> $logfile 2>&1 ) & if [ "x$pidfile" != "x" ]; then echo $! > "$pidfile"; fi else if [ "x$pidfile" != "x" ]; then echo $$ > "$pidfile"; fi if [ -z "$logfile" ]; then exec $command else exec $command >> $logfile 2>&1 fi fi avis-1.2.2/server/lib/0000755000175000017500000000000012200662546014363 5ustar dpocockdpocockavis-1.2.2/server/src/0000755000175000017500000000000011147555544014414 5ustar dpocockdpocockavis-1.2.2/server/src/test/0000755000175000017500000000000011147555544015373 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/0000755000175000017500000000000011147555544016162 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/0000755000175000017500000000000011147555544017124 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/federation/0000755000175000017500000000000011147555544021244 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/federation/JUTestAstXdrCoding.java0000644000175000017500000000534011147555544025541 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.federation.io.XdrAstCoding; import org.avis.subscription.ast.Node; import org.junit.Test; import static org.avis.subscription.ast.Nodes.unparse; import static org.avis.subscription.ast.nodes.Const.CONST_FALSE; import static org.avis.subscription.ast.nodes.Const.CONST_TRUE; import static org.avis.subscription.parser.SubscriptionParserBase.parse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class JUTestAstXdrCoding { @Test public void astIO () throws Exception { roundtrip ("require (foobar)"); roundtrip ("foobar == 2"); roundtrip ("int64 (foobar)"); roundtrip ("foobar != 'hello'"); roundtrip ("~foobar << 2 == 6L"); roundtrip ("! (foobar <= 3.14)"); roundtrip ("foobar == 'hello' || " + "greebo & 1 == 1 && begins-with (greebo, 'frob', 'wibble')"); roundtrip ("size (foobar) > 10 && (foobar - 20 >= 100)"); roundtrip ("regex (name, 'a.*b')"); roundtrip ("name == 1 || name != 8 || name < 3"); roundtrip ("name == 1 || name != 8 ^^ ! (name >= 3)"); roundtrip ("name == name + 3 / 4 % foobar >>> 6"); roundtrip ("name == x & 1 | y"); roundtrip ("name == x ^ 1 | -y"); roundtrip ("int32 (name1) || string (name2)"); // check CONST_FALSE gets turned into EMPTY node roundtrip (CONST_FALSE); // can't do CONST_TRUE try { roundtrip (CONST_TRUE); fail (); } catch (ProtocolCodecException ex) { // ok } } private static void roundtrip (String expr) throws Exception { roundtrip (parse (expr)); } private static void roundtrip (Node ast) throws Exception { ByteBuffer in = ByteBuffer.allocate (1024); XdrAstCoding.encodeAST (in, ast); in.flip (); Node copy = XdrAstCoding.decodeAST (in); assertEquals (0, in.remaining ()); assertEquals (unparse (ast), unparse (copy)); } } avis-1.2.2/server/src/test/org/avis/federation/TestUtils.java0000644000175000017500000000477311147555544024062 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.concurrent.TimeUnit; import static java.lang.System.currentTimeMillis; import static java.lang.Thread.sleep; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static junit.framework.Assert.fail; public class TestUtils { public static final long MAX_WAIT = 8000; /** * Wait up to 8 seconds for a connector to be in connected state. */ public static void waitForConnect (Connector connector) throws InterruptedException { long finish = currentTimeMillis () + MAX_WAIT; while (currentTimeMillis () < finish && !connector.isConnected ()) sleep (200); if (!connector.isConnected ()) fail ("Failed to connect"); } /** * Wait up to 8 seconds for a connector to go to waiting for an * async connect. */ public static void waitForAsyncConnect (Connector connector) throws InterruptedException { long finish = currentTimeMillis () + MAX_WAIT; while (currentTimeMillis () < finish && !connector.isWaitingForAsyncConnection ()) { sleep (200); } if (!connector.isWaitingForAsyncConnection ()) fail ("Failed to go to async connect state"); } public static void waitForSingleLink (final Acceptor acceptor) throws InterruptedException { waitUpto (MAX_WAIT, MILLISECONDS, new Predicate () { public boolean satisfied () { return acceptor.links.size () == 1; } }); } public static void waitUpto (long duration, TimeUnit unit, Predicate predicate) throws InterruptedException { long finishAt = currentTimeMillis () + unit.toMillis (duration); while (!predicate.satisfied () && currentTimeMillis () < finishAt) sleep (10); if (!predicate.satisfied ()) fail (); } } avis-1.2.2/server/src/test/org/avis/federation/Predicate.java0000644000175000017500000000141111147555544024004 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; public interface Predicate { public boolean satisfied (); } avis-1.2.2/server/src/test/org/avis/federation/JUTestFederationTLS.java0000644000175000017500000001427211147555544025657 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.net.InetSocketAddress; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.ThreadModel; import org.apache.mina.filter.SSLFilter; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.avis.federation.io.FederationFrameCodec; import org.avis.federation.io.messages.FedConnRqst; import org.avis.io.TestingIoHandler; import org.avis.logging.Log; import org.avis.router.Router; import org.avis.router.RouterOptionSet; import org.avis.router.RouterOptions; import org.avis.util.LogFailTester; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.avis.federation.Federation.VERSION_MAJOR; import static org.avis.federation.Federation.VERSION_MINOR; import static org.avis.federation.JUTestFederation.PORT1; import static org.avis.federation.JUTestFederation.PORT2; import static org.avis.federation.TestUtils.waitForConnect; import static org.avis.federation.TestUtils.waitForSingleLink; import static org.avis.io.TLS.defaultSSLContext; import static org.avis.logging.Log.INFO; import static org.avis.logging.Log.enableLogging; import static org.avis.util.Collections.set; public class JUTestFederationTLS { private LogFailTester logTester; private boolean oldLogInfoState; @Before public void setup () { // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); oldLogInfoState = Log.shouldLog (INFO); enableLogging (INFO, false); logTester = new LogFailTester (); } @After public void tearDown () { enableLogging (INFO, oldLogInfoState); logTester.assertOkAndDispose (); } @Test public void listener () throws Exception { FederationClass fedClass = StandardFederatorSetup.defaultClass (); FederationClasses classes = new FederationClasses (fedClass); RouterOptionSet routerOptionSet = new RouterOptionSet (); routerOptionSet.inheritFrom (FederationOptionSet.OPTION_SET); RouterOptions options = new RouterOptions (routerOptionSet); options.set ("Listen", "elvin:/secure/127.0.0.1:" + PORT1); options.set ("TLS.Keystore", getClass ().getResource ("router.ks")); options.set ("TLS.Keystore-Passphrase", "testing"); Router router = new Router (options); EwafURI ewafURI = new EwafURI ("ewaf:/secure/127.0.0.1:" + (PORT1 + 1)); Acceptor acceptor = new Acceptor (router, "server1", classes, set (ewafURI), options); // connector TestingIoHandler listener = new TestingIoHandler (); IoSession session = connectFederationTLS (router, ewafURI, listener); session.write (new FedConnRqst (VERSION_MAJOR, VERSION_MINOR, "server2")); waitForSingleLink (acceptor); acceptor.close (); session.close (); router.close (); } /** * Full connect/accept test over TLS. */ @Test public void full () throws Exception { FederationClass fedClass = StandardFederatorSetup.defaultClass (); EwafURI ewafURI = new EwafURI ("ewaf:/secure/127.0.0.1:" + (PORT1 + 1)); // router1 (listener) FederationClasses classes = new FederationClasses (fedClass); RouterOptionSet routerOptionSet = new RouterOptionSet (); routerOptionSet.inheritFrom (FederationOptionSet.OPTION_SET); RouterOptions options1 = new RouterOptions (routerOptionSet); options1.set ("Listen", "elvin://127.0.0.1:" + PORT1); options1.set ("TLS.Keystore", getClass ().getResource ("router.ks")); options1.set ("TLS.Keystore-Passphrase", "testing"); Router router1 = new Router (options1); Acceptor acceptor = new Acceptor (router1, "server1", classes, set (ewafURI), options1); // router 2 (connector) RouterOptions options2 = new RouterOptions (routerOptionSet); options2.set ("Listen", "elvin://127.0.0.1:" + PORT2); options2.set ("TLS.Keystore", getClass ().getResource ("router.ks")); options2.set ("TLS.Keystore-Passphrase", "testing"); Router router2 = new Router (options2); Connector connector = new Connector (router2, "server2", ewafURI, fedClass, options2); waitForConnect (connector); waitForSingleLink (acceptor); connector.close (); acceptor.close (); router1.close (); router2.close (); } /** * Create a connection to federation listener. */ private static IoSession connectFederationTLS (Router router, EwafURI uri, IoHandler listener) throws Exception { SocketConnector connector = new SocketConnector (1, router.executor ()); SocketConnectorConfig connectorConfig = new SocketConnectorConfig (); connector.setWorkerTimeout (0); connectorConfig.setThreadModel (ThreadModel.MANUAL); connectorConfig.setConnectTimeout (20); SSLFilter filter = new SSLFilter (defaultSSLContext ()); filter.setUseClientMode (true); connectorConfig.getFilterChain ().addFirst ("ssl", filter); connectorConfig.getFilterChain ().addLast ("codec", FederationFrameCodec.FILTER); ConnectFuture future = connector.connect (new InetSocketAddress (uri.host, uri.port), listener, connectorConfig); future.join (); return future.getSession (); } } avis-1.2.2/server/src/test/org/avis/federation/JUTestFederationOptions.java0000644000175000017500000001437611147555544026655 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Properties; import org.junit.Test; import org.avis.config.OptionTypeParam; import org.avis.config.Options; import org.avis.subscription.ast.Node; import org.avis.subscription.parser.ParseException; import org.avis.util.IllegalConfigOptionException; import org.avis.util.Pair; import static org.avis.federation.FederationClass.parse; import static org.avis.subscription.ast.Nodes.unparse; import static org.avis.util.Collections.list; import static org.avis.util.Collections.set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class JUTestFederationOptions { @SuppressWarnings("unchecked") @Test public void basic () throws Exception { Options options = new Options (FederationOptionSet.OPTION_SET); Properties props = new Properties (); props.setProperty ("Federation.Activated", "Yes"); props.setProperty ("Federation.Subscribe[Internal]", "TRUE"); props.setProperty ("Federation.Subscribe[External]", "require (Message)"); props.setProperty ("Federation.Provide[Internal]", "TRUE"); props.setProperty ("Federation.Connect[Internal]", "ewaf://localhost ewaf://public"); props.setProperty ("Federation.Connect[External]", "ewaf://public.elvin.org"); props.setProperty ("Federation.Apply-Class[External]", "@.elvin.org domain"); props.setProperty ("Federation.Listen", "ewaf://0.0.0.0 ewaf://hello:7778"); props.setProperty ("Federation.Add-Incoming-Attribute[External][String]", "'hello'"); props.setProperty ("Federation.Add-Incoming-Attribute[External][Int32]", "42"); props.setProperty ("Federation.Add-Incoming-Attribute[External][Int64]", "12L"); props.setProperty ("Federation.Add-Incoming-Attribute[External][Real64]", "0.1"); props.setProperty ("Federation.Add-Incoming-Attribute[External][Opaque]", "[de ad]"); props.setProperty ("Federation.Add-Outgoing-Attribute[External][String]", "'hello world'"); options.setAll (props); assertEquals (true, options.getBoolean ("Federation.Activated")); assertEquals (set (new EwafURI ("ewaf://0.0.0.0"), new EwafURI ("ewaf://hello:7778")), options.get ("Federation.Listen")); Map subscribe = options.getParamOption ("Federation.Subscribe"); assertEquals (astExpr ("require (Message)"), unparse ((Node)subscribe.get ("External"))); assertEquals (astExpr ("TRUE"), unparse ((Node)subscribe.get ("Internal"))); Map connect = options.getParamOption ("Federation.Connect"); assertEquals (set (new EwafURI ("ewaf://localhost"), new EwafURI ("ewaf://public")), connect.get ("Internal")); assertEquals (set (new EwafURI ("ewaf://public.elvin.org")), connect.get ("External")); Map applyClass = options.getParamOption ("Federation.Apply-Class"); assertEquals (set ("@.elvin.org", "domain"), applyClass.get ("External")); assertEquals (set (new EwafURI ("ewaf://public.elvin.org")), connect.get ("External")); Map addIncomingAttribute = options.getParamOption ("Federation.Add-Incoming-Attribute"); Map incomingAttrs = (Map)addIncomingAttribute.get ("External"); assertEquals ("hello", incomingAttrs.get ("String")); assertEquals (42, incomingAttrs.get ("Int32")); assertEquals (12L, incomingAttrs.get ("Int64")); assertEquals (0.1, incomingAttrs.get ("Real64")); assertTrue (Arrays.equals (new byte [] {(byte)0xde, (byte)0xad}, (byte [])incomingAttrs.get ("Opaque"))); Map addOutgoingAttribute = options.getParamOption ("Federation.Add-Outgoing-Attribute"); Map outgoingAttrs = (Map)addOutgoingAttribute.get ("External"); assertEquals ("hello world", outgoingAttrs.get ("String")); } private static String astExpr (String expr) throws ParseException { return unparse (parse (expr)); } @Test public void splitParams () throws Exception { Pair> result = OptionTypeParam.splitOptionParam ("Base[Param1][Param2]"); assertEquals ("Base", result.item1); assertEquals (list ("Param1", "Param2"), result.item2); result = OptionTypeParam.splitOptionParam ("Base[Param1]"); assertEquals ("Base", result.item1); assertEquals (list ("Param1"), result.item2); result = OptionTypeParam.splitOptionParam ("Base"); assertEquals ("Base", result.item1); assertEquals (0, result.item2.size ()); assertInvalidParam ("Base["); // todo // assertInvalidParam ("Base[[hello]"); assertInvalidParam ("Base[hello"); assertInvalidParam ("Base[hello["); assertInvalidParam ("Base[hello]["); assertInvalidParam ("Base[hello]]"); assertInvalidParam ("Base]"); } private static void assertInvalidParam (String expr) { try { OptionTypeParam.splitOptionParam (expr); fail (); } catch (IllegalConfigOptionException ex) { // ok } } } avis-1.2.2/server/src/test/org/avis/federation/JUTestFederation.java0000644000175000017500000005411511147555544025274 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.HashMap; import java.util.Map; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.InetSocketAddress; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.ThreadModel; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.avis.config.Options; import org.avis.federation.io.FederationFrameCodec; import org.avis.federation.io.messages.FedConnRqst; import org.avis.federation.io.messages.FedSubReplace; import org.avis.io.TestingIoHandler; import org.avis.io.messages.Nack; import org.avis.io.messages.NotifyDeliver; import org.avis.io.messages.NotifyEmit; import org.avis.io.messages.SecRqst; import org.avis.router.Router; import org.avis.router.SimpleClient; import org.avis.security.Key; import org.avis.security.Keys; import org.avis.subscription.ast.nodes.Const; import org.avis.util.LogFailTester; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import static java.lang.Thread.sleep; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.avis.federation.Federation.DEFAULT_EWAF_PORT; import static org.avis.federation.Federation.VERSION_MAJOR; import static org.avis.federation.Federation.VERSION_MINOR; import static org.avis.federation.TestUtils.waitForAsyncConnect; import static org.avis.federation.TestUtils.waitForConnect; import static org.avis.logging.Log.INFO; import static org.avis.logging.Log.enableLogging; import static org.avis.logging.Log.shouldLog; import static org.avis.security.DualKeyScheme.Subset.CONSUMER; import static org.avis.security.DualKeyScheme.Subset.PRODUCER; import static org.avis.security.KeyScheme.SHA1_DUAL; import static org.avis.security.KeyScheme.SHA1_PRODUCER; import static org.avis.security.Keys.EMPTY_KEYS; import static org.avis.util.Collections.set; public class JUTestFederation { static final int PORT1 = 29170; static final int PORT2 = 29180; private static final String MANTARA_ELVIN = "/usr/local/sbin/elvind"; private LogFailTester logTester; private StandardFederatorSetup federation; private boolean oldLogInfoState; @Before public void setup () { // enableLogging (TRACE, true); // enableLogging (DIAGNOSTIC, true); oldLogInfoState = shouldLog (INFO); enableLogging (INFO, false); logTester = new LogFailTester (); } @After public void tearDown () throws Exception { enableLogging (INFO, oldLogInfoState); if (federation != null) { federation.close (); federation = null; } logTester.assertOkAndDispose (); } @Test public void uri () throws Exception { EwafURI uri = new EwafURI ("ewaf://hostname"); assertEquals ("ewaf", uri.scheme); assertEquals ("hostname", uri.host); assertEquals (VERSION_MAJOR, uri.versionMajor); assertEquals (VERSION_MINOR, uri.versionMinor); assertEquals (DEFAULT_EWAF_PORT, uri.port); } @Test public void wildcards () throws Exception { FederationClasses classes = new FederationClasses (); FederationClass class1 = classes.define ("class1"); FederationClass class2 = classes.define ("class2"); classes.map ("*.elvin.org", class1); classes.map ("111.111.111.1??", class2); assertSame (class1, classes.classFor ("public.elvin.org")); assertSame (class2, classes.classFor ("111.111.111.123")); assertSame (class2, classes.classFor ("111.111.111.134")); assertSame (classes.defaultClass (), classes.classFor ("111.111.111.234")); } @Test public void basic () throws Exception { federation = new StandardFederatorSetup (); testTwoWayClientSendReceive (federation.client1, federation.client2); federation.close (); } /** * Check that case does not matter for hostnames. */ @Test public void hostnameCaseInsensitive () throws Exception { FederationClass fedClass = StandardFederatorSetup.defaultClass (); FederationClasses classes1 = new FederationClasses (); FederationClasses classes2 = new FederationClasses (); classes1.map ("LOCALhost", fedClass); classes2.map ("localHOSt", fedClass); federation = new StandardFederatorSetup (classes1, classes2); federation.close (); } /** * Test the acceptor rejects various invalid attempts to connect. */ @Test public void rejection () throws Exception { FederationClass fedClass2 = StandardFederatorSetup.defaultClass (); FederationClasses classes = new FederationClasses (); classes.map ("localhost", fedClass2); Router server1 = new Router (PORT1); EwafURI ewafURI = new EwafURI ("ewaf://localhost:" + (PORT1 + 1)); Options options = new Options (FederationOptionSet.OPTION_SET); Acceptor acceptor = new Acceptor (server1, "server1", classes, set (ewafURI), options); // connector TestingIoHandler listener = new TestingIoHandler (); IoSession connectSession = connectFederation (server1, ewafURI, listener); // incompatible version logTester.pause (); connectSession.write (new FedConnRqst (Federation.VERSION_MAJOR + 1, 0, "server2")); Nack nack = listener.waitForMessage (Nack.class); logTester.unpause (); assertEquals (Nack.PROT_INCOMPAT, nack.error); connectSession.close (); // bad server domain (same as acceptor's) listener = new TestingIoHandler (); connectSession = connectFederation (server1, ewafURI, listener); logTester.pause (); connectSession.write (new FedConnRqst (Federation.VERSION_MAJOR, Federation.VERSION_MINOR, "server1")); nack = listener.waitForMessage (Nack.class); logTester.unpause (); assertEquals (Acceptor.INVALID_DOMAIN, nack.error); connectSession.close (); // no federation class mapped classes.clear (); listener = new TestingIoHandler (); connectSession = connectFederation (server1, ewafURI, listener); logTester.pause (); connectSession.write (new FedConnRqst (Federation.VERSION_MAJOR, Federation.VERSION_MINOR, "bogus")); nack = listener.waitForMessage (Nack.class); logTester.unpause (); assertEquals (Acceptor.INVALID_DOMAIN, nack.error); connectSession.close (); classes.map ("localhost", fedClass2); // bogus handshake listener = new TestingIoHandler (); connectSession = connectFederation (server1, ewafURI, listener); logTester.pause (); connectSession.write (new FedSubReplace (Const.CONST_FALSE)); // acceptor should just abort connection listener.waitForClose (connectSession); connectSession.close (); acceptor.close (); server1.close (); } @Test public void addAttributes () throws Exception { FederationClass fedClass1 = StandardFederatorSetup.defaultClass (); fedClass1.incomingAttributes = new HashMap (); fedClass1.outgoingAttributes = new HashMap (); fedClass1.incomingAttributes.put ("Incoming", "incoming"); fedClass1.outgoingAttributes.put ("Outgoing", "outgoing"); FederationClasses classes1 = new FederationClasses (fedClass1); FederationClasses classes2 = new FederationClasses (StandardFederatorSetup.defaultClass ()); federation = new StandardFederatorSetup (classes1, classes2); // client 1 -> client 2 federation.client1.sendNotify (map ("federated", "server1", "from", "client1")); NotifyDeliver notification = (NotifyDeliver)federation.client2.receive (); assertEquals ("client1", notification.attributes.get ("from")); assertEquals ("outgoing", notification.attributes.get ("Outgoing")); assertNull (notification.attributes.get ("Incoming")); // client 2 - > client 1 federation.client2.sendNotify (map ("federated", "server2", "from", "client2")); notification = (NotifyDeliver)federation.client1.receive (); assertEquals ("client2", notification.attributes.get ("from")); assertEquals ("incoming", notification.attributes.get ("Incoming")); assertNull (notification.attributes.get ("Outgoing")); federation.close (); } /** * Test secure delivery. */ @Test public void security () throws Exception { federation = new StandardFederatorSetup (); Key client1Private = new Key ("client1 private"); Key client1Public = client1Private.publicKeyFor (SHA1_PRODUCER); Keys client1NtfnKeys = new Keys (); client1NtfnKeys.add (SHA1_PRODUCER, client1Private); Keys client2SubKeys = new Keys (); client2SubKeys.add (SHA1_PRODUCER, client1Public); federation.client1.sendAndReceive (new SecRqst (client1NtfnKeys, EMPTY_KEYS, EMPTY_KEYS, EMPTY_KEYS)); federation.client2.sendAndReceive (new SecRqst (EMPTY_KEYS, EMPTY_KEYS, client2SubKeys, EMPTY_KEYS)); // client 1 -> client 2 federation.client1.send (new NotifyEmit (map ("federated", "server1", "from", "client1"), false, EMPTY_KEYS)); NotifyDeliver notification = (NotifyDeliver)federation.client2.receive (); assertEquals (1, notification.secureMatches.length); assertEquals (0, notification.insecureMatches.length); assertEquals ("client1", notification.attributes.get ("from")); federation.close (); } /** * Test that connector will keep trying to connect on initial failure. */ @Test public void connectTimeout () throws Exception { // Log.enableLogging (Log.DIAGNOSTIC, true); Router server1 = new Router (PORT1); Router server2 = new Router (PORT2); FederationClass fedClass = new FederationClass ("require (federated)", "require (federated)"); FederationClasses classes = new FederationClasses (fedClass); EwafURI ewafURI = new EwafURI ("ewaf://localhost:" + (PORT1 + 1)); // set connect/request timeout to 1 second Options options = new Options (FederationOptionSet.OPTION_SET); options.set ("Federation.Request-Timeout", 1); Connector connector = new Connector (server1, "server1", ewafURI, fedClass, options); waitForAsyncConnect (connector); Acceptor acceptor = new Acceptor (server2, "server2", classes, set (ewafURI), options); waitForConnect (connector); connector.close (); acceptor.close (); server1.close (); server2.close (); } /** * Test that connector will reconnect on disconnect. */ @Test public void reconnect () throws Exception { Router server1 = new Router (PORT1); Router server2 = new Router (PORT2); FederationClass fedClass = new FederationClass ("require (federated)", "require (federated)"); FederationClasses classes = new FederationClasses (fedClass); EwafURI ewafURI = new EwafURI ("ewaf://localhost:" + (PORT1 + 1)); // set connect timeout to 1 second Options options = new Options (FederationOptionSet.OPTION_SET); options.set ("Federation.Request-Timeout", 1); // Log.enableLogging (Log.DIAGNOSTIC, true); // Log.enableLogging (Log.TRACE, true); Acceptor acceptor = new Acceptor (server2, "server2", classes, set (ewafURI), options); Connector connector = new Connector (server1, "server1", ewafURI, fedClass, options); waitForConnect (connector); acceptor.close (); waitForAsyncConnect (connector); acceptor = new Acceptor (server2, "server2", classes, set (ewafURI), options); waitForConnect (connector); connector.close (); acceptor.close (); server1.close (); server2.close (); } @Test public void liveness () throws Exception { Router server1 = new Router (PORT1); Router server2 = new Router (PORT2); FederationClass fedClass = new FederationClass ("require (federated)", "require (federated)"); FederationClasses classes = new FederationClasses (fedClass); EwafURI ewafURI = new EwafURI ("ewaf://localhost:" + (PORT1 + 1)); // set connect timeout to 1 second Options options = new Options (FederationOptionSet.OPTION_SET); options.set ("Federation.Request-Timeout", 1); options.set ("Federation.Keepalive-Interval", 1); // Log.enableLogging (Log.DIAGNOSTIC, true); // Log.enableLogging (Log.TRACE, true); Acceptor acceptor = new Acceptor (server2, "server2", classes, set (ewafURI), options); Connector connector = new Connector (server1, "server1", ewafURI, fedClass, options); waitForConnect (connector); // "crash" link at acceptor end acceptor.hang (); // liveness check will generate a warning: ignore logTester.pause (); waitForAsyncConnect (connector); logTester.unpause (); connector.close (); acceptor.close (); server1.close (); server2.close (); } private static boolean runElvind = true; /** * Test against Mantara elvind. */ @Test @Ignore public void mantara () throws Exception { // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); EwafURI ewafURI = new EwafURI ("ewaf:1.0//localhost:" + (PORT1 + 1)); // run elvind Process elvind = null; if (runElvind) elvind = runMantaraElvind (ewafURI, "require (federated)", "require (federated)"); // start Avis on PORT2 Router server = new Router (PORT2); Options options = new Options (FederationOptionSet.OPTION_SET); FederationClass fedClass = new FederationClass ("require (federated)", "require (federated)"); Connector connector = new Connector (server, "avis", ewafURI, fedClass, options); waitForConnect (connector); // connect two clients, one to elvind, one to Avis SimpleClient client1 = new SimpleClient ("client1", "localhost", PORT1); SimpleClient client2 = new SimpleClient ("client2", "localhost", PORT2); client1.connect (); client2.connect (); client1.subscribe ("require (federated) && from == 'client2'"); client2.subscribe ("require (federated) && from == 'client1'"); testTwoWayClientSendReceive (client1, client2); // secure messaging: set up dual key scheme for two-way comms Key client1Private = new Key ("client1 private"); Key client2Private = new Key ("client2 private"); Key client1Public = client1Private.publicKeyFor (SHA1_PRODUCER); Key client2Public = client2Private.publicKeyFor (SHA1_PRODUCER); Keys client1NtfnKeys = new Keys (); client1NtfnKeys.add (SHA1_DUAL, PRODUCER, client1Private); client1NtfnKeys.add (SHA1_DUAL, CONSUMER, client2Public); Keys client1SubKeys = new Keys (); client1SubKeys.add (SHA1_DUAL, PRODUCER, client2Public); client1SubKeys.add (SHA1_DUAL, CONSUMER, client1Private); Keys client2NtfnKeys = new Keys (); client2NtfnKeys.add (SHA1_DUAL, PRODUCER, client2Private); client2NtfnKeys.add (SHA1_DUAL, CONSUMER, client1Public); Keys client2SubKeys = new Keys (); client2SubKeys.add (SHA1_DUAL, PRODUCER, client1Public); client2SubKeys.add (SHA1_DUAL, CONSUMER, client2Private); client1.sendAndReceive (new SecRqst (client1NtfnKeys, EMPTY_KEYS, client1SubKeys, EMPTY_KEYS)); client2.sendAndReceive (new SecRqst (client2NtfnKeys, EMPTY_KEYS, client2SubKeys, EMPTY_KEYS)); testTwoWayClientSendReceive (client1, client2, true); client1.close (); client2.close (); connector.close (); server.close (); if (elvind != null) elvind.destroy (); } /** * Ad hoc test of AST IO against Mantara elvind. */ @Test @Ignore public void mantaraAST () throws Exception { // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); EwafURI ewafURI = new EwafURI ("ewaf:1.0//localhost:" + (PORT1 + 1)); Process elvind = null; String require = "require (federated) && size (from) > 2 && string (from) && " + "(equals (from, 'client1', 'client2') || " + " begins-with (from, 'c') || (fred + 1 > 42))"; if (runElvind) elvind = runMantaraElvind (ewafURI, require, "TRUE"); Router server = new Router (PORT2); Options options = new Options (FederationOptionSet.OPTION_SET); FederationClass fedClass = new FederationClass (require, "require (federated)"); Connector connector = new Connector (server, "avis", ewafURI, fedClass, options); waitForConnect (connector); SimpleClient client1 = new SimpleClient ("client1", "localhost", PORT1); SimpleClient client2 = new SimpleClient ("client2", "localhost", PORT2); client1.connect (); client2.connect (); client1.subscribe ("require (federated) && from == 'client2'"); client2.subscribe ("require (federated) && from == 'client1'"); testTwoWayClientSendReceive (client1, client2); client1.close (); client2.close (); connector.close (); server.close (); if (elvind != null) elvind.destroy (); } private static Process runMantaraElvind (EwafURI ewafURI, String require, String provide) throws Exception { File mantaraConfig = File.createTempFile ("elvin", "conf"); mantaraConfig.deleteOnExit (); String config = "protocol elvin:4.0/tcp,none,xdr/0.0.0.0:" + PORT1 + "\n" + "federation yes\n" + "federation.name mantara\n" + "federation.protocol " + ewafURI + "\n" + "federation.class test\n" + "federation.subscribe test " + require + "\n" + "federation.provide test " + provide + "\n"; Writer configStream = new OutputStreamWriter (new FileOutputStream (mantaraConfig)); configStream.append (config); configStream.close (); Process elvind = new ProcessBuilder ().command (MANTARA_ELVIN, "-l", "-f", mantaraConfig.getAbsolutePath ()).start (); sleep (2000); // give elvind time to start return elvind; } private static void testTwoWayClientSendReceive (SimpleClient client1, SimpleClient client2) throws Exception { testTwoWayClientSendReceive (client1, client2, false); } /** * Test client1 can see message from client2 and vice-versa. */ private static void testTwoWayClientSendReceive (SimpleClient client1, SimpleClient client2, boolean secure) throws Exception { // client 1 -> client 2 client1.sendNotify (map ("federated", "server1", "from", "client1")); NotifyDeliver notification = (NotifyDeliver)client2.receive (); if (secure) { assertEquals (1, notification.secureMatches.length); assertEquals (0, notification.insecureMatches.length); } else { assertEquals (0, notification.secureMatches.length); assertEquals (1, notification.insecureMatches.length); } assertEquals ("client1", notification.attributes.get ("from")); // client 2 - > client 1 client2.sendNotify (map ("federated", "server2", "from", "client2")); notification = (NotifyDeliver)client1.receive (); if (secure) { assertEquals (1, notification.secureMatches.length); assertEquals (0, notification.insecureMatches.length); } else { assertEquals (0, notification.secureMatches.length); assertEquals (1, notification.insecureMatches.length); } assertEquals ("client2", notification.attributes.get ("from")); } private static Map map (String... nameValues) { HashMap map = new HashMap (); for (int i = 0; i < nameValues.length; i += 2) map.put (nameValues [i], nameValues [i + 1]); return map; } /** * Create a connection to federation listener. */ private static IoSession connectFederation (Router router, EwafURI uri, IoHandler listener) { SocketConnector connector = new SocketConnector (1, router.executor ()); SocketConnectorConfig connectorConfig = new SocketConnectorConfig (); connector.setWorkerTimeout (0); connectorConfig.setThreadModel (ThreadModel.MANUAL); connectorConfig.setConnectTimeout (20); connectorConfig.getFilterChain ().addLast ("codec", FederationFrameCodec.FILTER); ConnectFuture future = connector.connect (new InetSocketAddress (uri.host, uri.port), listener, connectorConfig); future.join (); return future.getSession (); } } avis-1.2.2/server/src/test/org/avis/federation/JUTestFederationIntegration.java0000644000175000017500000002034011147555544027471 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.Collection; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import org.avis.io.messages.NotifyEmit; import org.avis.logging.Log; import org.avis.router.Main; import org.avis.router.Router; import org.avis.router.SimpleClient; import org.avis.util.IllegalConfigOptionException; import org.avis.util.LogFailTester; import org.junit.After; import org.junit.Before; import org.junit.Test; import static java.lang.Thread.sleep; import static org.avis.federation.FederationManager.federationManagerFor; import static org.avis.federation.TestUtils.MAX_WAIT; import static org.avis.federation.TestUtils.waitForConnect; import static org.avis.logging.Log.INFO; import static org.avis.logging.Log.enableLogging; import static org.avis.logging.Log.shouldLog; import static org.avis.util.Streams.close; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; /** * Full federation integration test between three routers. Starts each * router as if started from command line and federates in a V: * router1 -> router2 and router1 -> router3. * * @author Matthew Phillips */ public class JUTestFederationIntegration { private LogFailTester logTester; private boolean oldLogInfoState; @Before public void setup () { oldLogInfoState = shouldLog (INFO); enableLogging (INFO, false); logTester = new LogFailTester (); } @After public void tearDown () throws Exception { enableLogging (INFO, oldLogInfoState); logTester.assertOkAndDispose (); } @Test public void integration () throws Exception { File config1 = configFile ( "Listen=elvin://127.0.0.1:29170\n" + "Federation.Activated=yes\n" + "Federation.Connect[Test]=" + "ewaf://127.0.0.1:29181 ewaf://127.0.0.1:29191\n" + "Federation.Subscribe[Test]=require (test)\n" + "Federation.Provide[Test]=require (test)\n" + "Federation.Request-Timeout=2" ); File config2 = configFile ( "Listen=elvin://127.0.0.1:29180\n" + "Federation.Activated=yes\n" + "Federation.Listen=ewaf://127.0.0.1:29181\n" + "Federation.Subscribe[Test]=require (test)\n" + "Federation.Provide[Test]=require (test)\n" + "Federation.Apply-Class[Test]=@127.0.0.1" ); File config3 = configFile ( "Listen=elvin://127.0.0.1:29190\n" + "Federation.Activated=yes\n" + "Federation.Listen=ewaf://127.0.0.1:29191\n" + "Federation.Subscribe[Test]=require (test)\n" + "Federation.Provide[Test]=require (test)\n" + "Federation.Apply-Class[Test]=127.0.0.1" ); Log.enableLogging (Log.INFO, false); // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); Router router2 = startRouter (config2); Router router3 = startRouter (config3); Router router1 = startRouter (config1); for (Connector connector : federationManagerFor (router1).connectors ()) waitForConnect (connector); SimpleClient client1 = new SimpleClient ("127.0.0.1", 29180); SimpleClient client2 = new SimpleClient ("127.0.0.1", 29190); client1.connect (); client1.subscribe ("test == 1"); client2.connect (); client2.subscribe ("test == 2"); client1.send (new NotifyEmit ("test", 2)); client2.receive (); client2.send (new NotifyEmit ("test", 1)); client1.receive (); client1.close (); client2.close (); router1.close (); router2.close (); router3.close (); } /** * Full integration test including TLS in connector/acceptor. * * Router 1 connects -> Router 2 * -> Router 3 */ @Test public void integrationTLS () throws Exception { String commonOptions = "TLS.Keystore=" + getClass ().getResource ("router.ks") + "\n" + "TLS.Keystore-Passphrase=testing\n" + "Federation.Activated=yes\n" + "Federation.Subscribe[Test]=require (test)\n" + "Federation.Provide[Test]=require (test)\n" + "Federation.Apply-Class[Test]=127.0.0.1\n" + "Federation.Request-Timeout=2\n" + "Federation.Require-Authenticated=127.0.0.1\n"; File config1 = configFile ( "Listen=elvin:/secure/127.0.0.1:29170\n" + commonOptions + "Federation.Connect[Test]=" + "ewaf:/secure/127.0.0.1:29181 ewaf:/secure/127.0.0.1:29191\n" ); File config2 = configFile ( "Listen=elvin://127.0.0.1:29180\n" + commonOptions + "Federation.Listen=" + "ewaf:/secure/127.0.0.1:29181 " + "ewaf://127.0.0.1:29182\n" ); File config3 = configFile ( "Listen=elvin://127.0.0.1:29190\n" + commonOptions + "Federation.Listen=ewaf:/secure/127.0.0.1:29191\n" ); // config with untrusted cert File config4 = configFile ( "Listen=elvin:/secure/127.0.0.1:29200\n" + commonOptions + "TLS.Keystore=" + getClass ().getResource ("untrusted.ks") + "\n" + "Federation.Connect[Test]=ewaf:/secure/127.0.0.1:29181" ); // config with trusted cert but not using TLS File config5 = configFile ( "Listen=elvin:/secure/127.0.0.1:29300\n" + commonOptions + "Federation.Connect[Test]=ewaf://127.0.0.1:29182" ); // Log.enableLogging (Log.INFO, true); // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); Router router2 = startRouter (config2); Router router3 = startRouter (config3); Router router1 = startRouter (config1); Collection connectors = federationManagerFor (router1).connectors (); assertEquals (2, connectors.size ()); for (Connector connector : connectors) waitForConnect (connector); SimpleClient client1 = new SimpleClient ("127.0.0.1", 29180); SimpleClient client2 = new SimpleClient ("127.0.0.1", 29190); client1.connect (); client1.subscribe ("test == 1"); client2.connect (); client2.subscribe ("test == 2"); client1.send (new NotifyEmit ("test", 2)); client2.receive (); client2.send (new NotifyEmit ("test", 1)); client1.receive (); client1.close (); client2.close (); // check that federator with untrusted cert cannot connect logTester.pause (); Router router4 = startRouter (config4); assertFailsToConnect (router4); router4.close (); // check that federator cannot connect insecurely logTester.pause (); Router router5 = startRouter (config5); assertFailsToConnect (router5); router5.close (); logTester.unpause (); router3.close (); router2.close (); router1.close (); } private static void assertFailsToConnect (Router router) throws InterruptedException { sleep (MAX_WAIT); for (Connector connector : federationManagerFor (router).connectors ()) assertFalse (connector.isConnected ()); } private static Router startRouter (File config) throws IllegalConfigOptionException, IOException { return Main.start ("-c", config.getAbsolutePath ()); } private static File configFile (String contents) throws IOException { File configFile = File.createTempFile ("avis", ".conf"); configFile.deleteOnExit (); PrintWriter configStream = new PrintWriter (configFile); try { configStream.append (contents); } finally { close (configStream); } return configFile; } }avis-1.2.2/server/src/test/org/avis/federation/JUTestFederationManager.java0000644000175000017500000002055211147555544026565 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.HashMap; import java.util.Map; import java.net.InetAddress; import org.avis.config.Options; import org.avis.io.messages.NotifyDeliver; import org.avis.logging.Log; import org.avis.router.Router; import org.avis.router.SimpleClient; import org.avis.util.LogFailTester; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.avis.federation.TestUtils.waitForConnect; import static org.avis.io.Net.addressesFor; import static org.avis.logging.Log.INFO; import static org.avis.logging.Log.enableLogging; import static org.avis.util.Collections.set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class JUTestFederationManager { private static final int PORT1 = 29170; private static final int PORT2 = PORT1 + 10; private LogFailTester logTester; private boolean oldLogInfoState; @Before public void setup () { // Log.enableLogging (Log.TRACE, true); // Log.enableLogging (Log.DIAGNOSTIC, true); oldLogInfoState = Log.shouldLog (INFO); enableLogging (INFO, false); logTester = new LogFailTester (); } @After public void tearDown () { enableLogging (INFO, oldLogInfoState); logTester.assertOkAndDispose (); } @Test public void basicListen () throws Exception { InetAddress localHost = InetAddress.getLocalHost (); EwafURI federationUri = new EwafURI ("ewaf://127.0.0.1:" + (PORT1 - 1)); Options options = new Options (FederationOptionSet.OPTION_SET); options.set ("Federation.Router-Name", "router1"); options.set ("Federation.Listen", federationUri); options.set ("Federation.Apply-Class[Test]", localHost.getHostAddress () + " localhost bogus"); options.set ("Federation.Subscribe[Test]", "require (federated)"); options.set ("Federation.Provide[Test]", "require (federated)"); options.set ("Federation.Add-Incoming-Attribute[Test][Added-Incoming]", "'incoming'"); options.set ("Federation.Add-Outgoing-Attribute[Test][Added-Outgoing]", "'outgoing'"); Router router1 = new Router (PORT1); FederationManager manager = new FederationManager (router1, options); FederationClass testClass = manager.classes.classFor (localHost); assertFalse (testClass.allowsNothing ()); assertEquals (addressesFor (set (federationUri)), manager.acceptor.listenAddresses); assertEquals ("incoming", testClass.incomingAttributes.get ("Added-Incoming")); assertEquals ("outgoing", testClass.outgoingAttributes.get ("Added-Outgoing")); Router router2 = new Router (PORT2); FederationClass fedClass = new FederationClass ("require (federated)", "require (federated)"); Connector connector = new Connector (router2, "router2", federationUri, fedClass, new Options (FederationOptionSet.OPTION_SET)); waitForConnect (connector); assertEquals (1, manager.acceptor.links.size ()); SimpleClient client1 = new SimpleClient ("client1", "localhost", PORT1); SimpleClient client2 = new SimpleClient ("client2", "localhost", PORT2); client1.connect (); client2.connect (); client1.subscribe ("require (federated) && from == 'client2'"); client2.subscribe ("require (federated) && from == 'client1'"); client1.sendNotify (map ("federated", "router1", "from", "client1")); NotifyDeliver notification = (NotifyDeliver)client2.receive (); assertEquals (0, notification.secureMatches.length); assertEquals (1, notification.insecureMatches.length); assertEquals ("client1", notification.attributes.get ("from")); client1.close (); client2.close (); connector.close (); router2.close (); router1.close (); assertTrue (manager.isClosed ()); } @Test public void fullConnectAccept () throws Exception { EwafURI federationUri = new EwafURI ("ewaf://127.0.0.1:" + (PORT1 - 1)); // router 1 (acceptor) Options options1 = new Options (FederationOptionSet.OPTION_SET); options1.set ("Federation.Router-Name", "router1"); options1.set ("Federation.Listen", federationUri); options1.set ("Federation.Apply-Class[Test]", "localhost"); options1.set ("Federation.Subscribe[Test]", "require (federated)"); options1.set ("Federation.Provide[Test]", "require (federated)"); Router router1 = new Router (PORT1); FederationManager manager1 = new FederationManager (router1, options1); // router 2 (connector) Options options2 = new Options (FederationOptionSet.OPTION_SET); options2.set ("Federation.Router-Name", "router2"); options2.set ("Federation.Connect[Test]", federationUri); options2.set ("Federation.Subscribe[Test]", "require (federated)"); options2.set ("Federation.Provide[Test]", "require (federated)"); Router router2 = new Router (PORT2); FederationManager manager2 = new FederationManager (router2, options2); waitForConnect (manager2.connectors.get (0)); assertEquals (1, manager1.acceptor.links.size ()); SimpleClient client1 = new SimpleClient ("client1", "localhost", PORT1); SimpleClient client2 = new SimpleClient ("client2", "localhost", PORT2); client1.connect (); client2.connect (); client1.subscribe ("require (federated) && from == 'client2'"); client2.subscribe ("require (federated) && from == 'client1'"); client1.sendNotify (map ("federated", "router1", "from", "client1")); NotifyDeliver notification = (NotifyDeliver)client2.receive (); assertEquals (0, notification.secureMatches.length); assertEquals (1, notification.insecureMatches.length); assertEquals ("client1", notification.attributes.get ("from")); client1.close (); client2.close (); router2.close (); router1.close (); assertTrue (manager1.isClosed ()); assertTrue (manager2.isClosed ()); } @Test public void defaultApplyClass () throws Exception { EwafURI federationUri = new EwafURI ("ewaf://127.0.0.1:" + (PORT1 - 1)); // router 1 (acceptor) Options options1 = new Options (FederationOptionSet.OPTION_SET); options1.set ("Federation.Router-Name", "router1"); options1.set ("Federation.Listen", federationUri); options1.set ("Federation.Subscribe[Test]", "require (federated)"); options1.set ("Federation.Provide[Test]", "require (federated)"); options1.set ("Federation.Default-Class", "Test"); Router router1 = new Router (PORT1); FederationManager manager1 = new FederationManager (router1, options1); // router 2 (connector) Options options2 = new Options (FederationOptionSet.OPTION_SET); options2.set ("Federation.Router-Name", "router2"); options2.set ("Federation.Connect[Test]", federationUri); options2.set ("Federation.Subscribe[Test]", "require (federated)"); options2.set ("Federation.Provide[Test]", "require (federated)"); Router router2 = new Router (PORT2); FederationManager manager2 = new FederationManager (router2, options2); waitForConnect (manager2.connectors.get (0)); assertEquals (1, manager1.acceptor.links.size ()); router2.close (); router1.close (); } private static Map map (String... nameValues) { HashMap map = new HashMap (); for (int i = 0; i < nameValues.length; i += 2) map.put (nameValues [i], nameValues [i + 1]); return map; } } avis-1.2.2/server/src/test/org/avis/federation/StandardFederatorSetup.java0000644000175000017500000000632711147555544026534 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import org.avis.config.Options; import org.avis.router.Router; import org.avis.router.SimpleClient; import org.avis.subscription.parser.ParseException; import static java.net.InetAddress.getLocalHost; import static org.avis.federation.JUTestFederation.PORT1; import static org.avis.federation.JUTestFederation.PORT2; import static org.avis.federation.TestUtils.waitForConnect; import static org.avis.util.Collections.set; public class StandardFederatorSetup { public Router server1; public Router server2; public SimpleClient client1; public SimpleClient client2; public Connector connector; public Acceptor acceptor; public StandardFederatorSetup () throws Exception { this (new Options (FederationOptionSet.OPTION_SET)); } public StandardFederatorSetup (FederationClasses classes1, FederationClasses classes2) throws Exception { this (classes1, classes2, new Options (FederationOptionSet.OPTION_SET)); } public StandardFederatorSetup (Options options) throws Exception { this (new FederationClasses (defaultClass ()), new FederationClasses (defaultClass ()), options); } public static FederationClass defaultClass () throws ParseException { return new FederationClass ("require (federated)", "require (federated)"); } public StandardFederatorSetup (FederationClasses classes1, FederationClasses classes2, Options options) throws Exception { server1 = new Router (JUTestFederation.PORT1); server2 = new Router (JUTestFederation.PORT2); EwafURI ewafURI = new EwafURI ("ewaf://localhost:" + (PORT1 + 1)); acceptor = new Acceptor (server2, "server2", classes2, set (ewafURI), options); connector = new Connector (server1, "server1", ewafURI, classes1.classFor (getLocalHost ()), options); waitForConnect (connector); client1 = new SimpleClient ("client1", "localhost", PORT1); client2 = new SimpleClient ("client2", "localhost", PORT2); client1.connect (); client2.connect (); client1.subscribe ("require (federated) && from == 'client2'"); client2.subscribe ("require (federated) && from == 'client1'"); } public void close () throws Exception { client1.close (); client2.close (); connector.close (); acceptor.close (); server1.close (); server2.close (); } }avis-1.2.2/server/src/test/org/avis/router/0000755000175000017500000000000011147555544020444 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/router/JUTestRouter.java0000644000175000017500000006007111147555544023672 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.io.Closeable; import org.avis.io.messages.ConfConn; import org.avis.io.messages.ConnRply; import org.avis.io.messages.ConnRqst; import org.avis.io.messages.Disconn; import org.avis.io.messages.DisconnRply; import org.avis.io.messages.DisconnRqst; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.NotifyDeliver; import org.avis.io.messages.NotifyEmit; import org.avis.io.messages.SecRqst; import org.avis.io.messages.SubAddRqst; import org.avis.io.messages.SubDelRqst; import org.avis.io.messages.SubModRqst; import org.avis.io.messages.SubRply; import org.avis.io.messages.TestConn; import org.avis.io.messages.UNotify; import org.avis.security.Key; import org.avis.security.KeyScheme; import org.avis.security.Keys; import org.avis.util.LogFailTester; import org.junit.After; import org.junit.Before; import org.junit.Test; import static java.lang.Thread.sleep; import static org.avis.io.messages.Nack.EXP_IS_TRIVIAL; import static org.avis.io.messages.Nack.PARSE_ERROR; import static org.avis.logging.Log.ALARM; import static org.avis.logging.Log.WARNING; import static org.avis.logging.Log.alarm; import static org.avis.logging.Log.enableLogging; import static org.avis.router.ConnectionOptionSet.CONNECTION_OPTION_SET; import static org.avis.security.KeyScheme.SHA1_PRODUCER; import static org.avis.security.Keys.EMPTY_KEYS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Test the router server. * * @author Matthew Phillips */ public class JUTestRouter { static final int PORT = 29170; private Router router; private Random random; private LogFailTester logTester; private ArrayList autoClose; @Before public void setup () { enableLogging (WARNING, true); enableLogging (ALARM, true); random = new Random (); logTester = new LogFailTester (); autoClose = new ArrayList (); } @After public void tearDown () { if (router != null) router.close (); for (int i = autoClose.size () - 1; i >= 0; i--) { try { autoClose.get (i).close (); } catch (Throwable ex) { alarm ("Failed to close", this, ex); } } logTester.assertOkAndDispose (); } @Test public void connect () throws Exception { router = new Router (PORT); SimpleClient client = new SimpleClient (); ConnRqst connRqst = new ConnRqst (4, 0); client.send (connRqst); ConnRply reply = (ConnRply)client.receive (); assertEquals (connRqst.xid, reply.xid); DisconnRqst disconnRqst = new DisconnRqst (); client.send (disconnRqst); DisconnRply disconnRply = (DisconnRply)client.receive (); assertEquals (disconnRqst.xid, disconnRply.xid); client.close (); router.close (); } /** * Test connection options. * * @see JUTestClientConnectionOptions */ @Test public void connectionOptions () throws Exception { router = new Router (PORT); SimpleClient client = new SimpleClient ("localhost", PORT); HashMap options = new HashMap (); options.put ("Packet.Max-Length", 1024); options.put ("Subscription.Max-Count", 16); options.put ("Subscription.Max-Length", 1024); options.put ("Attribute.Opaque.Max-Length", 2048 * 1024); options.put ("TCP.Send-Immediately", 1); options.put ("Bogus", "not valid"); ConnRply connReply = client.connect (options); // todo: when Attribute.Opaque.Max-Length supported, switch lines below // assertEquals (2048 * 1024, reply.options.get ("Attribute.Opaque.Max-Length")); assertEquals (Integer.MAX_VALUE, connReply.options.get ("Attribute.Opaque.Max-Length")); assertEquals (1, connReply.options.get ("TCP.Send-Immediately")); assertEquals (1024, connReply.options.get ("Packet.Max-Length")); assertEquals (16, connReply.options.get ("Subscription.Max-Count")); assertEquals (1024, connReply.options.get ("Subscription.Max-Length")); assertNull (connReply.options.get ("Bogus")); // try to send a frame bigger than 1K, check server rejects SecRqst secRqst = new SecRqst (); secRqst.addNtfnKeys = new Keys (); secRqst.addNtfnKeys.add (SHA1_PRODUCER, new Key (new byte [1025])); logTester.pause (); client.send (secRqst); Message reply = client.receive (); assertTrue (reply instanceof Disconn); // check the disconnect message advises looking at Packet.Max-Length assertTrue (((Disconn)reply).args.contains ("Packet.Max-Length")); client.closeImmediately (); router.close (); logTester.unpause (); router = new Router (PORT); // remove packet length restriction for following tests options.remove ("Packet.Max-Length"); // test Subscription.Max-Count enforcement client = new SimpleClient ("localhost", PORT); client.connect (options); for (int i = 0; i < 16; i++) client.subscribe ("Count == " + i); SubAddRqst subAddRqst = new SubAddRqst ("Invalid == 1"); client.send (subAddRqst); Nack nack = (Nack)client.receive (); assertEquals (subAddRqst.xid, nack.xid); client.close (); // test Subscription.Max-Length enforcement client = new SimpleClient ("localhost", PORT); client.connect (options); subAddRqst = new SubAddRqst (dummySubscription (2048)); client.send (subAddRqst); nack = (Nack)client.receive (); assertEquals (subAddRqst.xid, nack.xid); client.close (); // test Subscription.Max-Keys and Connection.Max-Keys enforcement int maxConnKeys = CONNECTION_OPTION_SET.getMaxValue ("Connection.Max-Keys"); int maxSubKeys = CONNECTION_OPTION_SET.getMaxValue ("Subscription.Max-Keys"); options = new HashMap (); options.put ("Connection.Max-Keys", maxConnKeys); options.put ("Subscription.Max-Keys", maxSubKeys); client = new SimpleClient ("localhost", PORT); client.connect (options); Keys keys = new Keys (); for (int i = 0; i < maxConnKeys + 1; i++) keys.add (KeyScheme.SHA1_CONSUMER, new Key (randomBytes (128))); secRqst = new SecRqst (keys, EMPTY_KEYS, EMPTY_KEYS, EMPTY_KEYS); client.send (secRqst); nack = (Nack)client.receive (); assertEquals (secRqst.xid, nack.xid); subAddRqst = new SubAddRqst ("n == 1"); subAddRqst.keys = keys; client.send (subAddRqst); nack = (Nack)client.receive (); assertEquals (subAddRqst.xid, nack.xid); client.close (); router.close (); } /** * Test connection options specified in router config. */ @Test public void routerOptions () throws Exception { RouterOptions options = new RouterOptions (PORT); options.set ("Packet.Max-Length", 1024); options.set ("Subscription.Max-Count", 16); options.set ("Subscription.Max-Length", 1024); //options.set ("Attribute.Opaque.Max-Length", 2048 * 1024); router = new Router (options); SimpleClient client = new SimpleClient ("localhost", PORT); client.connect (); // try to send a frame bigger than 1K, check server rejects SecRqst secRqst = new SecRqst (); secRqst.addNtfnKeys = new Keys (); secRqst.addNtfnKeys.add (SHA1_PRODUCER, new Key (new byte [1025])); logTester.pause (); client.send (secRqst); Message reply = client.receive (); router.close (); logTester.unpause (); assertTrue ("Expected a Disconn", reply instanceof Disconn); client.closeImmediately (); } /** * Use the simple client to run through a connect, subscribe, emit, * change sub, disconnect sequence. */ @Test public void subscribe () throws Exception { router = new Router (PORT); SimpleClient client = new SimpleClient (); client.connect (); SubAddRqst subAddRqst = new SubAddRqst ("number == 1"); client.send (subAddRqst); SubRply subReply = (SubRply)client.receive (); assertEquals (subAddRqst.xid, subReply.xid); // check NACK on bad subscription subAddRqst = new SubAddRqst ("(1 + 1"); client.send (subAddRqst); Nack nackReply = (Nack)client.receive (); assertEquals (PARSE_ERROR, nackReply.error); subAddRqst = new SubAddRqst ("1 == 1"); client.send (subAddRqst); nackReply = (Nack)client.receive (); assertEquals (EXP_IS_TRIVIAL, nackReply.error); // send notification Map ntfn = new HashMap (); ntfn.put ("name", "foobar"); ntfn.put ("number", 1); client.send (new NotifyEmit (ntfn)); NotifyDeliver notifyDeliver = (NotifyDeliver)client.receive (); assertEquals (0, notifyDeliver.secureMatches.length); assertEquals (1, notifyDeliver.insecureMatches.length); assertEquals (subReply.subscriptionId, notifyDeliver.insecureMatches [0]); assertEquals ("foobar", notifyDeliver.attributes.get ("name")); assertEquals (1, notifyDeliver.attributes.get ("number")); // send non-matching ntfn ntfn = new HashMap (); ntfn.put ("name", "foobar"); ntfn.put ("number", 2); // should get no reply to next: following tests will fail if not client.send (new NotifyEmit (ntfn)); // modify subscription SubModRqst subModRqst = new SubModRqst (subReply.subscriptionId, "number == 2", true); client.send (subModRqst); subReply = (SubRply)client.receive (); assertEquals (subReply.xid, subModRqst.xid); assertEquals (subReply.subscriptionId, subModRqst.subscriptionId); // remove subscription SubDelRqst delRqst = new SubDelRqst (subReply.subscriptionId); client.send (delRqst); subReply = (SubRply)client.receive (); assertEquals (subReply.subscriptionId, delRqst.subscriptionId); // check NACK on remove invalid subscription delRqst = new SubDelRqst (subReply.subscriptionId); client.send (delRqst); nackReply = (Nack)client.receive (); assertEquals (Nack.NO_SUCH_SUB, nackReply.error); // send a connection test client.send (TestConn.INSTANCE); assertTrue (client.receive () instanceof ConfConn); client.close (); router.close (); } /** * Test multiple clients sending messages between each other. */ @Test public void multiClient () throws Exception { router = new Router (PORT); // client 1 SimpleClient client1 = new SimpleClient (); client1.connect (); SubAddRqst subAddRqst1 = new SubAddRqst ("client == 1 || all == 1"); client1.send (subAddRqst1); SubRply subReply1 = (SubRply)client1.receive (); assertEquals (subAddRqst1.xid, subReply1.xid); // client 2 SimpleClient client2 = new SimpleClient (); client2.connect (); SubAddRqst subAddRqst2 = new SubAddRqst ("client == 2 || all == 1"); client2.send (subAddRqst2); SubRply subReply2 = (SubRply)client2.receive (); assertEquals (subAddRqst2.xid, subReply2.xid); // client 1 send message to client 2 Map ntfn = new HashMap (); ntfn.put ("client", 2); ntfn.put ("payload", "hello from client 1"); client1.send (new NotifyEmit (ntfn)); NotifyDeliver client2Notify = (NotifyDeliver)client2.receive (); assertEquals ("hello from client 1", client2Notify.attributes.get ("payload")); // client 2 send message to client 1 ntfn = new HashMap (); ntfn.put ("client", 1); ntfn.put ("payload", "hello from client 2"); client2.send (new NotifyEmit (ntfn)); NotifyDeliver client1Notify = (NotifyDeliver)client1.receive (); assertEquals ("hello from client 2", client1Notify.attributes.get ("payload")); // client 1 sends message to all ntfn = new HashMap (); ntfn.put ("all", 1); ntfn.put ("payload", "hello all"); client1.send (new NotifyEmit (ntfn)); client2Notify = (NotifyDeliver)client2.receive (); assertEquals ("hello all", client2Notify.attributes.get ("payload")); client1Notify = (NotifyDeliver)client1.receive (); assertEquals ("hello all", client1Notify.attributes.get ("payload")); client1.close (); client2.close (); router.close (); } /** * Test secure messaging using the producer key scheme. Other * schemes should really be tested, but the key matching logic for * all the schemes supported in the server is done by the security * tests, so not bothering for now. */ @Test public void security () throws Exception { router = new Router (PORT); // SimpleClient alice = new SimpleClient ("localhost", 2917); // SimpleClient bob = new SimpleClient ("localhost", 2917); // SimpleClient eve = new SimpleClient ("localhost", 2917); SimpleClient alice = new SimpleClient ("alice"); SimpleClient bob = new SimpleClient ("bob"); SimpleClient eve = new SimpleClient ("eve"); alice.connect (); bob.connect (); eve.connect (); Key alicePrivate = new Key ("alice private"); Key alicePublic = alicePrivate.publicKeyFor (SHA1_PRODUCER); Keys aliceNtfnKeys = new Keys (); aliceNtfnKeys.add (SHA1_PRODUCER, alicePrivate); Keys bobSubKeys = new Keys (); bobSubKeys.add (SHA1_PRODUCER, alicePublic); Keys eveSubKeys = new Keys (); eveSubKeys.add (SHA1_PRODUCER, new Key ("Not alice's key").publicKeyFor (SHA1_PRODUCER)); bob.subscribe ("require (From-Alice)", bobSubKeys); eve.subscribe ("require (From-Alice)", eveSubKeys); checkAliceBobEve (alice, bob, eve, aliceNtfnKeys); alice.close (); bob.close (); eve.close (); } /** * Test that global keys set via SecModify work. */ @Test public void securitySecModify () throws Exception { router = new Router (PORT); SimpleClient alice = new SimpleClient ("alice"); SimpleClient bob = new SimpleClient ("bob"); SimpleClient eve = new SimpleClient ("eve"); alice.connect (); bob.connect (); eve.connect (); Key alicePrivate = new Key ("alice private"); Key alicePublic = alicePrivate.publicKeyFor (SHA1_PRODUCER); Keys aliceNtfnKeys = new Keys (); aliceNtfnKeys.add (SHA1_PRODUCER, alicePrivate); Keys bobSubKeys = new Keys (); bobSubKeys.add (SHA1_PRODUCER, alicePublic); Keys eveSubKeys = new Keys (); eveSubKeys.add (SHA1_PRODUCER, new Key ("Not alice's key").publicKeyFor (SHA1_PRODUCER)); bob.subscribe ("require (From-Alice)"); eve.subscribe ("require (From-Alice)"); alice.sendAndReceive (new SecRqst (aliceNtfnKeys, EMPTY_KEYS, EMPTY_KEYS, EMPTY_KEYS)); bob.sendAndReceive (new SecRqst (EMPTY_KEYS, EMPTY_KEYS, bobSubKeys, EMPTY_KEYS)); eve.sendAndReceive (new SecRqst (EMPTY_KEYS, EMPTY_KEYS, eveSubKeys, EMPTY_KEYS)); checkAliceBobEve (alice, bob, eve, EMPTY_KEYS); alice.close (); bob.close (); eve.close (); } /** * Check that alice and bob receive securely, eve doesn't. */ private static void checkAliceBobEve (SimpleClient alice, SimpleClient bob, SimpleClient eve, Keys ntfnKeys) throws Exception { Map ntfn = new HashMap (); ntfn.put ("From-Alice", 1); alice.sendNotify (ntfn, ntfnKeys); NotifyDeliver bobNtfn = (NotifyDeliver)bob.receive (); assertEquals (1, bobNtfn.secureMatches.length); assertEquals (0, bobNtfn.insecureMatches.length); assertEquals (1, bobNtfn.attributes.get ("From-Alice")); try { NotifyDeliver eveNtfn = (NotifyDeliver)eve.receive (2000); assertEquals (1, eveNtfn.attributes.get ("From-Alice")); fail ("Eve foiled our super secret scheme"); } catch (MessageTimeoutException ex) { // ok } } /** * Test changing subscription security settings on the fly. */ @Test public void securitySubModify () throws Exception { router = new Router (PORT); SimpleClient alice = new SimpleClient ("alice"); SimpleClient bob = new SimpleClient ("bob"); alice.connect (); bob.connect (); Key alicePrivate = new Key ("alice private"); Key alicePublic = alicePrivate.publicKeyFor (SHA1_PRODUCER); Keys aliceNtfnKeys = new Keys (); aliceNtfnKeys.add (SHA1_PRODUCER, alicePrivate); Keys bobSubKeys = new Keys (); bobSubKeys.add (SHA1_PRODUCER, alicePublic); SubAddRqst subAddRqst = new SubAddRqst ("require (From-Alice)", bobSubKeys, true); bob.send (subAddRqst); SubRply subRply = bob.receive (SubRply.class); Map ntfn = new HashMap (); ntfn.put ("From-Alice", 1); // send secure alice.sendNotify (ntfn, aliceNtfnKeys); NotifyDeliver bobNtfn = (NotifyDeliver)bob.receive (); assertEquals (1, bobNtfn.secureMatches.length); assertEquals (0, bobNtfn.insecureMatches.length); assertEquals (subRply.subscriptionId, bobNtfn.secureMatches [0]); // send insecure alice.sendNotify (ntfn); bobNtfn = (NotifyDeliver)bob.receive (); assertEquals (0, bobNtfn.secureMatches.length); assertEquals (1, bobNtfn.insecureMatches.length); assertEquals (subRply.subscriptionId, bobNtfn.insecureMatches [0]); // change bob to require secure SubModRqst subModRqst = new SubModRqst (subRply.subscriptionId, "", false); bob.send (subModRqst); bob.receive (SubRply.class); // send insecure again, bob should not get it alice.sendNotify (ntfn); try { bobNtfn = (NotifyDeliver)bob.receive (2000); fail ("Server delivered message insecurely"); } catch (MessageTimeoutException ex) { // ok } // change bob's keys so they do not match subModRqst = new SubModRqst (subRply.subscriptionId, "", false); subModRqst.delKeys = new Keys (); subModRqst.delKeys.add (SHA1_PRODUCER, alicePublic); bob.send (subModRqst); bob.receive (SubRply.class); // send secure again, bob should not get it alice.sendNotify (ntfn, aliceNtfnKeys); try { bobNtfn = (NotifyDeliver)bob.receive (2000); fail ("Server delivered message insecurely"); } catch (MessageTimeoutException ex) { // ok } alice.close (); bob.close (); } @Test public void unotify () throws Exception { router = new Router (PORT); SimpleClient client1 = new SimpleClient ("client1"); SimpleClient client2 = new SimpleClient ("client2"); client2.connect (); client2.subscribe ("number == 1"); Map ntfn = new HashMap (); ntfn.put ("number", 1); ntfn.put ("client", "client 1"); client1.send (new UNotify (4, 0, ntfn)); // todo MINA close can eat messages in queue: fix this sleep (1000); client1.close (); NotifyDeliver reply = (NotifyDeliver)client2.receive (); assertEquals ("client 1", reply.attributes.get ("client")); client2.close (); } /** * Test handling of client that does Bad Things. */ @Test public void badClient () throws Exception { // this test generates warnings by design: turn off checking logTester.assertOkAndDispose (); enableLogging (WARNING, false); router = new Router (PORT); SimpleClient client = new SimpleClient (); SimpleClient badClient = new SimpleClient (); client.connect (); client.subscribe ("number == 1"); // try to send a notification with no ConnRqst Map ntfn = new HashMap (); ntfn.put ("name", "foobar"); ntfn.put ("number", 1); badClient.send (new NotifyEmit (ntfn)); try { client.receive (2000); fail ("Server allowed client with no connection to notify"); } catch (MessageTimeoutException ex) { // ok } // try change security with no connection badClient.close (); badClient = new SimpleClient (); SecRqst secRqst = new SecRqst (); badClient.send (secRqst); Message reply = badClient.receive (); assertTrue (reply instanceof Disconn); badClient.close (); // try subscription with no connection badClient = new SimpleClient (); SubAddRqst subAddRqst = new SubAddRqst ("require (hello)", EMPTY_KEYS, true); badClient.send (subAddRqst); reply = badClient.receive (); assertTrue (reply instanceof Disconn); badClient.close (); // try to connect twice badClient = new SimpleClient (); badClient.connect (); ConnRqst connRqst = new ConnRqst (4, 0); badClient.send (connRqst); reply = badClient.receive (); assertTrue (reply instanceof Disconn); // server will have disconnected us for being Bad, so just kill socket badClient.closeImmediately (); // modify non-existent sub badClient = new SimpleClient (); badClient.connect (); SubModRqst subModRqst = new SubModRqst (123456, "", true); badClient.send (subModRqst); reply = badClient.receive (); assertTrue (reply instanceof Nack); assertEquals (subModRqst.xid, ((Nack)reply).xid); badClient.close (); client.close (); } /** * Test events are delivered in the order they're sent. */ @Test public void inorder () throws Exception { router = new Router (PORT); final SimpleClient client1 = new SimpleClient ("client1"); final SimpleClient client2 = new SimpleClient ("client2"); autoClose.add (client1); autoClose.add (client2); client1.connect (); client2.connect (); client2.subscribe ("int32 (serial)"); Thread notifyThread = new Thread () { @Override public void run () { Map ntfn = new HashMap (); try { for (int serial = 0; serial < 1000 && !isInterrupted (); serial++) { ntfn.put ("serial", serial); client1.sendNotify (ntfn); } } catch (Exception ex) { ex.printStackTrace (); } } }; notifyThread.start (); for (int i = 0; i < 1000 ; i++) { NotifyDeliver ntfn = (NotifyDeliver)client2.receive (); int serial = (Integer)ntfn.attributes.get ("serial"); if (serial != i) { notifyThread.interrupt (); notifyThread.join (10000); fail ("Events not in order: " + "serial was " + serial + ", should have been " + i); } } } private static String dummySubscription (int length) { StringBuilder str = new StringBuilder ("i == -1"); for (int i = 0; str.length () + 15 < length; i++) str.append (" && i == " + i); return str.toString (); } private byte [] randomBytes (int length) { byte [] data = new byte [length]; random.nextBytes (data); return data; } } avis-1.2.2/server/src/test/org/avis/router/JUTestFlooding.java0000644000175000017500000001127511147555544024155 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.ArrayList; import java.util.List; import java.io.IOException; import org.apache.mina.common.ByteBuffer; import org.avis.router.Router; import org.avis.util.LogFailTester; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import static org.avis.logging.Log.DIAGNOSTIC; import static org.avis.logging.Log.TRACE; import static org.avis.logging.Log.info; import static org.avis.logging.Log.enableLogging; import static org.avis.logging.Log.warn; import static java.lang.Thread.sleep; import static org.avis.router.JUTestRouter.PORT; /** * Tests for the server's robustness to clients spamming with large * numbers of big messages. By default a local server is started, but * the tests work best with serevr started in a separate VM. Set * USE_EXTERNAL_SERVER to true if you want to do this and run a server * on port 29170. */ public class JUTestFlooding { private static final boolean USE_EXTERNAL_SERVER = false; /** Time in millis to run flood tests. */ private static final long FLOODING_TIME = 5 * 1000; private Router server; private LogFailTester logTester; @Before public void setup () throws IOException { if (!USE_EXTERNAL_SERVER) server = new Router (PORT); logTester = new LogFailTester (); ByteBuffer.setUseDirectBuffers (false); } @After public void shutdown () { if (server != null) server.close (); logTester.assertOkAndDispose (); } /** * A "bad" client sends a continuous flood of large messages while * three others try to exchange messages. This test doesn't actually * assert anything, but simply tests whether server can keep serving * while being flooded. */ @Test @Ignore public void floodingFairness () throws Exception { enableLogging (TRACE, false); enableLogging (DIAGNOSTIC, false); MaliciousClient badClient = new MaliciousClient ("Bad client", "localhost", PORT); GoodClient goodClient1 = new GoodClient ("Good client 1", "localhost", PORT); GoodClient goodClient2 = new GoodClient ("Good client 2", "localhost", PORT); GoodClient goodClient3 = new GoodClient ("Good client 3", "localhost", PORT); badClient.startFlooding (); goodClient1.startSending (); goodClient2.startSending (); goodClient3.startSending (); info ("Waiting while clients do their thing...", this); sleep (FLOODING_TIME); badClient.stopFlooding (); goodClient1.stopSending (); goodClient2.stopSending (); goodClient3.stopSending (); try { badClient.close (20000); } catch (MessageTimeoutException ex) { warn ("Bad client close () failed: " + ex.getMessage (), this); } goodClient1.close (10000); goodClient2.close (10000); goodClient3.close (10000); info (badClient.report (), this); info (goodClient1.report (), this); info (goodClient2.report (), this); info (goodClient3.report (), this); } /** * Try to blow server's heap by setting up a number of "bad" clients * all spamming large messages at server. */ @Test @Ignore public void floodingHeap () throws Exception { enableLogging (TRACE, false); enableLogging (DIAGNOSTIC, false); List badClients = new ArrayList (); for (int i = 0; i < 4; i++) badClients.add (new MaliciousClient ("Bad client " + i, "localhost", PORT)); for (MaliciousClient client : badClients) client.startFlooding (); info ("Waiting while clients do their thing...", this); sleep (FLOODING_TIME); for (MaliciousClient client : badClients) client.stopFlooding (); for (MaliciousClient client : badClients) client.report (); info ("Closing clients...", this); // close () can take a long time when queues backed up... for (MaliciousClient client : badClients) client.close (60000); info ("Done", this); } }avis-1.2.2/server/src/test/org/avis/router/JUTestAttack.java0000644000175000017500000001003011147555544023607 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.io.IOException; import org.avis.io.messages.SubAddRqst; import org.avis.router.Router; import org.avis.security.Key; import org.avis.security.Keys; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import static org.avis.logging.Log.DIAGNOSTIC; import static org.avis.logging.Log.TRACE; import static org.avis.logging.Log.info; import static org.avis.logging.Log.enableLogging; import static org.avis.router.ConnectionOptionSet.CONNECTION_OPTION_SET; import static org.avis.router.JUTestRouter.PORT; import static org.avis.security.KeyScheme.SHA1_CONSUMER; /** * Test attacks deliberately designed to DOS/crash the server by * exhausting resources. * * @author Matthew Phillips */ public class JUTestAttack { private Random random; private Router server; @Before public void setup () throws IOException { enableLogging (TRACE, false); enableLogging (DIAGNOSTIC, false); random = new Random (); server = new Router (PORT); } @After public void teardown () { if (server != null) server.close (); } /** * Attack the server's heap space by adding the default max * keys/subs. Will pass with -Xmx120M setting. */ @Test @Ignore public void attackKeys () throws Exception { int numKeys = CONNECTION_OPTION_SET.defaults.getInt ("Subscription.Max-Keys"); int numSubs = CONNECTION_OPTION_SET.defaults.getInt ("Subscription.Max-Count"); SimpleClient client = new SimpleClient ("localhost", PORT); client.connect (); info ("Sending keys...", this); for (int i = 0; i < numSubs; i++) { Keys keys = new Keys (); for (int j = 0; j < numKeys; j++) keys.add (SHA1_CONSUMER, new Key (randomBytes (128))); SubAddRqst subAddRqst = new SubAddRqst ("number == " + i, keys, true); client.send (subAddRqst); client.receive (60 * 1000); } client.close (); } /** * Attack server by sending the max number of subs with very long * expressions. Takes about 2 mins to kill server on Powerbook G4. * Adding -Xmx120M allows it to pass. */ @Test @Ignore public void attackSubscriptions () throws Exception { int maxSubs = CONNECTION_OPTION_SET.getMaxValue ("Subscription.Max-Count"); int maxLength = CONNECTION_OPTION_SET.getMaxValue ("Subscription.Max-Length"); SimpleClient client = new SimpleClient ("localhost", PORT); Map options = new HashMap (); options.put ("Subscription.Max-Count", maxSubs); options.put ("Subscription.Max-Length", maxLength); client.connect (options); info ("Subscribing...", this); String subscriptionExpr = dummySubscription (maxLength); for (int i = 0; i < maxSubs; i++) client.subscribe (subscriptionExpr); client.close (); } private static String dummySubscription (int length) { StringBuilder str = new StringBuilder ("i == -1"); for (int i = 0; str.length () + 15 < length; i++) str.append (" && i == " + i); return str.toString (); } private byte [] randomBytes (int length) { byte [] data = new byte [length]; random.nextBytes (data); return data; } } avis-1.2.2/server/src/test/org/avis/router/GoodClient.java0000644000175000017500000000326611147555544023345 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimerTask; import static org.avis.logging.Log.alarm; public class GoodClient extends AgentClient { public GoodClient (String name, String host, int port) throws Exception { super (name, host, port); subscribe ("Channel == 'goodclient'"); } public void startSending () { timer.schedule (new SendTask (), 0, 20); } public void stopSending () { timer.cancel (); } class SendTask extends TimerTask { @Override public void run () { try { Map ntfn = new HashMap (); ntfn.put ("From", getClass ().getName ()); ntfn.put ("Time", new Date ().toString ()); ntfn.put ("Channel", "goodclient"); sendNotify (ntfn); sentMessageCount++; } catch (Exception ex) { alarm ("Failed to send notification", this, ex); } } } } avis-1.2.2/server/src/test/org/avis/router/JUTestRouterTLS.java0000644000175000017500000001051611147555544024254 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.net.InetSocketAddress; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.mina.common.ExceptionMonitor; import org.apache.mina.common.ThreadModel; import org.apache.mina.filter.SSLFilter; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.junit.Before; import org.junit.Test; import org.avis.io.ClientFrameCodec; import org.avis.io.ExceptionMonitorLogger; public class JUTestRouterTLS { private static final int DEFAULT_PORT = 29170; private static final int SECURE_PORT = 29171; @Before public void setup () { // route MINA exceptions to log ExceptionMonitor.setInstance (ExceptionMonitorLogger.INSTANCE); } /* * Command to generate the test key store: * keytool -genkey -alias test -keysize 512 -validity 3650 -keyalg RSA \ * -dname "CN=test.com, OU=test, O=Test Inc, L=Adelaide, \ * S=South Australia, C=AU" -keypass testing -storepass testing \ * -keystore tls_test.ks */ @Test public void connect () throws Exception { ExceptionMonitor.setInstance (ExceptionMonitorLogger.INSTANCE); RouterOptions options = new RouterOptions (); URI keystore = getClass ().getResource ("tls_test.ks").toURI (); options.set ("Listen", "elvin://127.0.0.1:" + DEFAULT_PORT + " " + "elvin:/secure/127.0.0.1:" + SECURE_PORT); options.set ("TLS.Keystore", keystore); options.set ("TLS.Keystore-Passphrase", "testing"); Router router = new Router (options); SimpleClient standardClient = new SimpleClient (new InetSocketAddress ("127.0.0.1", DEFAULT_PORT), createStandardConfig ()); standardClient.connect (); standardClient.close (); SimpleClient secureClient = new SimpleClient (new InetSocketAddress ("127.0.0.1", SECURE_PORT), createTLSConfig ()); secureClient.connect (); secureClient.close (); router.close (); } private static SocketConnectorConfig createTLSConfig () throws Exception { SocketConnectorConfig connectorConfig = createStandardConfig (); SSLContext sslContext = SSLContext.getInstance ("TLS"); sslContext.init (null, new TrustManager [] {ACCEPT_ALL_MANAGER}, null); SSLFilter sslFilter = new SSLFilter (sslContext); sslFilter.setUseClientMode (true); connectorConfig.getFilterChain ().addFirst ("ssl", sslFilter); return connectorConfig; } private static SocketConnectorConfig createStandardConfig () throws Exception { SocketConnectorConfig connectorConfig = new SocketConnectorConfig (); connectorConfig.setThreadModel (ThreadModel.MANUAL); connectorConfig.setConnectTimeout (10); connectorConfig.getFilterChain ().addLast ("codec", ClientFrameCodec.FILTER); return connectorConfig; } static final X509TrustManager ACCEPT_ALL_MANAGER = new X509TrustManager () { public void checkClientTrusted (X509Certificate [] x509Certificates, String s) throws CertificateException { // zip: allow anything } public void checkServerTrusted (X509Certificate [] x509Certificates, String s) throws CertificateException { // zip: allow anything } public X509Certificate [] getAcceptedIssuers () { return new X509Certificate [0]; } }; } avis-1.2.2/server/src/test/org/avis/router/DosAttack.java0000644000175000017500000000612511147555544023170 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.avis.common.ElvinURI; import org.avis.io.messages.NotifyEmit; import static java.lang.System.currentTimeMillis; import static org.avis.logging.Log.info; public class DosAttack { public static void main (String [] args) throws Exception { if (args.length < 1) { System.err.println ("Usage: DosAttack "); System.exit (1); } final Collection receiveClients = new ArrayList (); ElvinURI uri = new ElvinURI (args [0]); ByteBuffer.setUseDirectBuffers (false); final SimpleClient sendClient = new SimpleClient (uri.host, uri.port); sendClient.connect (); for (int i = 0; i < 10; i++) { SimpleClient receiveClient = new SimpleClient (uri.host, uri.port); receiveClient.connect (); receiveClient.subscribe ("require (name)"); receiveClients.add (receiveClient); } sendClient.subscribe ("require (name)"); final Map ntfn = new HashMap (); ntfn.put ("name", "foobar"); ntfn.put ("data", new byte [64 * 1024]); new Thread () { @Override public void run () { info ("Sender running...", this); long start = currentTimeMillis (); int count = 0; try { while (currentTimeMillis () - start < 30 * 60 * 1000) { sendClient.send (new NotifyEmit (ntfn)); count++; if (count % 20 == 0) info (count + " messages sent", this); } } catch (Exception ex) { ex.printStackTrace (); } } }.start (); new Thread () { @Override public void run () { int count = 0; info ("Receiver running...", this); try { while (true) { for (SimpleClient receiveClient : receiveClients) receiveClient.receive (); count++; if (count % 20 == 0) info (count + " messages received", this); } } catch (Exception ex) { ex.printStackTrace (); } } }.start (); } } avis-1.2.2/server/src/test/org/avis/router/JUTestClientConnectionOptions.java0000644000175000017500000000744411147555544027231 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.HashMap; import java.util.Map; import org.junit.Test; import static org.avis.io.LegacyConnectionOptions.legacyToNew; import static org.avis.router.ClientConnectionOptionSet.CLIENT_CONNECTION_OPTION_SET; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; /** * Test the {@link ClientConnectionOptions} class. * * @author Matthew Phillips */ public class JUTestClientConnectionOptions { @Test public void validation () { HashMap requested = new HashMap (); requested.put ("Packet.Max-Length", 8 * 1024 * 1024); // valid requested.put ("Attribute.Max-Count", 63); // bogus value requested.put ("Bogus", 42); // bogus requested.put ("Receive-Queue.Drop-Policy", "bogus"); // bogus requested.put ("Send-Queue.Drop-Policy", "fail"); // valid requested.put ("Receive-Queue.Drop-Policy", "oldest"); // valid, default requested.put ("Send-Queue.Max-Length", "bogus"); // bogus ClientConnectionOptions options; try { options = new ClientConnectionOptions (requested); Map valid = options.accepted (); assertRequested (requested, valid, "Packet.Max-Length"); assertDefault (valid, "Attribute.Max-Count"); assertNull (valid.get ("Bogus")); assertDefault (valid, "Receive-Queue.Drop-Policy"); assertRequested (requested, valid, "Send-Queue.Drop-Policy"); assertRequested (requested, valid, "Receive-Queue.Drop-Policy"); assertDefault (valid, "Send-Queue.Max-Length"); } catch (ExceptionInInitializerError ex) { ex.getCause ().printStackTrace (); } } @Test public void compatibility () { HashMap requested = new HashMap (); requested.put ("router.attribute.string.max-length", 8 * 1024 * 1024); // valid requested.put ("router.send-queue.drop-policy", "fail"); // valid requested.put ("router.attribute.max-count", 63); // bogus requested.put ("router.coalesce-delay", 0); // valid ClientConnectionOptions options = new ClientConnectionOptions (requested); Map accepted = options.accepted (); // todo: when Attribute.String.Max-Length supported, switch lines below // assertRequested (requested, valid, "router.attribute.string.max-length"); assertDefault (accepted, "router.attribute.string.max-length"); assertDefault (accepted, "router.attribute.max-count"); assertRequested (requested, accepted, "router.send-queue.drop-policy"); assertRequested (requested, accepted, "router.coalesce-delay"); } private static void assertDefault (Map accepted, String name) { assertEquals (CLIENT_CONNECTION_OPTION_SET.defaults.get (legacyToNew (name)), accepted.get (name)); } private static void assertRequested (Map requested, Map valid, String name) { assertEquals (requested.get (name), valid.get (name)); } } avis-1.2.2/server/src/test/org/avis/router/AgentClient.java0000644000175000017500000000274511147555544023514 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Timer; import org.apache.mina.common.IoSession; public abstract class AgentClient extends SimpleClient { public volatile int sentMessageCount; public volatile int receivedMessageCount; protected Timer timer; public AgentClient (String name, String host, int port) throws Exception { super (name, host, port); timer = new Timer (); sentMessageCount = 0; receivedMessageCount = 0; connect (); } public String report () { return clientName + " sent " + sentMessageCount + ", received " + receivedMessageCount; } @Override public void messageReceived (IoSession session, Object message) throws Exception { receivedMessageCount++; super.messageReceived (session, message); } } avis-1.2.2/server/src/test/org/avis/router/SimpleClient.java0000644000175000017500000002525411147555544023707 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import junit.framework.AssertionFailedError; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.RuntimeIOException; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.avis.io.ClientFrameCodec; import org.avis.io.messages.ConnRply; import org.avis.io.messages.ConnRqst; import org.avis.io.messages.DisconnRply; import org.avis.io.messages.DisconnRqst; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.NotifyEmit; import org.avis.io.messages.RequestMessage; import org.avis.io.messages.SubAddRqst; import org.avis.io.messages.SubRply; import org.avis.io.messages.XidMessage; import org.avis.security.Keys; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.avis.io.messages.ConnRqst.EMPTY_OPTIONS; import static org.avis.logging.Log.alarm; import static org.avis.logging.Log.trace; import static org.avis.router.JUTestRouter.PORT; import static org.avis.security.Keys.EMPTY_KEYS; import static org.avis.util.Text.className; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Basic Avis test client. */ public class SimpleClient implements IoHandler, Closeable { protected static final int RECEIVE_TIMEOUT = 5000; protected IoSession clientSession; protected boolean connected; protected BlockingQueue incomingMessages = new LinkedBlockingQueue (); public String clientName; public SimpleClient () throws IOException { this ("localhost", PORT); } public SimpleClient (String clientName) throws IOException { this (clientName, "localhost", PORT); } public SimpleClient (String hostname, int port) throws IOException { this ("client", hostname, port); } public SimpleClient (String clientName, String hostname, int port) throws IOException { this.clientName = clientName; SocketConnector connector = new SocketConnector (); /* Change the worker timeout to 1 second to make the I/O thread * quit soon when there's no connection to manage. */ connector.setWorkerTimeout (1); SocketConnectorConfig cfg = new SocketConnectorConfig (); cfg.setConnectTimeout (10); cfg.getFilterChain ().addLast ("codec", ClientFrameCodec.FILTER); ConnectFuture future = connector.connect (new InetSocketAddress (hostname, port), this, cfg); future.join (); clientSession = future.getSession (); } public SimpleClient (InetSocketAddress address, SocketConnectorConfig cfg) { this ("client", address, cfg); } public SimpleClient (String clientName, InetSocketAddress address, SocketConnectorConfig cfg) { this.clientName = clientName; SocketConnector connector = new SocketConnector (); /* Change the worker timeout to 1 second to make the I/O thread * quit soon when there's no connection to manage. */ connector.setWorkerTimeout (1); ConnectFuture future = connector.connect (address, this, cfg); future.join (); clientSession = future.getSession (); } public synchronized void send (Message message) throws NoConnectionException { checkConnected (); /* * todo under MINA 1.1.5 this joining-the-send-future malarkey has * no effect: a close after a send can still eat the sent message. * This only really affects UNotify because the others all use the * Disconn sequence. */ if (!clientSession.write (message).join (RECEIVE_TIMEOUT)) throw new RuntimeIOException ("Failed to send " + message.name ()); } public Message receive () throws InterruptedException, MessageTimeoutException, NoConnectionException { return receive (RECEIVE_TIMEOUT); } public Message receive (long timeout) throws MessageTimeoutException, NoConnectionException, InterruptedException { Message message = incomingMessages.poll (timeout, MILLISECONDS); if (message == null) { if (!clientSession.isConnected ()) { throw new MessageTimeoutException (clientName + " did not receive a reply: connection is closed"); } else { throw new MessageTimeoutException (clientName + " did not receive a reply"); } } return message; } private void checkConnected () throws NoConnectionException { if (!clientSession.isConnected ()) throw new NoConnectionException ("Not connected"); } public R sendAndReceive (RequestMessage request) throws Exception { send (request); return receiveReply (request); } @SuppressWarnings("unchecked") R receiveReply (RequestMessage request) throws Exception { XidMessage reply = receive (request.replyType ()); if (reply.xid != request.xid) { throw new IOException ("Protocol error: Transaction ID mismatch in reply from router"); } else if (request.replyType ().isAssignableFrom (reply.getClass ())) { return (R)reply; } else if (reply instanceof Nack) { Nack nack = (Nack)reply; throw new IOException ("NACK reply:" + nack.formattedMessage ()); } else { throw new IOException ("Protocol error: received a " + className (reply) + ": was expecting " + className (request.replyType ())); } } public T receive (Class type) throws MessageTimeoutException, InterruptedException, NoConnectionException { return receive (type, RECEIVE_TIMEOUT); } /** * Wait until we receive a message of a given type. Other messages * are discarded. * @throws NoConnectionException */ @SuppressWarnings("unchecked") public synchronized T receive (Class type, long timeout) throws MessageTimeoutException, InterruptedException, NoConnectionException { long start = currentTimeMillis (); while (currentTimeMillis () - start <= timeout) { Message message = receive (timeout); if (type.isAssignableFrom (message.getClass ())) return (T)message; } throw new MessageTimeoutException ("Failed to receive a " + type.getName ()); } public void sendNotify (Map attributes) throws Exception { send (new NotifyEmit (attributes, true, EMPTY_KEYS)); } public void sendNotify (Map attributes, Keys keys) throws Exception { send (new NotifyEmit (attributes, false, keys)); } public SubRply subscribe (String subExpr) throws Exception { return subscribe (subExpr, EMPTY_KEYS); } public synchronized SubRply subscribe (String subExpr, Keys keys) throws Exception { SubAddRqst subAddRqst = new SubAddRqst (subExpr, keys, true); send (subAddRqst); Message reply = receive (); if (reply instanceof SubRply) { SubRply subRply = (SubRply)reply; assertEquals (subAddRqst.xid, subRply.xid); return subRply; } else if (reply instanceof Nack) { throw new AssertionFailedError (clientName + ": subscription NACK: " + ((Nack)reply).message + ": " + subExpr); } else { throw new AssertionFailedError (clientName + ": unexpected reply type: " + reply.getClass ().getName ()); } } public ConnRply connect () throws Exception { return connect (EMPTY_OPTIONS); } public synchronized ConnRply connect (Map options) throws Exception { checkConnected (); send (new ConnRqst (4, 0, options, EMPTY_KEYS, EMPTY_KEYS)); Message reply = receive (); assertTrue (reply instanceof ConnRply); connected = true; return (ConnRply)reply; } public void close () { try { close (RECEIVE_TIMEOUT); } catch (Exception ex) { throw new RuntimeIOException (ex); } } public synchronized void close (long timeout) throws Exception { if (clientSession == null) return; if (connected && clientSession.isConnected ()) { send (new DisconnRqst ()); receive (DisconnRply.class, timeout); } try { if (!clientSession.close ().join (5000)) throw new IOException ("Failed to close client session"); } finally { clientSession = null; } } /** * Close session with no disconnect request. */ public synchronized void closeImmediately () { connected = false; clientSession.close (); clientSession = null; } // IoHandler interface public void exceptionCaught (IoSession session, Throwable ex) throws Exception { alarm (clientName + ": client internal exception", this, ex); } public void messageReceived (IoSession session, Object message) throws Exception { trace (clientName + ": message received: " + message, this); incomingMessages.add ((Message)message); } public void messageSent (IoSession session, Object message) throws Exception { // zip } public void sessionClosed (IoSession session) throws Exception { // zip } public void sessionCreated (IoSession session) throws Exception { // zip } public void sessionIdle (IoSession session, IdleStatus status) throws Exception { // zip } public void sessionOpened (IoSession session) throws Exception { // zip } }avis-1.2.2/server/src/test/org/avis/router/MessageTimeoutException.java0000644000175000017500000000201111147555544026113 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; public class MessageTimeoutException extends Exception { public MessageTimeoutException (String message) { super (message); } public MessageTimeoutException (Throwable cause) { super (cause); } public MessageTimeoutException (String message, Throwable cause) { super (message, cause); } } avis-1.2.2/server/src/test/org/avis/router/MemoryLeak.java0000644000175000017500000000370411147555544023360 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.HashMap; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.avis.io.messages.NotifyEmit; import static java.lang.System.currentTimeMillis; /** * Send/receive a stream of notifications to the router in order to * watch heap activity. * * @author Matthew Phillips */ public class MemoryLeak { public static void main () throws Exception { ByteBuffer.setUseDirectBuffers (true); Router router = new Router (29170); SimpleClient client = new SimpleClient (); client.connect (); client.subscribe ("require (name)"); Map ntfn = new HashMap (); ntfn.put ("name", "foobar"); ntfn.put ("data", new byte [64 * 1024]); long start = currentTimeMillis (); int count = 0; while (currentTimeMillis () - start < 30 * 60 * 1000) { client.send (new NotifyEmit (ntfn)); client.receive (); count++; if ((currentTimeMillis () - start) % (10 * 1000) == 0) { System.gc (); System.out.println ("Heap = " + Runtime.getRuntime ().freeMemory ()); } } System.out.println ("Messages = " + count); client.close (); router.close (); } } avis-1.2.2/server/src/test/org/avis/router/MaliciousClient.java0000644000175000017500000000347411147555544024403 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimerTask; import static org.avis.logging.Log.alarm; public class MaliciousClient extends AgentClient { public MaliciousClient (int port) throws Exception { super ("malicious", "localhost", port); } public MaliciousClient (String name, String host, int port) throws Exception { super (name, host, port); } public void startFlooding () { timer.schedule (new FloodTask (), 0, 1); } public void stopFlooding () { timer.cancel (); } class FloodTask extends TimerTask { private final byte [] BIG_BLOB = new byte [500 * 1024]; @Override public void run () { try { Map ntfn = new HashMap (); ntfn.put ("From", getClass ().getName ()); ntfn.put ("Time", new Date ().toString ()); ntfn.put ("Payload", BIG_BLOB); sendNotify (ntfn); sentMessageCount++; } catch (Exception ex) { alarm ("Failed to send notification", this, ex); } } } } avis-1.2.2/server/src/test/org/avis/router/Fuzz.java0000644000175000017500000000660411147555544022253 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Random; import java.util.concurrent.ExecutorService; import java.net.InetSocketAddress; import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoFutureListener; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoSession; import org.apache.mina.common.ThreadModel; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import static java.lang.Thread.sleep; import static java.util.concurrent.Executors.newCachedThreadPool; /** * Fuzz tests an Elvin router by writing message frames with random payloads. * * @author Matthew Phillips */ public class Fuzz { final static int [] MESSAGES = new int [] {32, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 80, 81, 82, 83}; public static void main (String [] args) throws InterruptedException { new Fuzz ((args.length > 0 ? args [0] : "127.0.0.1")).run (); } private SocketConnector connector; private ExecutorService executor; private InetSocketAddress remoteAddress; private Random random; public Fuzz (String host) { executor = newCachedThreadPool (); // connector connector = new SocketConnector (1, executor); connector.setWorkerTimeout (0); remoteAddress = new InetSocketAddress (host, 2917); random = new Random (hashCode ()); } public void run () throws InterruptedException { while (true) { IoSession session = connect (); ByteBuffer buffer = ByteBuffer.allocate (128 * 1024); buffer.acquire (); int bytes = random.nextInt (buffer.capacity () - 8); bytes = bytes - (bytes % 4); buffer.clear (); buffer.putInt (bytes + 4); buffer.putInt (MESSAGES [random.nextInt (MESSAGES.length)]); for (int i = bytes; i > 0; i--) buffer.put ((byte)(random.nextInt (256) - 127)); buffer.flip (); session.write (buffer).addListener (IoFutureListener.CLOSE); System.out.println ("Wrote " + bytes + " bytes"); sleep (10); } } private IoSession connect () { SocketConnectorConfig connectorConfig = new SocketConnectorConfig (); connectorConfig.setThreadModel (ThreadModel.MANUAL); connectorConfig.setConnectTimeout (20); ConnectFuture future = connector.connect (remoteAddress, new IoHandlerAdapter (), connectorConfig); future.join (); return future.getSession (); } } avis-1.2.2/server/src/test/org/avis/router/JUTestRouterOptions.java0000644000175000017500000001224711147555544025250 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import org.avis.common.ElvinURI; import org.avis.federation.FederationOptionSet; import org.junit.Test; import static java.net.NetworkInterface.getNetworkInterfaces; import static org.avis.common.Common.DEFAULT_PORT; import static org.avis.io.Net.addressesFor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class JUTestRouterOptions { @Test public void bindAll () throws Exception { RouterOptions options = new RouterOptions (); options.set ("Port", "29170"); options.set ("Listen", "elvin://0.0.0.0"); Set uris = options.listenURIs (); Set addresses = addressesFor (uris); assertEquals (1, uris.size ()); assertTrue (addresses.contains (new InetSocketAddress (options.getInt ("Port")))); } /** * Test binding to an interface name. */ @Test public void bindInterface () throws Exception { NetworkInterface netInterface = getNetworkInterfaces ().nextElement (); Set interfaceAddresses = new HashSet (); for (Enumeration i = netInterface.getInetAddresses (); i.hasMoreElements (); ) { InetAddress address = i.nextElement (); // System.out.println ("Interface address = " + address); if (!address.isLinkLocalAddress ()) interfaceAddresses.add (address); } testInterfaces (interfaceAddresses, netInterface.getName (), DEFAULT_PORT); testInterfaces (interfaceAddresses, netInterface.getName () + ":29170", 29170); } /** * Test binding to a host name. */ @Test public void bindHost () throws Exception { InetAddress localhost = InetAddress.getLocalHost (); Set localhostAddresses = new HashSet (); for (InetAddress address : InetAddress.getAllByName (localhost.getHostName ())) { // System.out.println ("Host address = " + address); if (!address.isLinkLocalAddress ()) localhostAddresses.add (address); } testHost (localhostAddresses, localhost.getHostName (), DEFAULT_PORT); testHost (localhostAddresses, localhost.getHostName () + ":29170", 29170); } /** * Test multiple URI's in Listen field with whitespace. * @throws Exception */ @Test public void multipleURIs () throws Exception { RouterOptions options = new RouterOptions (); options.set ("Listen", "elvin:/tcp,none,xdr/localhost:1234 \t elvin://localhost"); Set addresses = addressesFor (options.listenURIs ()); boolean found1234 = false; for (InetSocketAddress address : addresses) { if (address.getPort () == 1234) found1234 = true; } assertTrue (found1234); } /** * Test that adding federator options (as router's Main does) to * router's works */ @Test public void federationOptions () throws Exception { RouterOptionSet routerOptionSet = new RouterOptionSet (); routerOptionSet.inheritFrom (FederationOptionSet.OPTION_SET); RouterOptions config = new RouterOptions (routerOptionSet); config.set ("Federation.Subscribe[Test]", "require (federated)"); } private void testHost (Set hostAddresses, String hostOption, int port) throws Exception { RouterOptions options = new RouterOptions (); options.set ("Listen", "elvin://" + hostOption); Set addresses = addressesFor (options.listenURIs ()); assertEquals (hostAddresses.size (), addresses.size ()); for (InetAddress address : hostAddresses) assertTrue (addresses.contains (new InetSocketAddress (address, port))); } private void testInterfaces (Set interfaceAddresses, String interfaceOption, int port) throws Exception { RouterOptions options = new RouterOptions (); options.set ("Listen", "elvin://!" + interfaceOption); Set addresses = addressesFor (options.listenURIs ()); assertEquals (interfaceAddresses.size (), addresses.size ()); for (InetAddress address : interfaceAddresses) assertTrue (addresses.contains (new InetSocketAddress (address, port))); } } avis-1.2.2/server/src/test/org/avis/config/0000755000175000017500000000000011147555544020371 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/config/JUTestOptions.java0000644000175000017500000002031211147555544023764 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.net.URI; import org.junit.Test; import org.avis.util.IllegalConfigOptionException; import static java.util.Collections.emptySet; import static org.avis.common.Common.K; import static org.avis.common.Common.MB; import static org.avis.util.Collections.set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class JUTestOptions { @Test public void basic () { ConnectionOptionSet connectionOptionSet = new ConnectionOptionSet (); OptionSet routerOptionSet = new OptionSet (connectionOptionSet); routerOptionSet.add ("Port", 1, 2917, 65535); routerOptionSet.add ("Interfaces", "*"); routerOptionSet.add ("Vendor-Identification", "Foobar"); routerOptionSet.add ("Federation.Activated", false); Options routerOptions = new Options (routerOptionSet); Properties loadedProperties = new Properties (); loadedProperties.setProperty ("Port", "29170"); loadedProperties.setProperty ("Keys.Max-Count", "42"); loadedProperties.setProperty ("Interfaces", "en1"); loadedProperties.setProperty ("Federation.Activated", "yes"); routerOptions.setAll (loadedProperties); Map requestedOptions = new HashMap (); requestedOptions.put ("Packet.Max-Length", 20*K); requestedOptions.put ("router.subscription.max-count", 1234); Options connectionOptions = new Options (connectionOptionSet); connectionOptions.addDefaults (routerOptions); connectionOptions.setAll (requestedOptions); Map accepted = connectionOptionSet.accepted (connectionOptions, requestedOptions); assertEquals (29170, routerOptions.getInt ("Port")); assertEquals ("en1", routerOptions.getString ("Interfaces")); assertEquals (true, routerOptions.getBoolean ("Federation.Activated")); assertEquals (20*K, connectionOptions.getInt ("Packet.Max-Length")); assertEquals (42, connectionOptions.getInt ("Keys.Max-Count")); assertEquals (1234, connectionOptions.getInt ("Subscription.Max-Count")); assertEquals (1234, accepted.get ("router.subscription.max-count")); } /** * Test that options are case-preserving but case independent for lookup. */ @Test public void caseIndependence () { OptionSet optionSet = new OptionSet (); optionSet.add ("Port", 0, 2917, 65536); optionSet.add ("foobar", "frob", "wibble"); Options options = new Options (optionSet); options.set ("port", 1234); options.set ("FooBar", "wibble"); assertEquals (1234, options.get ("Port")); assertEquals (1234, options.get ("port")); assertEquals ("wibble", options.get ("foobar")); assertEquals ("wibble", options.get ("FOOBAR")); assertTrue (options.options ().contains ("FooBar")); } /** * Test unit conversion. */ @Test public void units () { OptionSet optionSet = new OptionSet (); optionSet.add ("Packet.Max-Length", 0, 100, 10000); optionSet.add ("Attribute.Max-Count", 0, 100, 5000000); Options options = new Options (optionSet); options.set ("Packet.Max-Length", "5K"); options.set ("Attribute.Max-Count", "1M"); assertEquals (5*1024, options.get ("Packet.Max-Length")); assertEquals (1*1024*1024, options.get ("Attribute.Max-Count")); } @Test public void multipleInherit () throws Exception { OptionSet optionSet1 = new OptionSet (); optionSet1.add ("Packet.Max-Length", 0, 100, 10000); optionSet1.add ("Attribute.Max-Count", 0, 100, 5000000); OptionSet optionSet2 = new OptionSet (); optionSet2.add ("Federation.Active", true); optionSet2.add ("Federation.Name", ""); OptionSet rootOptionSet = new OptionSet (); rootOptionSet.add ("Root1", true); Options options = new Options (rootOptionSet); // add inherited afterwards to be tricky rootOptionSet.inheritFrom (optionSet1); rootOptionSet.inheritFrom (optionSet2); assertEquals (true, options.get ("Federation.Active")); assertEquals (100, options.get ("Packet.Max-Length")); assertEquals (true, options.get ("Root1")); options.set ("Packet.Max-Length", 200); options.set ("Federation.Active", false); options.set ("Root1", false); assertEquals (false, options.get ("Federation.Active")); assertEquals (200, options.get ("Packet.Max-Length")); assertEquals (false, options.get ("Root1")); } @Test public void uri () throws Exception { OptionSet optionSet = new OptionSet (); optionSet.add ("FileUri", new OptionTypeURI (), new URI ("")); optionSet.add ("HttpUri", new OptionTypeURI (), new URI ("")); Options options = new Options (optionSet); options.set ("FileUri", "file.txt"); options.set ("HttpUri", "http://host/file.txt"); URI fileUri = options.getAbsoluteURI ("FileUri"); URI httpUri = options.getAbsoluteURI ("HttpUri"); String dir = System.getProperty ("user.dir").replaceAll ("\\\\", "/"); assertEquals ("file:" + dir + "/file.txt", fileUri.toString ()); assertTrue (fileUri.isAbsolute ()); assertEquals ("http://host/file.txt", httpUri.toString ()); } @Test public void optionTypeSet () throws Exception { OptionSet optionSet = new OptionSet (); optionSet.add ("Values", new OptionTypeSet (String.class), emptySet ()); Options options = new Options (optionSet); options.set ("Values", "1"); assertEquals (set ("1"), options.get ("Values")); options.set ("Values", "1 2"); assertEquals (set ("1", "2"), options.get ("Values")); options.set ("Values", " 1\\ 2 \n 3\n "); assertEquals (set ("1 2", "3"), options.get ("Values")); } static class ConnectionOptionSet extends OptionSet { private Map legacyToNew; private Map newToLegacy; public ConnectionOptionSet () { this.legacyToNew = new HashMap (); this.newToLegacy = new HashMap (); add ("Packet.Max-Length", 1*K, 1*MB, 1*MB); add ("Subscription.Max-Count", 16, 2*K, 2*K); add ("Keys.Max-Count", 16, 2*K, 2*K); add ("Send-Queue.Drop-Policy", "oldest", "newest", "largest", "fail"); addLegacy ("router.packet.max-length", "Packet.Max-Length"); addLegacy ("router.subscription.max-count", "Subscription.Max-Count"); } public Map accepted (Options connectionOptions, Map requestedOptions) { HashMap accepted = new HashMap (); for (String option : connectionOptions.options ()) { Object value = connectionOptions.get (option); if (!requestedOptions.containsKey (option)) option = newToLegacy.get (option); accepted.put (option, value); } return accepted; } private void addLegacy (String oldOption, String newOption) { legacyToNew.put (oldOption, newOption); newToLegacy.put (newOption, oldOption); } @Override protected void validateAndSet (Options options, String option, Object value) throws IllegalConfigOptionException { if (legacyToNew.containsKey (option)) option = legacyToNew.get (option); super.validateAndSet (options, option, value); } } } avis-1.2.2/server/src/test/org/avis/subscription/0000755000175000017500000000000011147555544021650 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/subscription/parser/0000755000175000017500000000000011147555544023144 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/subscription/parser/JUTestParser.java0000644000175000017500000002325411147555544026350 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.parser; import java.io.StringReader; import org.avis.subscription.ast.Node; import org.junit.Test; import static org.avis.subscription.ast.Nodes.unparse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * Test the subscription expression parser and validator. * * @author Matthew Phillips */ public class JUTestParser { /** * Test identifiers as per Appendix A of client spec. */ @Test public void identifiers () throws Exception { assertParsesTo ("hello", "(field 'hello')"); assertParsesTo ("_hello", "(field '_hello')"); assertParsesTo ("_", "(field '_')"); assertParsesTo ("_1", "(field '_1')"); for (int i = 0x21; i <= 0xff; i++) { if (i == 0x22 || i == 0x27 || i == 0x28 || i == 0x29 || i == 0x30 || i == 0x2c || i == 0x5b || i == 0x5c || i == 0x5d) continue; assertParsesTo ("tricky" + (char)i + "id", "(field 'tricky" + (char)i + "id')"); } } @Test public void strings () throws Exception { assertParsesTo ("'a\\n'", "'an'"); assertParsesTo ("'a\\\\'", "'a\\'"); assertParsesTo ("\"a\\\\!\"", "'a\\!'"); assertParsesTo ("\"a\\\"\"", "'a\"'"); assertParsesTo ("\"\'abc\'\"", "''abc''"); assertParseError ("'a\\'"); } @Test public void numbers () throws Exception { assertParsesTo ("0.0", "0.0"); assertParsesTo ("3.4", "3.4"); assertParsesTo ("1.2e10", "1.2E10"); assertParsesTo ("1.2E10", "1.2E10"); assertParsesTo ("1.2E+10", "1.2E10"); assertParsesTo ("1.2E-10", "1.2E-10"); assertParseError ("0."); assertParseError (".1"); assertParseError ("0.1e"); assertParseError ("0.1e-"); assertParseError ("0.1eg"); assertParseError ("0.1f"); } /** * Test parser doesn't get confused by a function names as a field. */ @Test public void functionsAsFields () throws Exception { assertParsesTo ("begins-with == 'hello'", "(== (field 'begins-with') 'hello')"); assertParsesTo ("int32 == 32", "(== (field 'int32') 32)"); } /** * Basic parse tests using comparison and logical comparators. */ @Test public void basic () throws Exception { assertParsesTo ("field1 > 2 && field2 == 'hello\\'there'", "(&& (> (field 'field1') 2) (== (field 'field2') " + "'hello'there'))"); assertParsesTo ("(field1 != 10L || field2 < 3.2) ^^ (field3 == \"hello\")", "(^^ (|| (! (== (field 'field1') 10L)) " + "(< (field 'field2') 3.2)) " + "(== (field 'field3') 'hello'))"); } /** * Test the various functions. */ @Test public void functions () throws Exception { assertParsesTo ("size (name)", "(size (field 'name'))"); assertParsesTo ("require (name)", "(require (field 'name'))"); assertParsesTo ("int32 (name)", "(int32 (field 'name'))"); assertParsesTo ("int64 (name)", "(int64 (field 'name'))"); assertParsesTo ("real64 (name)", "(real64 (field 'name'))"); assertParsesTo ("string (name)", "(string (field 'name'))"); assertParsesTo ("begins-with (name, 'hello')", "(begins-with (field 'name') 'hello')"); assertParsesTo ("ends-with (name, 'hello')", "(ends-with (field 'name') 'hello')"); assertParsesTo ("contains (name, 'hello')", "(contains (field 'name') 'hello')"); assertParsesTo ("regex (name, 'hello!+')", "(regex (field 'name') 'hello!+')"); assertParsesTo ("wildcard (name, 'hel*lo?')", "(wildcard (field 'name') 'hel*lo?')"); assertParsesTo ("fold-case (name)", "(fold-case (field 'name'))"); assertParsesTo ("decompose (name)", "(decompose (field 'name'))"); assertParsesTo ("decompose-compat (name)", "(decompose-compat (field 'name'))"); assertParsesTo ("equals (name, 'hello')", "(== (field 'name') 'hello')"); assertParsesTo ("equals ('hello', 'hello')", "(== 'hello' 'hello')"); assertParsesTo ("equals (name, 'hello', 'there')", "(|| (== (field 'name') 'hello') (== (field 'name') 'there'))"); assertParsesTo ("equals (name, 'hello', 42)", "(|| (== (field 'name') 'hello') (== (field 'name') 42))"); // check that spaces are not required before brackets assertParsesTo ("size(name)", "(size (field 'name'))"); assertParseError ("equals ('hello', 'there', 1)"); assertParseError ("begins-with (name, 1)"); assertParseError ("ends-with (name, 1)"); assertParseError ("contains (name, 1)"); assertParseError ("fold-case (1)"); assertParseError ("decompose (1)"); assertParseError ("decompose-compat (1)"); assertParseError ("regex (name, 1)"); assertParseError ("regex (name, '(abc')"); assertParseError ("foobar (name, 'hello')"); } /** * Test infix/prefix math ops. */ @Test public void mathOps () throws Exception { assertParsesTo ("1 + 2", "(+ 1 2)"); assertParsesTo ("1 + 2 + 3", "(+ (+ 1 2) 3)"); assertParsesTo ("1 - 2", "(- 1 2)"); assertParsesTo ("1 - 2 - 3", "(- (- 1 2) 3)"); assertParsesTo ("1 - 2 + 3", "(- 1 (+ 2 3))"); assertParsesTo ("1 * 2 / 3", "(* 1 (/ 2 3))"); assertParsesTo ("1 % 2", "(% 1 2)"); // check "-" operator assertParsesTo ("-2", "(- 2)"); assertParsesTo ("~10", "(~ 10)"); assertParsesTo ("1 & 2", "(& 1 2)"); assertParsesTo ("1 ^ 2", "(^ 1 2)"); assertParsesTo ("1 | 2", "(| 1 2)"); assertParsesTo ("1 << 2", "(<< 1 2)"); assertParsesTo ("1 >> 2", "(>> 1 2)"); assertParsesTo ("1 >>> 2", "(>>> 1 2)"); assertParsesTo ("(name1 & 0x0F) >> name2", "(>> (& (field 'name1') 15) (field 'name2'))"); assertParsesTo ("(name1 << 2L) | name2 & 0xF0", "(| (<< (field 'name1') 2L) " + "(& (field 'name2') 240))"); // check math in equals () assertParsesTo ("equals (name, 1 + 2)", "(== (field 'name') (+ 1 2))"); // errors assertParseError ("1 + 'hello'"); assertParseError ("'hello' + 'hello'"); assertParseError ("1.0 << 2"); assertParseError ("1 << 2.0"); assertParseError ("~'hello'"); assertParseError ("- 'hello'"); assertParseError ("--"); } /** * Test error basic parse error detection. Some error detection * testing is also done as part of other tests. */ @Test public void errors () throws Exception { // token errors assertParseError ("'"); assertParseError ("\""); // syntax errors assertParseError (""); assertParseError (" "); assertParseError ("(1 > 2"); assertParseError ("(1 > 2) (2 > 1)"); // type errors assertParseError ("5 > 'name'"); assertParseError ("field > 3 || 'name'"); assertParseError ("field > 3 || 42"); assertParseError ("'hello' && 'there'"); // non-boolean expressions assertParseValidateError ("2"); assertParseValidateError ("'hello'"); // constant expressions assertParseValidateError ("1 == 1"); assertParseValidateError ("field > 5 || 1 == 1"); } /** * Test some more complex expressions from Sticker. */ @Test public void tickerClientSubs () throws Exception { parseAndValidate ("(require (Message) && require (From) && " + "(fold-case (Group) == \"matthew@home\" || " + "fold-case (From) == \"matthew@home\")) || " + "(require (TICKERTEXT) && require (USER) && " + "fold-case (TICKERTAPE) == \"matthew@home\" || " + "fold-case (USER) == \"matthew@home\")"); parseAndValidate ("Presence-Protocol < 2000 && string (Groups) && string (User) && " + "equals (Presence-Info, \'initial\', \'update\', \'xyz\') && " + "fold-case (User) != \"matthew@home\" && " + "(contains (fold-case (Groups), \"|dsto|\", \"|elvin|\", \"|ticker-dev|\") || " + "(equals (fold-case (User), \"foobar@dsto\", \"frodo@home\")))"); } private static void assertParseError (String expr) { try { parse (expr); fail ("Parse error not detected: \"" + expr + "\""); } catch (ParseException ex) { // System.out.println ("parse error: " + ex); // ok } } private static void assertParseValidateError (String expr) { try { parseAndValidate (expr); fail ("Validate error not detected: \"" + expr + "\""); } catch (ParseException ex) { // System.out.println ("parse error: " + ex); // ok } } private static void assertParsesTo (String subExpr, String treeExpr) throws ParseException { assertEquals (treeExpr, unparse (parse (subExpr))); } private static Node parseAndValidate (String expr) throws ParseException { return new SubscriptionParser (new StringReader (expr)).parseAndValidate (); } private static Node parse (String expr) throws ParseException { return new SubscriptionParser (new StringReader (expr)).parse (); } } avis-1.2.2/server/src/test/org/avis/subscription/ast/0000755000175000017500000000000011147555544022437 5ustar dpocockdpocockavis-1.2.2/server/src/test/org/avis/subscription/ast/JUTestEvaluation.java0000644000175000017500000006434311147555544026522 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.io.StringReader; import java.lang.reflect.Constructor; import org.junit.Test; import org.avis.subscription.ast.nodes.And; import org.avis.subscription.ast.nodes.Compare; import org.avis.subscription.ast.nodes.Const; import org.avis.subscription.ast.nodes.Field; import org.avis.subscription.ast.nodes.MathBitAnd; import org.avis.subscription.ast.nodes.MathBitInvert; import org.avis.subscription.ast.nodes.MathBitLogShiftRight; import org.avis.subscription.ast.nodes.MathBitOr; import org.avis.subscription.ast.nodes.MathBitShiftLeft; import org.avis.subscription.ast.nodes.MathBitShiftRight; import org.avis.subscription.ast.nodes.MathBitXor; import org.avis.subscription.ast.nodes.MathDiv; import org.avis.subscription.ast.nodes.MathMinus; import org.avis.subscription.ast.nodes.MathMod; import org.avis.subscription.ast.nodes.MathMult; import org.avis.subscription.ast.nodes.MathPlus; import org.avis.subscription.ast.nodes.MathUnaryMinus; import org.avis.subscription.ast.nodes.Nan; import org.avis.subscription.ast.nodes.Not; import org.avis.subscription.ast.nodes.Or; import org.avis.subscription.ast.nodes.Require; import org.avis.subscription.ast.nodes.Size; import org.avis.subscription.ast.nodes.StrBeginsWith; import org.avis.subscription.ast.nodes.StrContains; import org.avis.subscription.ast.nodes.StrEndsWith; import org.avis.subscription.ast.nodes.StrFoldCase; import org.avis.subscription.ast.nodes.StrRegex; import org.avis.subscription.ast.nodes.StrUnicodeDecompose; import org.avis.subscription.ast.nodes.StrWildcard; import org.avis.subscription.ast.nodes.Type; import org.avis.subscription.ast.nodes.Xor; import org.avis.subscription.parser.ParseException; import org.avis.subscription.parser.SubscriptionParser; import static java.lang.Double.NaN; import static org.avis.subscription.ast.Node.BOTTOM; import static org.avis.subscription.ast.Node.EMPTY_NOTIFICATION; import static org.avis.subscription.ast.Node.FALSE; import static org.avis.subscription.ast.Node.TRUE; import static org.avis.subscription.ast.nodes.StrUnicodeDecompose.Mode.DECOMPOSE; import static org.avis.subscription.ast.nodes.StrUnicodeDecompose.Mode.DECOMPOSE_COMPAT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** * Test the evaluation of AST's. * * @author Matthew Phillips */ public class JUTestEvaluation { /** All tri-state logic states. */ private static final Boolean [] LOGIC_STATES = new Boolean [] {TRUE, BOTTOM, FALSE}; private static final Boolean [] NOT_TRUTH_TABLE = new Boolean [] {FALSE, BOTTOM, TRUE}; private static final Boolean [] OR_TRUTH_TABLE = new Boolean [] {TRUE, TRUE, TRUE, TRUE, BOTTOM, BOTTOM, TRUE, BOTTOM, FALSE}; private static final Boolean [] AND_TRUTH_TABLE = new Boolean [] {TRUE, BOTTOM, FALSE, BOTTOM, BOTTOM, FALSE, FALSE, FALSE, FALSE}; private static final Boolean [] XOR_TRUTH_TABLE = new Boolean [] {FALSE, BOTTOM, TRUE, BOTTOM, BOTTOM, BOTTOM, TRUE, BOTTOM, FALSE}; /** * Test NOT, AND, OR and XOR logic operators against their tri-state * truth tables. */ @Test public void logicOps () throws Exception { // NOT for (Boolean state : LOGIC_STATES) { assertEquals (NOT_TRUTH_TABLE [toIndex (state)], new Not (new Const (state)).evaluate (EMPTY_NOTIFICATION)); } // OR, AND, XOR for (Boolean a : LOGIC_STATES) { for (Boolean b : LOGIC_STATES) { checkBooleanOp (And.class, a, b, and (a, b)); checkBooleanOp (Or.class, a, b, or (a, b)); checkBooleanOp (Xor.class, a, b, xor (a, b)); } } // check that triple children work also for (Boolean a : LOGIC_STATES) { for (Boolean b : LOGIC_STATES) { for (Boolean c : LOGIC_STATES) { checkBooleanOp (And.class, a, b, c, and (and (a, b), c)); checkBooleanOp (Or.class, a, b, c, or (or (a, b), c)); checkBooleanOp (Xor.class, a, b, c, xor (xor (a, b), c)); } } } } /** * Lookup result for AND in truth table. */ private static Boolean and (Boolean a, Boolean b) { return AND_TRUTH_TABLE [toIndex (a) * 3 + toIndex (b)]; } /** * Lookup result for OR in truth table. */ private static Boolean or (Boolean a, Boolean b) { return OR_TRUTH_TABLE [toIndex (a) * 3 + toIndex (b)]; } /** * Lookup result for XOR in truth table. */ private static Boolean xor (Boolean a, Boolean b) { return XOR_TRUTH_TABLE [toIndex (a) * 3 + toIndex (b)]; } /** * Truth table index for tri-state value. */ private static int toIndex (Boolean b) { if (b == TRUE) return 0; else if (b == BOTTOM) return 1; else return 2; } /** * Basic test for the Compare operator minus numeric conversion. */ @Test public void compare () { // less than: 10 < 20 assertTrue ("10 < 20", compare (10, 20, -1, false)); assertFalse ("10 < 10", compare (10, 10, -1, false)); // less than or equal assertTrue ("10 <= 20", compare (10, 20, -1, true)); assertTrue ("10 <= 10", compare (10, 10, -1, true)); assertFalse ("20 <= 10", compare (20, 10, -1, true)); // greater than assertTrue ("20 > 10", compare (20, 10, 1, false)); assertFalse ("10 > 10", compare (10, 10, 1, false)); // greater than or equal assertTrue ("20 >= 10", compare (20, 10, 1, true)); assertTrue ("10 >= 10", compare (10, 10, 1, true)); assertFalse ("10 > 20", compare (10, 20, 1, true)); // equal assertTrue ("10 == 10", compare (10, 10, 0, true)); assertFalse ("10 == 20", compare (10, 20, 0, true)); } /** * Test logic operator expressions using operators in LOGIC_OP_EXPR1 * and LOGIC_OP_EXPR2. */ @Test public void logicOpExprs () { Node expr1 = logicOpExpr1 (); Map n1 = new HashMap (); n1.put ("name", "Matt"); n1.put ("age", 19); n1.put ("blah", "blah"); assertTrue (expr1.evaluate (n1) == TRUE); Map n2 = new HashMap (); n2.put ("name", "Matt"); n2.put ("age", 30); n2.put ("blah", "blah"); assertFalse (expr1.evaluate (n2) == TRUE); Map n3 = new HashMap (); n3.put ("name", "Matt"); n3.put ("blah", "blah"); assertEquals (BOTTOM, expr1.evaluate (n3)); Map n4 = new HashMap (); n4.put ("name", "Matt"); n4.put ("age", 19); n4.put ("blah", "frob"); assertFalse (expr1.evaluate (n4) == TRUE); // XOR tests Node expr2 = logicOpExpr2 (); Map n5 = new HashMap (); n5.put ("name", "Matt"); n5.put ("age", 5); assertTrue (expr2.evaluate (n5) == TRUE); Map n6 = new HashMap (); n6.put ("name", "Matt"); n6.put ("age", 30); assertFalse (expr2.evaluate (n6) == TRUE); Map n7 = new HashMap (); n7.put ("hello", "there"); assertEquals (BOTTOM, expr2.evaluate (n7)); // opaque data Map n8 = new HashMap (); n8.put ("name", new byte [] {1, 2, 3}); n8.put ("age", 30); assertEquals (BOTTOM, expr2.evaluate (n8)); } @Test public void functions () throws Exception { Map ntfn; // basic string comparison predicates testPred (StrBeginsWith.class, "foobar", "foo", TRUE); testPred (StrBeginsWith.class, "foobar", "frob", FALSE); testPred (StrBeginsWith.class, null, "frob", BOTTOM); testPred (StrEndsWith.class, "foobar", "bar", TRUE); testPred (StrEndsWith.class, "foobar", "frob", FALSE); testPred (StrEndsWith.class, null, "frob", BOTTOM); testPred (StrContains.class, "foobar", "oob", TRUE); testPred (StrContains.class, "foobar", "frob", FALSE); testPred (StrContains.class, null, "frob", BOTTOM); testPred (StrRegex.class, "foobar", "o+", TRUE); testPred (StrRegex.class, "foobar", "o+x", FALSE); testPred (StrRegex.class, null, "o+", BOTTOM); testPred (StrWildcard.class, "foobar", "fo*a?", TRUE); testPred (StrWildcard.class, "foobar", "fo*a", FALSE); testPred (StrWildcard.class, null, "fo*a?", BOTTOM); testPred (StrWildcard.class, "fo*a", "fo\\*a", TRUE); testPred (StrWildcard.class, "foxa", "fo\\*a", FALSE); testPred (StrWildcard.class, "fo\\", "fo\\\\", TRUE); testPred (StrWildcard.class, "fo\\", "fo\\", TRUE); // require ntfn = new HashMap (); ntfn.put ("exists", "true"); assertEquals (TRUE, new Require ("exists").evaluate (ntfn)); assertEquals (BOTTOM, new Require ("not_exists").evaluate (ntfn)); // size ntfn = new HashMap (); ntfn.put ("opaque", new byte [10]); ntfn.put ("string", "1234"); ntfn.put ("int32", 1234); assertEquals (10, new Size ("opaque").evaluate (ntfn)); assertEquals (4, new Size ("string").evaluate (ntfn)); assertEquals (BOTTOM, new Size ("int32").evaluate (ntfn)); assertEquals (BOTTOM, new Size ("not_exists").evaluate (ntfn)); // fold-case assertEquals ("hello", new StrFoldCase (new Const ("HellO")).evaluate (EMPTY_NOTIFICATION)); assertEquals (BOTTOM, new StrFoldCase (new Const (null)).evaluate (EMPTY_NOTIFICATION)); // decompose // see examples at http://unicode.org/reports/tr15/#Canonical_Composition_Examples assertEquals ("\u0041\u0301", new StrUnicodeDecompose (new Const ("\u00C1"), DECOMPOSE).evaluate (EMPTY_NOTIFICATION)); assertEquals ("A\u0308\uFB03n", new StrUnicodeDecompose (new Const ("\u00C4\uFB03n"), DECOMPOSE).evaluate (EMPTY_NOTIFICATION)); assertEquals ("A\u0308ffin", new StrUnicodeDecompose (new Const ("\u00C4\uFB03n"), DECOMPOSE_COMPAT).evaluate (EMPTY_NOTIFICATION)); // nan ntfn = new HashMap (); ntfn.put ("nan", NaN); ntfn.put ("notnan", 42.0); ntfn.put ("notnan_int", 42); assertEquals (TRUE, new Nan (new Field ("nan")).evaluate (ntfn)); assertEquals (FALSE, new Nan (new Field ("notnan")).evaluate (ntfn)); assertEquals (BOTTOM, new Nan (new Field ("notnan_int")).evaluate (ntfn)); assertEquals (BOTTOM, new Nan (new Field ("nonexistent")).evaluate (ntfn)); // type checks ntfn = new HashMap (); ntfn.put ("int32", 1); ntfn.put ("int64", 2L); ntfn.put ("real64", 42.0); ntfn.put ("string", "hello"); ntfn.put ("opaque", new byte [] {1, 2, 3}); assertEquals (TRUE, new Type ("int32", Integer.class).evaluate (ntfn)); assertEquals (TRUE, new Type ("int64", Long.class).evaluate (ntfn)); assertEquals (TRUE, new Type ("real64", Double.class).evaluate (ntfn)); assertEquals (TRUE, new Type ("string", String.class).evaluate (ntfn)); assertEquals (TRUE, new Type ("opaque", byte [].class).evaluate (ntfn)); assertEquals (FALSE, new Type ("string", Integer.class).evaluate (ntfn)); assertEquals (BOTTOM, new Type ("nonexistent", String.class).evaluate (ntfn)); } /** * Check that variable-type expressions like equals (name, "foobar", * 42) work. */ @Test public void compareMultitype () { Map ntfn = new HashMap (); ntfn.put ("name", "foobar"); Node node = Compare.createEquals (argsList (field ("name"), Const.string ("foobar"), Const.int32 (42))); assertEquals (TRUE, node.evaluate (ntfn)); ntfn.put ("name", 42); assertEquals (TRUE, node.evaluate (ntfn)); ntfn.put ("name", new byte [] {1, 2, 3}); assertEquals (BOTTOM, node.evaluate (ntfn)); } @Test public void mathOps () throws Exception { testMathOp (MathMinus.class, 20 - 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathMinus.class, 20L - 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathMinus.class, 10.5 - 20.25, Const.real64 (10.5), Const.real64 (20.25)); testMathOp (MathMinus.class, 10 - 20.25, Const.int32 (10), Const.real64 (20.25)); testMathOp (MathMinus.class, null, new Field ("string"), Const.real64 (20.25)); testMathOp (MathMinus.class, null, Const.int32 (10), new Field ("")); testMathOp (MathPlus.class, 20 + 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathPlus.class, 20L + 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathPlus.class, 10.5 + 20.25, Const.real64 (10.5), Const.real64 (20.25)); testMathOp (MathPlus.class, 10 + 20.25, Const.int32 (10), Const.real64 (20.25)); testMathOp (MathMult.class, 20 * 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathMult.class, 20L * 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathMult.class, 10.5 * 20.25, Const.real64 (10.5), Const.real64 (20.25)); testMathOp (MathMult.class, 10 * 20.25, Const.int32 (10), Const.real64 (20.25)); testMathOp (MathDiv.class, 20 / 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathDiv.class, 20L / 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathDiv.class, 10.5 / 20.25, Const.real64 (10.5), Const.real64 (20.25)); testMathOp (MathDiv.class, 10 / 20.25, Const.int32 (10), Const.real64 (20.25)); // div by 0 testMathOp (MathDiv.class, BOTTOM, Const.int32 (10), Const.int32 (0)); testMathOp (MathDiv.class, BOTTOM, Const.int64 (10), Const.int64 (0)); testMathOp (MathDiv.class, Double.POSITIVE_INFINITY, Const.real64 (10), Const.real64 (0)); testMathOp (MathMod.class, BOTTOM, Const.int32 (20), Const.int32 (0)); testMathOp (MathMod.class, Double.NaN, Const.real64 (20), Const.real64 (0)); // modulo div testMathOp (MathMod.class, 20 % 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathMod.class, 20L % 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathMod.class, 10.5 % 20.25, Const.real64 (10.5), Const.real64 (20.25)); testMathOp (MathMod.class, 10 % 20.25, Const.int32 (10), Const.real64 (20.25)); // bitwise ops testMathOp (MathBitAnd.class, 20 & 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathBitAnd.class, 20L & 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathBitAnd.class, 20L & 30, Const.int64 (20), Const.int32 (30)); testMathOp (MathBitOr.class, 20 | 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathBitOr.class, 20L | 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathBitOr.class, 20L | 30, Const.int64 (20), Const.int32 (30)); testMathOp (MathBitXor.class, 20 ^ 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathBitXor.class, 20L ^ 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathBitXor.class, 20L ^ 30, Const.int64 (20), Const.int32 (30)); testMathOp (MathBitShiftLeft.class, 20 << 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathBitShiftLeft.class, 20L << 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathBitShiftLeft.class, 20L << 30, Const.int64 (20), Const.int32 (30)); testMathOp (MathBitShiftRight.class, 20 >> 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathBitShiftRight.class, 20L >> 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathBitShiftRight.class, 20L >> 30, Const.int64 (20), Const.int32 (30)); testMathOp (MathBitLogShiftRight.class, 20 >>> 30, Const.int32 (20), Const.int32 (30)); testMathOp (MathBitLogShiftRight.class, 20L >>> 30L, Const.int64 (20), Const.int64 (30)); testMathOp (MathBitLogShiftRight.class, 20L >>> 30, Const.int64 (20), Const.int32 (30)); // bitwise complement ~ Map ntfn = new HashMap (); ntfn.put ("string", "string"); MathBitInvert invert; invert = new MathBitInvert (Const.int32 (10)); assertEquals (~10, invert.evaluate (ntfn)); invert = new MathBitInvert (Const.int64 (1234567890L)); assertEquals (~1234567890L, invert.evaluate (ntfn)); invert = new MathBitInvert (new Field ("string")); assertEquals (null, invert.evaluate (ntfn)); // unary minus MathUnaryMinus unaryMinus = new MathUnaryMinus (Const.int32 (42)); assertEquals (-42, unaryMinus.evaluate (ntfn)); unaryMinus = new MathUnaryMinus (Const.int32 (-42)); assertEquals (42, unaryMinus.evaluate (ntfn)); unaryMinus = new MathUnaryMinus (Const.int32 (0)); assertEquals (0, unaryMinus.evaluate (ntfn)); unaryMinus = new MathUnaryMinus (Const.int64 (123)); assertEquals (-123l, unaryMinus.evaluate (ntfn)); unaryMinus = new MathUnaryMinus (Const.real64 (3.14)); assertEquals (-3.14, unaryMinus.evaluate (ntfn)); unaryMinus = new MathUnaryMinus (new Field ("string")); assertEquals (null, unaryMinus.evaluate (ntfn)); } /** * Test that the Compare node handles upconverting numeric children. */ @Test public void numericPromotion () { Const thirtyTwoLong = new Const (32L); Const thirtyTwoInt = new Const (32); Const tenInt = new Const (10); Const piDouble = new Const (3.1415); Compare compare; // 32L < 10 compare = new Compare (thirtyTwoLong, tenInt, -1, false); assertEquals (FALSE, compare.evaluate (new HashMap ())); // 10 > 32L compare = new Compare (tenInt, thirtyTwoLong, -1, false); assertEquals (TRUE, compare.evaluate (new HashMap ())); // 10 > pi compare = new Compare (tenInt, piDouble, 1, false); assertEquals (TRUE, compare.evaluate (new HashMap ())); // 32 > pi compare = new Compare (thirtyTwoLong, piDouble, 1, false); assertEquals (TRUE, compare.evaluate (new HashMap ())); // 32 == 32L compare = new Compare (thirtyTwoInt, thirtyTwoLong, 0, true); assertEquals (TRUE, compare.evaluate (new HashMap ())); } /** * Test inlining of constant sub-expressions. NOTE: this depends on * the parser to generate AST's. */ @Test public void constantExpressions () throws ParseException { // test reduction to constant assertReducesTo ("1 == 1", "true"); assertReducesTo ("1 != 1", "false"); assertReducesTo ("1 != 1", "false"); assertReducesTo ("10 > 9", "true"); assertReducesTo ("!(10 > 9)", "false"); assertReducesTo ("1 == 1 ^^ 10 > 9", "false"); assertReducesTo ("1 != 1 || 2 != 2 || 3 != 3", "false"); assertReducesTo ("1 == 1 && 2 == 2 && 3 == 3", "true"); assertReducesTo ("! ! (1 == 1)", "true"); // test AND/OR erasure of constant non-contributing subtree assertReducesTo ("field == 5 && 10 > 9", "(== (field 'field') 5)"); assertReducesTo ("field == 5 || 10 < 9", "(== (field 'field') 5)"); assertReducesTo ("field == 5 || !(10 > 9)", "(== (field 'field') 5)"); // test redundant constants are removed from AND/OR assertReducesTo ("field == 5 || 9 > 10 || field == 10", "(|| (== (field 'field') 5) (== (field 'field') 10))"); assertReducesTo ("field == 5 && 10 > 9 && field == 10", "(&& (== (field 'field') 5) (== (field 'field') 10))"); // predicate functions assertReducesTo ("fold-case ('HellO')", "'hello'"); assertReducesTo ("decompose ('\u00C4\uFB03n')", "'A\u0308\uFB03n'"); assertReducesTo ("decompose-compat ('\u00C4\uFB03n')", "'A\u0308ffin'"); // some math ops assertReducesTo ("-10", "-10"); assertReducesTo ("1 + 1", "2"); assertReducesTo ("2 * 4.5", "9.0"); assertReducesTo ("0xFF & 0xF0", "240"); assertReducesTo ("0x0F << 4", "240"); } /** * Test a math operator node. * * @param opType The operator type. * @param correct The correct answer. * @param number1 Number parameter 1 * @param number2 Number parameter 1 */ private static void testMathOp (Class opType, Object correct, Node number1, Node number2) throws Exception { Node op = opType.getConstructor (Node.class, Node.class).newInstance (number1, number2); Map ntfn = new HashMap (); ntfn.put ("string", "string"); assertEquals (correct, op.evaluate (ntfn)); } /** * Create a list from an array of nodes. */ private static List argsList (Node... args) { ArrayList argArray = new ArrayList (args.length); for (Node node : args) argArray.add (node); return argArray; } /** * Test a predicate operator node. * * @param type The node type. * @param arg1 Argument 1. * @param arg2 Argument 2. * @param answer The correct answer. */ private static void testPred (Class type, String arg1, String arg2, Boolean answer) throws Exception { Node node = type.getConstructor (Node.class, Const.class).newInstance (new Const (arg1), new Const (arg2)); assertEquals (answer, node.evaluate (EMPTY_NOTIFICATION)); } private static void assertReducesTo (String subExpr, String treeExpr) throws ParseException { // System.out.println ("tree: " + // Nodes.unparse (parse (subExpr).expandConstants ())); assertEquals (treeExpr, Nodes.unparse (parse (subExpr).inlineConstants ())); } private static Node parse (String expr) throws org.avis.subscription.parser.ParseException { return new SubscriptionParser (new StringReader (expr)).parse (); } /** * Check that an operator matches its truth table entry. * * @param opType Operator type. * @param a Left value * @param b Right value * @param correct Correct result */ private static void checkBooleanOp (Class opType, Boolean a, Boolean b, Boolean correct) throws Exception { NODE node = newLogicNodeInstance (opType, a, b); Object result = node.evaluate (EMPTY_NOTIFICATION); assertEquals ("Truth table check failed:" + node.name () + " (" + a + ", " + b + ") == " + correct + ": was " + result, correct, result); } private static void checkBooleanOp (Class opType, Boolean a, Boolean b, Boolean c, Boolean correct) throws Exception { NODE node = newLogicNodeInstance (opType, a, b, c); Object result = node.evaluate (EMPTY_NOTIFICATION); assertEquals ("Truth table check failed:" + node.name () + " (" + a + ", " + b + ", " + c + ") == " + correct + ": was " + result, correct, result); } /** * Create a new node instance for nodes taking two fixed value * boolean children. */ private static NODE newLogicNodeInstance (Class nodeType, Boolean a, Boolean b) throws Exception { Constructor c = nodeType.getConstructor (Node.class, Node.class); return c.newInstance (new Const (a), new Const (b)); } private static NODE newLogicNodeInstance (Class nodeType, Boolean a, Boolean b, Boolean c) throws Exception { Constructor constructor = nodeType.getConstructor (Collection.class); ArrayList children = new ArrayList (3); children.add (new Const (a)); children.add (new Const (b)); children.add (new Const (c)); return constructor.newInstance (children); } /** * Use the Compare node to compare two numbers. */ private Boolean compare (int n1, int n2, int inequality, boolean equality) { return (Boolean)new Compare (new Const (n1), new Const (n2), inequality, equality).evaluate (EMPTY_NOTIFICATION); } /** * Generate an AST that tests AND, OR, NOT, plus various comparisons * * name == "Matt" && (age < 20 || age >= 50) && ! blah == "frob" */ private static Node logicOpExpr1 () { return new And (new Compare (field ("name"), new Const ("Matt"), 0, true), new Or (new Compare (field ("age"), new Const (20), -1, false), new Compare (field ("age"), new Const (50), 1, true)), new Not (new Compare (field ("blah"), new Const ("frob"), 0, true))); } /** * Generate an AST that tests XOR. * * name == "Matt" ^^ age == 30"; */ private static Node logicOpExpr2 () { return new Xor (new Compare (field ("name"), new Const ("Matt"), 0, true), new Compare (field ("age"), new Const (30), 0, true)); } private static Node field (String name) { return new Field (name); } } avis-1.2.2/server/src/main/0000755000175000017500000000000011147555544015340 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/0000755000175000017500000000000011147555544016127 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/0000755000175000017500000000000011147555544017071 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/federation/0000755000175000017500000000000011147555544021211 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/federation/FederationClasses.java0000644000175000017500000001007411147555544025454 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.net.InetAddress; import java.net.UnknownHostException; import org.avis.io.InetAddressFilter; import org.avis.util.Filter; import org.avis.util.Pair; import static java.lang.String.CASE_INSENSITIVE_ORDER; /** * Defines a mapping from remote hosts to the FederationClass that * should be applied to them. * * @see FederationClass * * @author Matthew Phillips */ public class FederationClasses { private FederationClass defaultClass; private Map classes; private List, FederationClass>> hostToClass; public FederationClasses () { this (new FederationClass ()); } /** * Create a new instance. * * @param defaultClass The default federation class. */ public FederationClasses (FederationClass defaultClass) { this.defaultClass = defaultClass; this.classes = new TreeMap (CASE_INSENSITIVE_ORDER); this.hostToClass = new ArrayList,FederationClass>> (); } public void setDefaultClass (FederationClass newDefaultClass) { defaultClass = newDefaultClass; } public FederationClass defaultClass () { return defaultClass; } /** * Find an existing federation class with the given name or create a * new one. */ public FederationClass define (String name) { FederationClass fedClass = classes.get (name); if (fedClass == null) { fedClass = new FederationClass (); fedClass.name = name; classes.put (name, fedClass); } return fedClass; } /** * Get the federation class mapped to a given host. * * @param address The host's address. * * @return The federation class, or defaultClass if no explicit * mapping found. */ public FederationClass classFor (InetAddress address) { for (Pair, FederationClass> entry : hostToClass) { if (entry.item1.matches (address)) return entry.item2; } return defaultClass; } /** * Get the federation class mapped to a given host. * * @param hostName The host's canonical name. * * @return The federation class, or defaultClass if no explicit * mapping found. */ public FederationClass classFor (String hostName) { try { return classFor (InetAddress.getByName (hostName)); } catch (UnknownHostException ex) { throw new IllegalArgumentException (ex.getMessage ()); } } /** * Map a host pattern to a federation class. * * @param matcher A host-matching filter. * @param fedClass The federation class to use for matching hosts. */ public void map (Filter matcher, FederationClass fedClass) { hostToClass.add (new Pair, FederationClass> (matcher, fedClass)); } /** * Map a wildcard host pattern to a federation class. * * @param host A wildcard host-matching pattern. * @param fedClass The federation class to use for matching hosts. */ public void map (String host, FederationClass fedClass) { map (new InetAddressFilter (host), fedClass); } /** * Clear all mappings and classes. */ public void clear () { classes.clear (); hostToClass.clear (); } }avis-1.2.2/server/src/main/org/avis/federation/Federation.java0000644000175000017500000000656511147555544024150 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.io.IOException; import org.apache.mina.common.IoSession; import org.apache.mina.common.WriteFuture; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.Message; import org.avis.router.Router; import static org.avis.logging.Log.TRACE; import static org.avis.logging.Log.diagnostic; import static org.avis.logging.Log.shouldLog; import static org.avis.logging.Log.trace; import static org.avis.logging.Log.warn; import static org.avis.util.Text.idFor; /** * General federation definitions and methods. * * @author Matthew Phillips */ public final class Federation { public static final int DEFAULT_EWAF_PORT = 2916; public static final int VERSION_MAJOR = 1; public static final int VERSION_MINOR = 0; private Federation () { // zip } /** * Send a message with logging when trace enabled. * * @param session The IO session. * @param serverDomain The server domain. * @param message The message. * * @return The IO future. */ public static WriteFuture send (IoSession session, String serverDomain, Message message) { if (shouldLog (TRACE)) { trace ("Federation session " + idFor (session) + " sent " + message.name (), Federation.class); } return session.write (message); } /** * Log a trace on message receipt. */ public static void logMessageReceived (Message message, IoSession session, Object source) { if (shouldLog (TRACE)) { trace ("Federation session " + idFor (session) + " received " + message.name (), source); } } /** * Log info about an error message from the frame codec. */ public static void logError (ErrorMessage message, Object source) { warn ("Error in federation packet", source, message.error); } /** * Called by IO handlers to log MINA exceptions. */ public static void logMinaException (Throwable cause, Object source) { if (cause instanceof IOException) { diagnostic ("I/O exception while processing federation message", source, cause); } else { warn ("Unexpected exception while processing federation message", source, cause); } } public static void logSessionOpened (IoSession session, String direction, Object source) { if (shouldLog (TRACE)) { trace ("Federation session " + idFor (session) + " opened for " + direction + " connection on " + session.getServiceAddress () + (Router.isSecure (session) ? " (using TLS)" : ""), source); } } } avis-1.2.2/server/src/main/org/avis/federation/FederationManager.java0000644000175000017500000002373111147555544025435 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import java.io.IOException; import java.lang.management.ManagementFactory; import org.avis.config.Options; import org.avis.router.CloseListener; import org.avis.router.Router; import org.avis.subscription.ast.Node; import org.avis.util.IllegalConfigOptionException; import static java.lang.Integer.toHexString; import static java.lang.System.identityHashCode; import static java.util.Collections.emptySet; import static org.avis.common.ElvinURI.defaultProtocol; import static org.avis.common.ElvinURI.secureProtocol; import static org.avis.io.Net.localHostName; import static org.avis.subscription.ast.nodes.Const.CONST_TRUE; import static org.avis.util.Text.shortException; /** * Constructs the federation setup by reading Federation.* * configuration items from a config and setting up federation * classes, listeners and acceptors to match. * * @author Matthew Phillips */ public class FederationManager implements CloseListener { protected Router router; protected FederationClasses classes; protected Acceptor acceptor; protected List connectors; public FederationManager (Router router, Options federationConfig) throws IllegalConfigOptionException, IOException { this.router = router; String serverDomain = initServerDomain (federationConfig); classes = initClasses (federationConfig); initAddAttributes (federationConfig, classes); connectors = initConnectors (router, serverDomain, classes, federationConfig); acceptor = initAcceptor (router, serverDomain, classes, federationConfig); router.addCloseListener (this); } /** * Find the federation manager for a router. * * @param router The router. * @return The last federation manager created for the router. * * @throws IllegalArgumentException if no manager found. */ public static FederationManager federationManagerFor (Router router) { for (Object listener : router.closeListeners ()) { if (listener instanceof FederationManager) return (FederationManager)listener; } throw new IllegalArgumentException ("No federation manager"); } public Acceptor acceptor () { return acceptor; } public Collection connectors () { return connectors; } public Set listenURIs () { if (acceptor == null) return emptySet (); else return acceptor.listenURIs (); } public void routerClosing (Router theRouter) { close (); } public void close () { router.removeCloseListener (this); if (acceptor != null) acceptor.close (); for (Connector connector : connectors) connector.close (); acceptor = null; connectors = null; } public boolean isClosed () { return connectors == null; } @SuppressWarnings("unchecked") private static List initConnectors (Router router, String serverDomain, FederationClasses classes, Options config) throws IllegalConfigOptionException, IOException { Map> connect = (Map>)config.getParamOption ("Federation.Connect"); // check federation classes and URI's make sense for (Entry> entry : connect.entrySet ()) { FederationClass fedClass = classes.define (entry.getKey ()); if (fedClass.allowsNothing ()) { throw new IllegalConfigOptionException ("Federation.Connect[" + entry.getKey () + "]", "No federation subscribe/provide defined: " + "this connection cannot import or export any notifications"); } for (EwafURI uri : entry.getValue ()) checkUri ("Federation.Connect[" + entry.getKey () + "]", uri); } List connectors = new ArrayList (connect.size ()); for (Entry> entry : connect.entrySet ()) { FederationClass fedClass = classes.define (entry.getKey ()); for (EwafURI uri : entry.getValue ()) { connectors.add (new Connector (router, serverDomain, uri, fedClass, config)); } } return connectors; } private String initServerDomain (Options federationConfig) { String domain = federationConfig.getString ("Federation.Router-Name"); if (domain.length () == 0) { try { domain = defaultServerDomain (); } catch (IOException ex) { throw new IllegalConfigOptionException ("Federation.Router-Name", "Cannot auto detect default router name, " + "please set this manually: " + shortException (ex)); } } return domain; } /** * Do the best we can to guess a good server domain based on * identity hashcode.PID.hostname. */ private String defaultServerDomain () throws IOException { String instanceId = toHexString (identityHashCode (this)); String runtimeName = ManagementFactory.getRuntimeMXBean ().getName (); /* * RuntimeMXBean.getName () returns pid@hostname on many VM's: if * it looks like this is the case, use it otherwise fall back on * hashcode + hostname. */ if (runtimeName.matches ("\\d+@.+")) return instanceId + '.' + runtimeName; else return instanceId + '@' + localHostName (); } @SuppressWarnings("unchecked") private static Acceptor initAcceptor (Router router, String serverDomain, FederationClasses classes, Options config) { Set uris = (Set)config.get ("Federation.Listen"); if (uris.isEmpty ()) { return null; } else { try { for (EwafURI uri : uris) checkUri ("Federation.Listen", uri); return new Acceptor (router, serverDomain, classes, uris, config); } catch (IOException ex) { throw new IllegalConfigOptionException ("Federation.Listen", shortException (ex)); } } } @SuppressWarnings("unchecked") private static FederationClasses initClasses (Options federationConfig) { FederationClasses classes = new FederationClasses (); Map provide = federationConfig.getParamOption ("Federation.Provide"); for (Entry entry : provide.entrySet ()) { FederationClass fedClass = classes.define (entry.getKey ()); fedClass.outgoingFilter = (Node)entry.getValue (); } Map subscribe = federationConfig.getParamOption ("Federation.Subscribe"); for (Entry entry : subscribe.entrySet ()) { Node incomingFilter = (Node)entry.getValue (); /* * Cannot sub TRUE right now. When we support 1.1-level * federation this will be possible as CONST_TRUE will be * &&'d with the current consolidated subscription. */ if (incomingFilter == CONST_TRUE) { throw new IllegalConfigOptionException ("Federation.Subscribe[" + entry.getKey () + "]", "Federation with TRUE is not currently supported"); } classes.define (entry.getKey ()).incomingFilter = incomingFilter; } Map applyClass = federationConfig.getParamOption ("Federation.Apply-Class"); for (Entry entry : applyClass.entrySet ()) { FederationClass fedClass = classes.define (entry.getKey ()); for (String hostPatterns : (Set)entry.getValue ()) { // compatibility with Avis 1.1 if (hostPatterns.contains ("@")) hostPatterns = hostPatterns.replaceAll ("@", ""); classes.map (hostPatterns, fedClass); } } classes.setDefaultClass (classes.define (federationConfig.getString ("Federation.Default-Class"))); return classes; } @SuppressWarnings("unchecked") private static void initAddAttributes (Options config, FederationClasses classes) { Map incoming = config.getParamOption ("Federation.Add-Incoming-Attribute"); for (Entry entry : incoming.entrySet ()) { FederationClass fedClass = classes.define (entry.getKey ()); fedClass.incomingAttributes = (Map)entry.getValue (); } Map outgoing = config.getParamOption ("Federation.Add-Outgoing-Attribute"); for (Entry entry : outgoing.entrySet ()) { FederationClass fedClass = classes.define (entry.getKey ()); fedClass.outgoingAttributes = (Map)entry.getValue (); } } private static void checkUri (String option, EwafURI uri) { if (!uri.protocol.equals (defaultProtocol ()) && !uri.protocol.equals (secureProtocol ())) { throw new IllegalConfigOptionException (option, "Avis only supports " + defaultProtocol () + " and " + secureProtocol () + " protocols: " + uri); } } } avis-1.2.2/server/src/main/org/avis/federation/Link.java0000644000175000017500000002677311147555544022770 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.Map; import org.apache.mina.common.IoSession; import org.apache.mina.common.WriteFuture; import org.avis.federation.io.messages.Ack; import org.avis.federation.io.messages.FedNotify; import org.avis.federation.io.messages.FedSubReplace; import org.avis.io.messages.Disconn; import org.avis.io.messages.DropWarn; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.LivenessFailureMessage; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.Notify; import org.avis.io.messages.RequestMessage; import org.avis.io.messages.RequestTimeoutMessage; import org.avis.router.NotifyListener; import org.avis.router.Router; import org.avis.security.Keys; import org.avis.subscription.ast.Node; import static java.lang.System.arraycopy; import static java.util.Arrays.asList; import static org.apache.mina.common.IoFutureListener.CLOSE; import static org.avis.federation.Federation.logError; import static org.avis.io.messages.Disconn.REASON_PROTOCOL_VIOLATION; import static org.avis.io.messages.Disconn.REASON_SHUTDOWN; import static org.avis.logging.Log.TRACE; import static org.avis.logging.Log.internalError; import static org.avis.logging.Log.shouldLog; import static org.avis.logging.Log.trace; import static org.avis.logging.Log.warn; import static org.avis.subscription.ast.nodes.Const.CONST_FALSE; import static org.avis.util.Collections.union; import static org.avis.util.Text.className; import static org.avis.util.Text.idFor; /** * A bi-directional federation link between two endpoints, typically * established by either a {@link Acceptor} or {@link Connector}. * * @author Matthew Phillips */ public class Link implements NotifyListener { /** * Internal disconnection reason code indicating we're shutting down * on a Disconn request from the remote host. */ private static final int REASON_DISCONN_REQUESTED = -1; /** * Internal disconnection reason code indicating we're shutting down * because the remote federator vetoed a request. */ private static final int REASON_REQUEST_REJECTED = -2; /** * Internal disconnection reason code indicating remote federator failed * a liveness check. */ private static final int REASON_FEDERATOR_NOT_RESPONDING = -3; private static final String [] EMPTY_ROUTING = new String [0]; private Router router; private IoSession session; private FederationClass federationClass; private String serverDomain; private String remoteServerDomain; private String remoteHostName; private Node remotePullFilter; private boolean subscribed; private volatile boolean closed; public Link (Router router, IoSession session, FederationClass federationClass, String serverDomain, String remoteServerDomain, String remoteHostName) { this.session = session; this.router = router; this.federationClass = federationClass; this.serverDomain = serverDomain; this.remoteServerDomain = remoteServerDomain; this.remoteHostName = remoteHostName; this.remotePullFilter = CONST_FALSE; this.subscribed = false; subscribe (); if (federationClass.outgoingFilter != CONST_FALSE) router.addNotifyListener (this); } public IoSession session () { return session; } public String remoteServerDomain () { return remoteServerDomain; } /** * True if the link is fully initialised from its POV. This is * automatically true for links with a null pull filter, and true * for links with a non-null pull filter that has been ACK'd by the * remote. */ public boolean isLive () { return federationClass.incomingFilter == CONST_FALSE || subscribed; } public boolean isClosed () { return closed; } /** * True if this link closed the session rather than the remote host. */ public boolean initiatedSessionClose () { return session.containsAttribute ("linkClosed"); } /** * Close the link and the session after sending a Disconn. */ public void close () { close (REASON_SHUTDOWN); } private void close (int reason) { close (reason, ""); } private void close (int reason, String message) { if (closed) return; closed = true; router.removeNotifyListener (this); if (session.isConnected ()) { session.setAttribute ("linkClosed"); // just close session for internal codes if (reason < 0) session.close (); else send (new Disconn (reason, message)).addListener (CLOSE); } } private void subscribe () { if (federationClass.incomingFilter != CONST_FALSE) send (new FedSubReplace (federationClass.incomingFilter)); } /** * Called by router when a notification is delivered. */ public void notifyReceived (Notify message, Keys keys) { String [] routing = routingFor (message); Map attributes = union (message.attributes, federationClass.outgoingAttributes); if (shouldPush (routing, attributes)) { send (new FedNotify (message, attributes, keys.addedTo (message.keys), serverDomainAddedTo (routing))); } } /** * Test if we should push a notification to the remote router given * its routing and attributes. */ private boolean shouldPush (String [] routing, Map attributes) { return !containsDomain (routing, remoteServerDomain) && matches (federationClass.outgoingFilter, attributes) && matches (remotePullFilter, attributes); } /** * Test if we should pull a notification sent by the remote router. */ private boolean shouldPull (FedNotify message) { return !containsDomain (message.routing, serverDomain) && matches (federationClass.incomingFilter, message.attributes); } public void handleMessage (Message message) { switch (message.typeId ()) { case FedSubReplace.ID: handleFedSubReplace ((FedSubReplace)message); break; case FedNotify.ID: handleFedNotify ((FedNotify)message); break; case Disconn.ID: close (REASON_DISCONN_REQUESTED); break; case DropWarn.ID: handleDropWarn (); break; case Nack.ID: handleNack ((Nack)message); break; case Ack.ID: handleAck ((Ack)message); break; case RequestTimeoutMessage.ID: handleRequestTimeout (((RequestTimeoutMessage)message).request); break; case LivenessFailureMessage.ID: handleLivenessFailure (); break; case ErrorMessage.ID: handleError ((ErrorMessage)message); break; default: handleProtocolViolation ("Unexpected " + message.name ()); } } private void handleDropWarn () { warn ("Remote federator sent a dropped packet warning: " + "a message may have been discarded due to congestion", this); } private void handleRequestTimeout (RequestMessage request) { if (request.getClass () == FedSubReplace.class) { warn ("Federation subscription request to remote federator at " + remoteHostName + " timed out: retrying", this); subscribe (); } else { // this shouldn't happen, FedSubReplace is the only request we send internalError ("Request timeout for unexpected message type: " + className (request), this); } } private void handleLivenessFailure () { warn ("Remote federator at " + remoteHostName + " has stopped responding", this); close (REASON_FEDERATOR_NOT_RESPONDING); } private void handleError (ErrorMessage message) { logError (message, this); handleProtocolViolation (message.error.getMessage ()); } private void handleAck (Ack ack) { if (ack.request.getClass () == FedSubReplace.class) subscribed = true; } private void handleNack (Nack nack) { warn ("Disconnecting from remote federator at " + remoteHostName + " " + "after it rejected a " + nack.request.name (), this); close (REASON_REQUEST_REJECTED); } private void handleFedSubReplace (FedSubReplace message) { remotePullFilter = message.incomingFilter.inlineConstants (); send (new Ack (message)); } private void handleFedNotify (FedNotify message) { if (shouldLog (TRACE)) { trace ("Federator for domain \"" + serverDomain + "\" " + "FedNotify: routing=" + asList (message.routing), this); } if (containsDomain (message.routing, remoteServerDomain)) { message.attributes = union (message.attributes, federationClass.incomingAttributes); if (shouldPull (message)) { router.injectNotify (message); } else { if (shouldLog (TRACE)) trace ("Quenching federated message " + idFor (message), this); } } else { warn ("Closing federation link on receipt of FedNotify from remote " + "federator that has not added its own domain (" + remoteServerDomain + ") to the routing list: " + asList (message.routing), this); handleProtocolViolation ("Remote server domain was not in FedNotify routing list"); } } private void handleProtocolViolation (String message) { warn ("Disconnecting remote federator at " + remoteHostName + " " + "due to protocol violation: " + message, this); close (REASON_PROTOCOL_VIOLATION, message); } private WriteFuture send (Message message) { return Federation.send (session, serverDomain, message); } /** * Test if a given message matches an AST filter. */ private static boolean matches (Node filter, Map attributes) { return filter.evaluate (attributes) == Node.TRUE; } /** * The routing list for a given notification. */ private static String [] routingFor (Notify message) { if (message instanceof FedNotify) return ((FedNotify)message).routing; else return EMPTY_ROUTING; } /** * True if the routing list contains a given server domain. */ private static boolean containsDomain (String [] routing, String serverDomain) { for (String domain : routing) { if (domain.equals (serverDomain)) return true; } return false; } /** * Return a routing list with the federator's local server domain added. */ private String [] serverDomainAddedTo (String [] routing) { String [] newRouting = new String [routing.length + 1]; newRouting [0] = serverDomain; arraycopy (routing, 0, newRouting, 1, routing.length); return newRouting; } } avis-1.2.2/server/src/main/org/avis/federation/io/0000755000175000017500000000000011147555544021620 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/federation/io/XdrAstType.java0000644000175000017500000000317311147555544024536 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io; /** * Enumeration of Elvin AST node type codes. See client spec section * 8.2.3.4. * * @author Matthew Phillips */ final class XdrAstType { public static final int EMPTY = 0, NAME = 1, CONST_INT32 = 2, CONST_INT64 = 3, CONST_REAL64 = 4, CONST_STRING = 5, REGEXP = 6, EQUALS = 8, NOT_EQUALS = 9, LESS_THAN = 10, LESS_THAN_EQUALS = 11, GREATER_THAN = 12, GREATER_THAN_EQUALS = 13, OR = 16, XOR = 17, AND = 18, NOT = 19, UNARY_PLUS = 24, UNARY_MINUS = 25, MULTIPLY = 26, DIVIDE = 27, MODULO = 28, ADD = 29, SUBTRACT = 30 , SHIFT_LEFT = 32, SHIFT_RIGHT = 33, LOGICAL_SHIFT_RIGHT = 34, BIT_AND = 35, BIT_XOR = 36, BIT_OR = 37, BIT_NEGATE = 38, INT32 = 40, INT64 = 41, REAL64 = 42, STRING = 43, OPAQUE = 44, NAN = 45, BEGINS_WITH = 48, CONTAINS = 49, ENDS_WITH = 50, WILDCARD = 51, REGEX = 52, FOLD_CASE = 56, DECOMPOSE = 61, DECOMPOSE_COMPAT = 62, REQUIRE = 64, F_EQUALS = 65, SIZE = 66; } avis-1.2.2/server/src/main/org/avis/federation/io/XdrAstParser.java0000644000175000017500000003170011147555544025046 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io; import java.util.ArrayList; import java.util.List; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.subscription.ast.IllegalChildException; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.nodes.And; import org.avis.subscription.ast.nodes.Compare; import org.avis.subscription.ast.nodes.Const; import org.avis.subscription.ast.nodes.Field; import org.avis.subscription.ast.nodes.MathBitAnd; import org.avis.subscription.ast.nodes.MathBitInvert; import org.avis.subscription.ast.nodes.MathBitLogShiftRight; import org.avis.subscription.ast.nodes.MathBitOr; import org.avis.subscription.ast.nodes.MathBitShiftLeft; import org.avis.subscription.ast.nodes.MathBitShiftRight; import org.avis.subscription.ast.nodes.MathBitXor; import org.avis.subscription.ast.nodes.MathDiv; import org.avis.subscription.ast.nodes.MathMinus; import org.avis.subscription.ast.nodes.MathMod; import org.avis.subscription.ast.nodes.MathMult; import org.avis.subscription.ast.nodes.MathPlus; import org.avis.subscription.ast.nodes.MathUnaryMinus; import org.avis.subscription.ast.nodes.Nan; import org.avis.subscription.ast.nodes.Not; import org.avis.subscription.ast.nodes.Or; import org.avis.subscription.ast.nodes.Require; import org.avis.subscription.ast.nodes.Size; import org.avis.subscription.ast.nodes.StrBeginsWith; import org.avis.subscription.ast.nodes.StrContains; import org.avis.subscription.ast.nodes.StrEndsWith; import org.avis.subscription.ast.nodes.StrFoldCase; import org.avis.subscription.ast.nodes.StrRegex; import org.avis.subscription.ast.nodes.StrUnicodeDecompose; import org.avis.subscription.ast.nodes.StrWildcard; import org.avis.subscription.ast.nodes.Type; import org.avis.subscription.ast.nodes.Xor; import static org.avis.federation.io.XdrAstType.ADD; import static org.avis.federation.io.XdrAstType.AND; import static org.avis.federation.io.XdrAstType.BEGINS_WITH; import static org.avis.federation.io.XdrAstType.BIT_AND; import static org.avis.federation.io.XdrAstType.BIT_NEGATE; import static org.avis.federation.io.XdrAstType.BIT_OR; import static org.avis.federation.io.XdrAstType.BIT_XOR; import static org.avis.federation.io.XdrAstType.CONST_INT32; import static org.avis.federation.io.XdrAstType.CONST_INT64; import static org.avis.federation.io.XdrAstType.CONST_REAL64; import static org.avis.federation.io.XdrAstType.CONST_STRING; import static org.avis.federation.io.XdrAstType.CONTAINS; import static org.avis.federation.io.XdrAstType.DECOMPOSE; import static org.avis.federation.io.XdrAstType.DECOMPOSE_COMPAT; import static org.avis.federation.io.XdrAstType.DIVIDE; import static org.avis.federation.io.XdrAstType.EMPTY; import static org.avis.federation.io.XdrAstType.ENDS_WITH; import static org.avis.federation.io.XdrAstType.EQUALS; import static org.avis.federation.io.XdrAstType.FOLD_CASE; import static org.avis.federation.io.XdrAstType.F_EQUALS; import static org.avis.federation.io.XdrAstType.GREATER_THAN; import static org.avis.federation.io.XdrAstType.GREATER_THAN_EQUALS; import static org.avis.federation.io.XdrAstType.INT32; import static org.avis.federation.io.XdrAstType.INT64; import static org.avis.federation.io.XdrAstType.LESS_THAN; import static org.avis.federation.io.XdrAstType.LESS_THAN_EQUALS; import static org.avis.federation.io.XdrAstType.LOGICAL_SHIFT_RIGHT; import static org.avis.federation.io.XdrAstType.MODULO; import static org.avis.federation.io.XdrAstType.MULTIPLY; import static org.avis.federation.io.XdrAstType.NAME; import static org.avis.federation.io.XdrAstType.NAN; import static org.avis.federation.io.XdrAstType.NOT; import static org.avis.federation.io.XdrAstType.NOT_EQUALS; import static org.avis.federation.io.XdrAstType.OPAQUE; import static org.avis.federation.io.XdrAstType.OR; import static org.avis.federation.io.XdrAstType.REAL64; import static org.avis.federation.io.XdrAstType.REGEX; import static org.avis.federation.io.XdrAstType.REGEXP; import static org.avis.federation.io.XdrAstType.REQUIRE; import static org.avis.federation.io.XdrAstType.SHIFT_LEFT; import static org.avis.federation.io.XdrAstType.SHIFT_RIGHT; import static org.avis.federation.io.XdrAstType.SIZE; import static org.avis.federation.io.XdrAstType.STRING; import static org.avis.federation.io.XdrAstType.SUBTRACT; import static org.avis.federation.io.XdrAstType.UNARY_MINUS; import static org.avis.federation.io.XdrAstType.UNARY_PLUS; import static org.avis.federation.io.XdrAstType.WILDCARD; import static org.avis.federation.io.XdrAstType.XOR; import static org.avis.io.XdrCoding.TYPE_INT32; import static org.avis.io.XdrCoding.TYPE_INT64; import static org.avis.io.XdrCoding.TYPE_REAL64; import static org.avis.io.XdrCoding.TYPE_STRING; import static org.avis.io.XdrCoding.getString; import static org.avis.subscription.ast.nodes.Const.CONST_FALSE; import static org.avis.util.Text.className; /** * Parser class for translating XDR-encoded AST's into Node-based AST's. * * @see XdrAstCoding#decodeAST(ByteBuffer) * * @author Matthew Phillips */ class XdrAstParser { private ByteBuffer in; public XdrAstParser (ByteBuffer in) { this.in = in; } /** * Read any AST expression. * * @return The root node of the expression. * * @throws ProtocolCodecException if an error occurs in decoding the * AST. * @throws IllegalChildException if a child in the AST is not a * valid type, i.e. AST is syntactically invalid. */ public Node expr () throws ProtocolCodecException, IllegalChildException { int type = in.getInt (); if (type == EMPTY) return CONST_FALSE; int leafType = in.getInt (); // sanity check leaf type for parent nodes if (type > CONST_STRING && leafType != 0) { throw new ProtocolCodecException ("Invalid leaf type for parent node: " + leafType); } switch (type) { case NAME: case CONST_INT32: case CONST_INT64: case CONST_REAL64: case CONST_STRING: return leaf (type, leafType); case REGEXP: return StrRegex.create (children ()); case EQUALS: return new Compare (binary ().expr (), expr (), 0, true); case NOT_EQUALS: return new Not (new Compare (binary ().expr (), expr (), 0, true)); case LESS_THAN: return new Compare (binary ().expr (), expr (), -1, false); case LESS_THAN_EQUALS: return new Compare (binary ().expr (), expr (), -1, true); case GREATER_THAN: return new Compare (binary ().expr (), expr (), 1, false); case GREATER_THAN_EQUALS: return new Compare (binary ().expr (), expr (), 1, true); case OR: return new Or (children ()); case XOR: return new Xor (children ()); case AND: return new And (children ()); case NOT: return new Not (single ().expr ()); case UNARY_PLUS: return single ().expr (); case UNARY_MINUS: return new MathUnaryMinus (single ().expr ()); case MULTIPLY: return new MathMult (binary ().expr (), expr ()); case DIVIDE: return new MathDiv (binary ().expr (), expr ()); case MODULO: return new MathMod (binary ().expr (), expr ()); case ADD: return new MathPlus (binary ().expr (), expr ()); case SUBTRACT: return new MathMinus (binary ().expr (), expr ()); case SHIFT_LEFT: return new MathBitShiftLeft (binary ().expr (), expr ()); case SHIFT_RIGHT: return new MathBitShiftRight (binary ().expr (), expr ()); case LOGICAL_SHIFT_RIGHT: return new MathBitLogShiftRight (binary ().expr (), expr ()); case BIT_AND: return new MathBitAnd (binary ().expr (), expr ()); case BIT_XOR: return new MathBitXor (binary ().expr (), expr ()); case BIT_OR: return new MathBitOr (binary ().expr (), expr ()); case BIT_NEGATE: return new MathBitInvert (single ().expr ()); case INT32: return new Type (single ().field (), Integer.class); case INT64: return new Type (single ().field (), Long.class); case REAL64: return new Type (single ().field (), Double.class); case STRING: return new Type (single ().field (), String.class); case OPAQUE: return new Type (single ().field (), byte [].class); case NAN: return new Nan (single ().field ()); case BEGINS_WITH: return StrBeginsWith.create (children ()); case CONTAINS: return StrContains.create (children ()); case ENDS_WITH: return StrEndsWith.create (children ()); case WILDCARD: return StrWildcard.create (children ()); case REGEX: return StrRegex.create (children ()); case FOLD_CASE: return new StrFoldCase (single ().expr ()); case DECOMPOSE: return new StrUnicodeDecompose (single ().expr (), StrUnicodeDecompose.Mode.DECOMPOSE); case DECOMPOSE_COMPAT: return new StrUnicodeDecompose (single ().expr (), StrUnicodeDecompose.Mode.DECOMPOSE_COMPAT); case REQUIRE: return new Require (single ().field ()); case F_EQUALS: return Compare.createEquals (children ()); case SIZE: return new Size (single ().field ()); default: throw new ProtocolCodecException ("Unknown AST node type: " + type); } } /** * Assert that a single child is found and return this, otherwise * throw an exception. Used as a predicate for single-child nodes. */ private XdrAstParser single () throws ProtocolCodecException { int count = in.getInt (); if (count == 1) return this; else throw new ProtocolCodecException ("Expected single child, found " + count); } /** * Assert that two children are found and return this, otherwise * throw an exception. Used as a predicate for binary nodes. */ private XdrAstParser binary () throws ProtocolCodecException { int count = in.getInt (); if (count == 2) return this; else throw new ProtocolCodecException ("Expected two children, found " + count); } /** * Read a list of child nodes of any length. */ private List children () throws ProtocolCodecException { int count = in.getInt (); if (count < 0) throw new ProtocolCodecException ("Child count cannot be negative: " + count); List children = new ArrayList (count); for ( ; count > 0; count--) children.add (expr ()); return children; } private Field field () throws ProtocolCodecException { Node node = expr (); if (node instanceof Field) return (Field)node; else throw new ProtocolCodecException ("Field node required, found " + className (node)); } /** * Read a leaf node of a given type and leaf type. * * @param type The node type. * @param leafType The type of value contained in the leaf: must be * one of the XdrCoding.TYPE_* values. * @return The node. * * @throws ProtocolCodecException if the node was not valid constant * node of the specified type. */ private Node leaf (int type, int leafType) throws ProtocolCodecException { switch (type) { case NAME: assertLeafType (leafType, TYPE_STRING); return new Field (getString (in)); case CONST_STRING: assertLeafType (leafType, TYPE_STRING); return new Const (getString (in)); case CONST_INT32: assertLeafType (leafType, TYPE_INT32); return new Const (in.getInt ()); case CONST_INT64: assertLeafType (leafType, TYPE_INT64); return new Const (in.getLong ()); case CONST_REAL64: assertLeafType (leafType, TYPE_REAL64); return new Const (in.getDouble ()); default: throw new Error (); } } /** * Check that a node type matches an actual required value. */ private static void assertLeafType (int required, int actual) throws ProtocolCodecException { if (required != actual) { throw new ProtocolCodecException ("Leaf node has incorrect value type: " + actual); } } } avis-1.2.2/server/src/main/org/avis/federation/io/XdrAstCoding.java0000644000175000017500000002514711147555544025025 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.subscription.ast.IllegalChildException; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.nodes.And; import org.avis.subscription.ast.nodes.Compare; import org.avis.subscription.ast.nodes.Const; import org.avis.subscription.ast.nodes.Field; import org.avis.subscription.ast.nodes.MathBitAnd; import org.avis.subscription.ast.nodes.MathBitInvert; import org.avis.subscription.ast.nodes.MathBitLogShiftRight; import org.avis.subscription.ast.nodes.MathBitOr; import org.avis.subscription.ast.nodes.MathBitShiftLeft; import org.avis.subscription.ast.nodes.MathBitShiftRight; import org.avis.subscription.ast.nodes.MathBitXor; import org.avis.subscription.ast.nodes.MathDiv; import org.avis.subscription.ast.nodes.MathMinus; import org.avis.subscription.ast.nodes.MathMod; import org.avis.subscription.ast.nodes.MathMult; import org.avis.subscription.ast.nodes.MathPlus; import org.avis.subscription.ast.nodes.MathUnaryMinus; import org.avis.subscription.ast.nodes.Nan; import org.avis.subscription.ast.nodes.Not; import org.avis.subscription.ast.nodes.Or; import org.avis.subscription.ast.nodes.Require; import org.avis.subscription.ast.nodes.Size; import org.avis.subscription.ast.nodes.StrBeginsWith; import org.avis.subscription.ast.nodes.StrContains; import org.avis.subscription.ast.nodes.StrEndsWith; import org.avis.subscription.ast.nodes.StrFoldCase; import org.avis.subscription.ast.nodes.StrRegex; import org.avis.subscription.ast.nodes.StrUnicodeDecompose; import org.avis.subscription.ast.nodes.StrWildcard; import org.avis.subscription.ast.nodes.Type; import org.avis.subscription.ast.nodes.Xor; import static org.avis.federation.io.XdrAstType.ADD; import static org.avis.federation.io.XdrAstType.AND; import static org.avis.federation.io.XdrAstType.BEGINS_WITH; import static org.avis.federation.io.XdrAstType.BIT_AND; import static org.avis.federation.io.XdrAstType.BIT_NEGATE; import static org.avis.federation.io.XdrAstType.BIT_OR; import static org.avis.federation.io.XdrAstType.BIT_XOR; import static org.avis.federation.io.XdrAstType.CONST_INT32; import static org.avis.federation.io.XdrAstType.CONST_INT64; import static org.avis.federation.io.XdrAstType.CONST_REAL64; import static org.avis.federation.io.XdrAstType.CONST_STRING; import static org.avis.federation.io.XdrAstType.CONTAINS; import static org.avis.federation.io.XdrAstType.DECOMPOSE; import static org.avis.federation.io.XdrAstType.DECOMPOSE_COMPAT; import static org.avis.federation.io.XdrAstType.DIVIDE; import static org.avis.federation.io.XdrAstType.EMPTY; import static org.avis.federation.io.XdrAstType.ENDS_WITH; import static org.avis.federation.io.XdrAstType.FOLD_CASE; import static org.avis.federation.io.XdrAstType.F_EQUALS; import static org.avis.federation.io.XdrAstType.GREATER_THAN; import static org.avis.federation.io.XdrAstType.GREATER_THAN_EQUALS; import static org.avis.federation.io.XdrAstType.INT32; import static org.avis.federation.io.XdrAstType.INT64; import static org.avis.federation.io.XdrAstType.LESS_THAN; import static org.avis.federation.io.XdrAstType.LESS_THAN_EQUALS; import static org.avis.federation.io.XdrAstType.LOGICAL_SHIFT_RIGHT; import static org.avis.federation.io.XdrAstType.MODULO; import static org.avis.federation.io.XdrAstType.MULTIPLY; import static org.avis.federation.io.XdrAstType.NAME; import static org.avis.federation.io.XdrAstType.NAN; import static org.avis.federation.io.XdrAstType.NOT; import static org.avis.federation.io.XdrAstType.OPAQUE; import static org.avis.federation.io.XdrAstType.OR; import static org.avis.federation.io.XdrAstType.REAL64; import static org.avis.federation.io.XdrAstType.REGEX; import static org.avis.federation.io.XdrAstType.REQUIRE; import static org.avis.federation.io.XdrAstType.SHIFT_LEFT; import static org.avis.federation.io.XdrAstType.SHIFT_RIGHT; import static org.avis.federation.io.XdrAstType.SIZE; import static org.avis.federation.io.XdrAstType.STRING; import static org.avis.federation.io.XdrAstType.SUBTRACT; import static org.avis.federation.io.XdrAstType.UNARY_MINUS; import static org.avis.federation.io.XdrAstType.WILDCARD; import static org.avis.federation.io.XdrAstType.XOR; import static org.avis.io.XdrCoding.TYPE_INT32; import static org.avis.io.XdrCoding.TYPE_INT64; import static org.avis.io.XdrCoding.TYPE_REAL64; import static org.avis.io.XdrCoding.TYPE_STRING; import static org.avis.io.XdrCoding.putString; import static org.avis.util.Text.className; /** * Functions for encoding/decoding Node-based AST's into the Elvin XDR * wire format. * * @author Matthew Phillips */ public final class XdrAstCoding { private static Map, Integer> typeCodes; static { typeCodes = new HashMap, Integer> (); typeCodes.put (And.class, AND); typeCodes.put (MathBitAnd.class, BIT_AND); typeCodes.put (MathBitInvert.class, BIT_NEGATE); typeCodes.put (MathBitLogShiftRight.class, LOGICAL_SHIFT_RIGHT); typeCodes.put (MathBitOr.class, BIT_OR); typeCodes.put (MathBitShiftLeft.class, SHIFT_LEFT); typeCodes.put (MathBitShiftRight.class, SHIFT_RIGHT); typeCodes.put (MathBitXor.class, BIT_XOR); typeCodes.put (MathDiv.class, DIVIDE); typeCodes.put (MathMinus.class, SUBTRACT); typeCodes.put (MathMod.class, MODULO); typeCodes.put (MathMult.class, MULTIPLY); typeCodes.put (MathPlus.class, ADD); typeCodes.put (MathUnaryMinus.class, UNARY_MINUS); typeCodes.put (Nan.class, NAN); typeCodes.put (Not.class, NOT); typeCodes.put (Or.class, OR); typeCodes.put (Require.class, REQUIRE); typeCodes.put (Size.class, SIZE); typeCodes.put (StrBeginsWith.class, BEGINS_WITH); typeCodes.put (StrContains.class, CONTAINS); typeCodes.put (StrEndsWith.class, ENDS_WITH); typeCodes.put (StrFoldCase.class, FOLD_CASE); typeCodes.put (StrRegex.class, REGEX); typeCodes.put (StrWildcard.class, WILDCARD); typeCodes.put (Xor.class, XOR); } private XdrAstCoding () { // zip } /** * Encode an AST in Elvin XDR format. * * @param out The buffer to encode to. * @param node The root of the AST. * * @see #decodeAST(ByteBuffer) */ public static void encodeAST (ByteBuffer out, Node node) throws ProtocolCodecException { if (node instanceof Const) { encodeConst (out, (Const)node); } else if (node instanceof Field) { out.putInt (NAME); out.putInt (TYPE_STRING); putString (out, ((Field)node).fieldName ()); } else { out.putInt (typeCodeFor (node)); out.putInt (0); // composite node base type is 0 Collection children = node.children (); out.putInt (children.size ()); for (Node child : children) encodeAST (out, child); } } /** * Generate the AST type code for a node, taking into account cases * where there is not a 1-1 mapping from Node -> Elvin AST node type. */ private static int typeCodeFor (Node node) { if (node instanceof Compare) { Compare compare = (Compare)node; switch (compare.inequality) { case 0: return F_EQUALS; case -1: return compare.equality ? LESS_THAN_EQUALS : LESS_THAN; case 1: return compare.equality ? GREATER_THAN_EQUALS : GREATER_THAN; default: throw new Error (); } } else if (node instanceof Type) { Class type = ((Type)node).type; if (type == String.class) return STRING; else if (type == Integer.class) return INT32; else if (type == Long.class) return INT64; else if (type == Double.class) return REAL64; else if (type == byte [].class) return OPAQUE; else throw new Error (); } else if (node instanceof StrUnicodeDecompose) { StrUnicodeDecompose decompose = (StrUnicodeDecompose)node; return decompose.mode == StrUnicodeDecompose.Mode.DECOMPOSE ? DECOMPOSE : DECOMPOSE_COMPAT; } else { // this will NPE if we hit an unmapped type return typeCodes.get (node.getClass ()); } } /** * Encode a constant value (leaf) node. */ private static void encodeConst (ByteBuffer out, Const node) throws ProtocolCodecException { Object value = node.value (); Class type = value.getClass (); if (type == String.class) { out.putInt (CONST_STRING); out.putInt (TYPE_STRING); putString (out, (String)value); } else if (type == Integer.class) { out.putInt (CONST_INT32); out.putInt (TYPE_INT32); out.putInt ((Integer)value); } else if (type == Long.class) { out.putInt (CONST_INT64); out.putInt (TYPE_INT64); out.putLong ((Long)value); } else if (type == Double.class) { out.putInt (CONST_REAL64); out.putInt (TYPE_REAL64); out.putDouble ((Double)value); } else if (type == Boolean.class) { if ((Boolean)node.value () == false) out.putInt (EMPTY); else throw new ProtocolCodecException ("Cannot encode TRUE in AST"); } else { throw new ProtocolCodecException ("Cannot encode constant type " + className (type)); } } /** * Decode an XDR-encoded AST into a Node-based AST. * * @param in The buffer to read from. * * @return The root of the AST. * * @throws ProtocolCodecException if an error occurred reading the tree. * * @see #encodeAST(ByteBuffer, Node) */ public static Node decodeAST (ByteBuffer in) throws ProtocolCodecException { try { return new XdrAstParser (in).expr (); } catch (IllegalChildException ex) { throw new ProtocolCodecException ("Invalid AST: " + ex.getMessage ()); } } } avis-1.2.2/server/src/main/org/avis/federation/io/FederationFrameCodec.java0000644000175000017500000000541111147555544026455 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io; import org.apache.mina.common.IoFilter; import org.apache.mina.filter.codec.ProtocolCodecException; import org.apache.mina.filter.codec.ProtocolCodecFactory; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.ProtocolDecoder; import org.apache.mina.filter.codec.ProtocolEncoder; import org.avis.federation.io.messages.Ack; import org.avis.federation.io.messages.FedConnRply; import org.avis.federation.io.messages.FedConnRqst; import org.avis.federation.io.messages.FedNotify; import org.avis.federation.io.messages.FedSubReplace; import org.avis.io.FrameCodec; import org.avis.io.messages.ConfConn; import org.avis.io.messages.Disconn; import org.avis.io.messages.DropWarn; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.TestConn; public class FederationFrameCodec extends FrameCodec implements ProtocolCodecFactory { private static final FederationFrameCodec INSTANCE = new FederationFrameCodec (); public static final IoFilter FILTER = new ProtocolCodecFilter (INSTANCE); public ProtocolEncoder getEncoder () throws Exception { return INSTANCE; } public ProtocolDecoder getDecoder () throws Exception { return INSTANCE; } @Override protected Message newMessage (int messageType, int frameSize) throws ProtocolCodecException { switch (messageType) { case Nack.ID: return new Nack (); case Disconn.ID: return new Disconn (); case Ack.ID: return new Ack (); case FedConnRply.ID: return new FedConnRply (); case FedConnRqst.ID: return new FedConnRqst (); case FedSubReplace.ID: return new FedSubReplace (); case FedNotify.ID: return new FedNotify (); case TestConn.ID: return TestConn.INSTANCE; case ConfConn.ID: return ConfConn.INSTANCE; case DropWarn.ID: return new DropWarn (); default: throw new ProtocolCodecException ("Unknown message type: ID = " + messageType); } } } avis-1.2.2/server/src/main/org/avis/federation/io/messages/0000755000175000017500000000000011147555544023427 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/federation/io/messages/FedSubReplace.java0000644000175000017500000000340311147555544026736 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.io.messages.RequestMessage; import org.avis.subscription.ast.Node; import static org.avis.federation.io.XdrAstCoding.decodeAST; import static org.avis.federation.io.XdrAstCoding.encodeAST; public class FedSubReplace extends RequestMessage { public static final int ID = 194; public Node incomingFilter; public FedSubReplace () { // zip } public FedSubReplace (Node incomingFilter) { super (nextXid ()); this.incomingFilter = incomingFilter; } @Override public Class replyType () { return Ack.class; } @Override public int typeId () { return ID; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); encodeAST (out, incomingFilter); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); incomingFilter = decodeAST (in); } } avis-1.2.2/server/src/main/org/avis/federation/io/messages/FedConnRply.java0000644000175000017500000000315311147555544026457 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.io.messages.XidMessage; import static org.avis.io.XdrCoding.getString; import static org.avis.io.XdrCoding.putString; public class FedConnRply extends XidMessage { public static final int ID = 193; public String serverDomain; public FedConnRply () { // zip } public FedConnRply (FedConnRqst request, String serverDomain) { super (request); this.serverDomain = serverDomain; } @Override public int typeId () { return ID; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); putString (out, serverDomain); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); serverDomain = getString (in); } } avis-1.2.2/server/src/main/org/avis/federation/io/messages/FedConnRqst.java0000644000175000017500000000375611147555544026473 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io.messages; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.io.messages.RequestMessage; import static org.avis.io.XdrCoding.getString; import static org.avis.io.XdrCoding.putString; public class FedConnRqst extends RequestMessage { public static final int ID = 192; public int versionMajor; public int versionMinor; public String serverDomain; public FedConnRqst () { // zip } public FedConnRqst (int versionMajor, int versionMinor, String serverDomain) { super (nextXid ()); this.versionMajor = versionMajor; this.versionMinor = versionMinor; this.serverDomain = serverDomain; } @Override public Class replyType () { return FedConnRply.class; } @Override public int typeId () { return ID; } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putInt (versionMajor); out.putInt (versionMinor); putString (out, serverDomain); } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); versionMajor = in.getInt (); versionMinor = in.getInt (); serverDomain = getString (in); } } avis-1.2.2/server/src/main/org/avis/federation/io/messages/Ack.java0000644000175000017500000000203711147555544024772 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io.messages; import org.avis.io.messages.XidMessage; public class Ack extends XidMessage { public static final int ID = 65; public Ack () { // zip } public Ack (XidMessage inReplyTo) { super (inReplyTo); } public Ack (int xid) { super (xid); } @Override public int typeId () { return ID; } } avis-1.2.2/server/src/main/org/avis/federation/io/messages/FedNotify.java0000644000175000017500000000430011147555544026156 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation.io.messages; import java.util.Map; import org.apache.mina.common.ByteBuffer; import org.apache.mina.filter.codec.ProtocolCodecException; import org.avis.io.messages.Notify; import org.avis.security.Keys; import static org.avis.io.XdrCoding.getStringArray; import static org.avis.io.XdrCoding.putStringArray; public class FedNotify extends Notify { public static final int ID = 195; public String [] routing; public FedNotify () { // zip } public FedNotify (Object... attributes) { super (attributes); } public FedNotify (Map attributes, boolean deliverInsecure, Keys keys, String [] routing) { super (attributes, deliverInsecure, keys); this.routing = routing; } public FedNotify (Notify original, String [] routing) { this (original.attributes, original.deliverInsecure, original.keys, routing); } public FedNotify (Notify original, Map attributes, Keys keys, String [] routing) { this (attributes, original.deliverInsecure, keys, routing); } @Override public int typeId () { return ID; } @Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); routing = getStringArray (in); } @Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); putStringArray (out, routing); } } avis-1.2.2/server/src/main/org/avis/federation/Connector.java0000644000175000017500000002741411147555544024016 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.Timer; import java.util.TimerTask; import java.io.Closeable; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.mina.common.DefaultIoFilterChainBuilder; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoFuture; import org.apache.mina.common.IoFutureListener; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.RuntimeIOException; import org.apache.mina.common.ThreadModel; import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.avis.config.Options; import org.avis.federation.io.FederationFrameCodec; import org.avis.federation.io.messages.FedConnRply; import org.avis.federation.io.messages.FedConnRqst; import org.avis.io.LivenessFilter; import org.avis.io.RequestTrackingFilter; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.RequestMessage; import org.avis.io.messages.RequestTimeoutMessage; import org.avis.router.Router; import org.avis.util.Filter; import org.avis.util.IllegalConfigOptionException; import static org.avis.federation.Federation.VERSION_MAJOR; import static org.avis.federation.Federation.VERSION_MINOR; import static org.avis.federation.Federation.logError; import static org.avis.federation.Federation.logMessageReceived; import static org.avis.federation.Federation.logMinaException; import static org.avis.federation.Federation.logSessionOpened; import static org.avis.io.FrameCodec.setMaxFrameLengthFor; import static org.avis.io.Net.hostIdFor; import static org.avis.logging.Log.diagnostic; import static org.avis.logging.Log.info; import static org.avis.logging.Log.internalError; import static org.avis.logging.Log.warn; import static org.avis.util.Text.className; import static org.avis.util.Text.shortException; /** * Initiates a federation connection to a remote host. After * connection and successfully handshaking with a FedConnRqst, it * creates and hands over processing to a Link. * * @see Link * @see Acceptor * * @author Matthew Phillips */ public class Connector implements IoHandler, Closeable { private EwafURI uri; private Options options; private Router router; private SocketConnector connector; private SocketConnectorConfig connectorConfig; private InetSocketAddress remoteAddress; private IoSession session; private String serverDomain; private FederationClass federationClass; private Link link; private Timer asyncConnectTimer; protected volatile boolean closing; @SuppressWarnings("unchecked") public Connector (Router router, String serverDomain, EwafURI uri, FederationClass federationClass, Options options) throws IllegalConfigOptionException, IOException { this.router = router; this.uri = uri; this.serverDomain = serverDomain; this.federationClass = federationClass; this.options = options; this.connector = new SocketConnector (1, router.executor ()); this.connectorConfig = new SocketConnectorConfig (); this.remoteAddress = new InetSocketAddress (uri.host, uri.port); long requestTimeout = options.getInt ("Federation.Request-Timeout") * 1000; long keepaliveInterval = options.getInt ("Federation.Keepalive-Interval") * 1000; /* Change the worker timeout to make the I/O thread quit soon * when there's no connection to manage. */ connector.setWorkerTimeout (0); connectorConfig.setThreadModel (ThreadModel.MANUAL); connectorConfig.setConnectTimeout ((int)(requestTimeout / 1000)); DefaultIoFilterChainBuilder filters = new DefaultIoFilterChainBuilder (); filters.addLast ("codec", FederationFrameCodec.FILTER); filters.addLast ("requestTracker", new RequestTrackingFilter (requestTimeout)); filters.addLast ("liveness", new LivenessFilter (keepaliveInterval, requestTimeout)); Filter authRequired = (Filter)options.get ("Federation.Require-Authenticated"); if (uri.isSecure ()) filters = router.createSecureFilters (filters, authRequired, true); else filters = router.createStandardFilters (filters, authRequired); connectorConfig.setFilterChainBuilder (filters); connect (); } public Link link () { return link; } public boolean isWaitingForAsyncConnection () { return asyncConnectTimer != null; } /** * True if the link has been connected plus successful handshake and * initial subscription. */ public boolean isConnected () { return link != null && link.isLive (); } /** * Kick off a connection attempt. */ synchronized void connect () { closing = false; cancelAsyncConnect (); connector.connect (remoteAddress, this, connectorConfig).addListener (new IoFutureListener () { public void operationComplete (IoFuture future) { connectFutureComplete (future); } }); } /** * Called by future created by connect () when complete. */ protected void connectFutureComplete (IoFuture future) { if (closing) return; try { if (!future.isReady ()) { diagnostic ("Connection attempt to federator at " + uri + " timed out, retrying", this); asyncConnect (); } else { open (future.getSession ()); } } catch (RuntimeIOException ex) { diagnostic ("Failed to connect to federator at " + uri + ", retrying: " + shortException (ex.getCause ()), this, ex); asyncConnect (); } } /** * Schedule a delayed connect () in Federation.Request-Timeout * seconds from now. * * @see #cancelAsyncConnect() */ synchronized void asyncConnect () { if (asyncConnectTimer != null) throw new Error (); asyncConnectTimer = new Timer ("Federation connector"); TimerTask connectTask = new TimerTask () { @Override public void run () { synchronized (Connector.this) { if (!closing) connect (); } } }; asyncConnectTimer.schedule (connectTask, options.getInt ("Federation.Request-Timeout") * 1000L); } /** * Canncel an async connect and its associated timer. * * @see #asyncConnect() */ private void cancelAsyncConnect () { if (asyncConnectTimer != null) { asyncConnectTimer.cancel (); asyncConnectTimer = null; } } /** * Called to open a federation connection when a connection and * session has been established. */ synchronized void open (IoSession newSession) { this.session = newSession; cancelAsyncConnect (); send (new FedConnRqst (VERSION_MAJOR, VERSION_MINOR, serverDomain)); } public void close () { synchronized (this) { closing = true; cancelAsyncConnect (); if (session != null) { if (session.isConnected ()) { if (link != null) link.close (); // closes session after disconn else session.close (); } else { if (link != null && !link.initiatedSessionClose ()) { warn ("Remote federator at " + uri + " " + "closed outgoing link with no warning", this); link.close (); } } session = null; } link = null; } } private void reopen () { if (closing) return; close (); diagnostic ("Scheduling reconnection for outgoing federation link to " + uri, this); closing = false; asyncConnect (); } private void handleMessage (Message message) { switch (message.typeId ()) { case FedConnRply.ID: createFederationLink (((FedConnRply)message).serverDomain); break; case Nack.ID: handleFedConnNack ((Nack)message); break; case RequestTimeoutMessage.ID: handleRequestTimeout (((RequestTimeoutMessage)message).request); break; case ErrorMessage.ID: logError ((ErrorMessage)message, this); close (); break; default: warn ("Unexpected message during handshake from remote federator at " + uri + ": " + message.name (), this); reopen (); } } private void handleRequestTimeout (RequestMessage request) { if (request.getClass () == FedConnRqst.class) { warn ("Federation connection request to remote federator at " + uri + " timed out, reconnecting", this); reopen (); } else { // this shouldn't happen, FedConnRqst is the only request we send internalError ("Request timeout for unexpected message type: " + className (request), this); } } private void handleFedConnNack (Nack nack) { warn ("Closing connection to remote router at " + uri + " after it rejected federation connect request: " + nack.formattedMessage (), this); reopen (); } private void createFederationLink (String remoteServerDomain) { info ("Federation outgoing link for " + uri + " established with \"" + hostIdFor (session) + "\", remote server domain \"" + remoteServerDomain + "\"", this); link = new Link (router, session, federationClass, serverDomain, remoteServerDomain, remoteAddress.getAddress ().getCanonicalHostName ()); } private void send (Message message) { Federation.send (session, serverDomain, message); } // IoHandler public void sessionOpened (IoSession theSession) throws Exception { logSessionOpened (theSession, "outgoing", this); } public void sessionClosed (IoSession theSession) throws Exception { info ("Federation link for " + uri + " with \"" + hostIdFor (theSession) + "\" disconnected", this); reopen (); } public void sessionCreated (IoSession theSession) throws Exception { setMaxFrameLengthFor (theSession, options.getInt ("Packet.Max-Length")); } public void messageReceived (IoSession theSession, Object theMessage) throws Exception { if (closing) return; Message message = (Message)theMessage; logMessageReceived (message, theSession, this); if (link == null) handleMessage (message); else if (!link.isClosed ()) link.handleMessage (message); } public void messageSent (IoSession theSession, Object message) throws Exception { // zip } public void sessionIdle (IoSession theSession, IdleStatus status) throws Exception { // zip } public void exceptionCaught (IoSession theSession, Throwable cause) throws Exception { logMinaException (cause, this); } } avis-1.2.2/server/src/main/org/avis/federation/FederationClass.java0000644000175000017500000000521511147555544025125 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.Map; import org.avis.subscription.ast.Node; import org.avis.subscription.parser.ParseException; import org.avis.subscription.parser.SubscriptionParserBase; import static java.util.Collections.emptyMap; import static org.avis.subscription.ast.nodes.Const.CONST_FALSE; import static org.avis.subscription.ast.nodes.Const.CONST_TRUE; /** * Defines a federation class which controls which notifications to * export and import from a router. * * @see FederationClasses * * @author Matthew Phillips */ public class FederationClass { public String name; public Node incomingFilter; public Node outgoingFilter; public Map incomingAttributes; public Map outgoingAttributes; /** * Create a new instance with both filters set to false. */ public FederationClass () { this (CONST_FALSE, CONST_FALSE); } public FederationClass (String incomingFilterExpr, String outgoingFilterExpr) throws ParseException { this (parse (incomingFilterExpr), parse (outgoingFilterExpr)); } public FederationClass (Node incomingFilter, Node outgoingFilter) { this.incomingFilter = incomingFilter; this.outgoingFilter = outgoingFilter; this.incomingAttributes = emptyMap (); this.outgoingAttributes = emptyMap (); } /** * True if this class neither exports nor imports anything. */ public boolean allowsNothing () { return incomingFilter == CONST_FALSE && outgoingFilter == CONST_FALSE; } /** * Parse a subscription expression, allowing TRUE and FALSE to stand for * CONST_TRUE and CONST_FALSE. */ public static Node parse (String subExpr) throws ParseException { subExpr = subExpr.trim (); if (subExpr.equalsIgnoreCase ("true")) { return CONST_TRUE; } else if (subExpr.equalsIgnoreCase ("false")) { return CONST_FALSE; } else { return SubscriptionParserBase.parse (subExpr); } } } avis-1.2.2/server/src/main/org/avis/federation/FederationOptionSet.java0000644000175000017500000000701111147555544026000 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import org.avis.config.OptionSet; import org.avis.config.OptionType; import org.avis.config.OptionTypeFromString; import org.avis.config.OptionTypeParam; import org.avis.config.OptionTypeSet; import org.avis.config.OptionTypeValueExpr; import org.avis.io.InetAddressFilter; import org.avis.subscription.ast.Node; import org.avis.subscription.parser.ParseException; import org.avis.util.Filter; import org.avis.util.IllegalConfigOptionException; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static org.avis.federation.FederationClass.parse; import static org.avis.router.ConnectionOptionSet.CONNECTION_OPTION_SET; /** * Configuration option set for Avis federation. * * @author Matthew Phillips */ public class FederationOptionSet extends OptionSet { public static final OptionSet OPTION_SET = new FederationOptionSet (); protected FederationOptionSet () { OptionTypeParam fedClassOption = new OptionTypeParam (new SubExpOption ()); OptionTypeParam attrOption = new OptionTypeParam (new OptionTypeValueExpr (), 2); OptionTypeSet setOfURI = new OptionTypeSet (EwafURI.class); add ("Federation.Activated", false); add ("Federation.Router-Name", ""); add ("Federation.Subscribe", fedClassOption, emptyMap ()); add ("Federation.Provide", fedClassOption, emptyMap ()); add ("Federation.Apply-Class", new OptionTypeParam (new OptionTypeSet (String.class)), emptyMap ()); add ("Federation.Default-Class", ""); add ("Federation.Connect", new OptionTypeParam (setOfURI), emptyMap ()); add ("Federation.Listen", setOfURI, emptySet ()); add ("Federation.Router-Name", ""); add ("Federation.Add-Incoming-Attribute", attrOption, emptyMap ()); add ("Federation.Add-Outgoing-Attribute", attrOption, emptyMap ()); add ("Federation.Request-Timeout", 1, 20, Integer.MAX_VALUE); add ("Federation.Keepalive-Interval", 1, 60, Integer.MAX_VALUE); add ("Federation.Require-Authenticated", new OptionTypeFromString (InetAddressFilter.class), Filter.MATCH_NONE); // allow connection options such as Packet.Max-Length inheritFrom (CONNECTION_OPTION_SET); } /** * A subscription expression option. */ static class SubExpOption extends OptionType { @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { try { if (value instanceof Node) return value; else return parse (value.toString ()); } catch (ParseException ex) { throw new IllegalConfigOptionException (option, "Invalid subscription: " + ex.getMessage ()); } } @Override public String validate (String option, Object value) { return validateType (value, Node.class); } } }avis-1.2.2/server/src/main/org/avis/federation/EwafURI.java0000644000175000017500000000274711147555544023330 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import org.avis.common.ElvinURI; import org.avis.common.InvalidURIException; import static org.avis.federation.Federation.DEFAULT_EWAF_PORT; import static org.avis.federation.Federation.VERSION_MAJOR; import static org.avis.federation.Federation.VERSION_MINOR; /** * A URI specifying an Elvin wide-area federation endpoint. * * @author Matthew Phillips */ public class EwafURI extends ElvinURI { public EwafURI (String uri) throws InvalidURIException { super (uri); } @Override protected boolean validScheme (String schemeToCheck) { return schemeToCheck.equals ("ewaf"); } @Override protected void init () { super.init (); this.versionMajor = VERSION_MAJOR; this.versionMinor = VERSION_MINOR; this.port = DEFAULT_EWAF_PORT; } } avis-1.2.2/server/src/main/org/avis/federation/Acceptor.java0000644000175000017500000003005311147555544023615 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.federation; import java.util.HashSet; import java.util.Set; import java.io.Closeable; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.mina.common.DefaultIoFilterChainBuilder; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.WriteFuture; import org.apache.mina.transport.socket.nio.SocketAcceptor; import org.avis.config.Options; import org.avis.federation.io.FederationFrameCodec; import org.avis.federation.io.messages.FedConnRply; import org.avis.federation.io.messages.FedConnRqst; import org.avis.io.LivenessFilter; import org.avis.io.RequestTrackingFilter; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.router.Router; import org.avis.util.Filter; import static org.apache.mina.common.IdleStatus.READER_IDLE; import static org.apache.mina.common.IoFutureListener.CLOSE; import static org.avis.federation.Federation.VERSION_MAJOR; import static org.avis.federation.Federation.VERSION_MINOR; import static org.avis.federation.Federation.logError; import static org.avis.federation.Federation.logMessageReceived; import static org.avis.federation.Federation.logMinaException; import static org.avis.federation.Federation.logSessionOpened; import static org.avis.io.FrameCodec.setMaxFrameLengthFor; import static org.avis.io.Net.addressesFor; import static org.avis.io.Net.hostIdFor; import static org.avis.io.Net.remoteHostAddressFor; import static org.avis.io.messages.Nack.PROT_INCOMPAT; import static org.avis.logging.Log.DIAGNOSTIC; import static org.avis.logging.Log.diagnostic; import static org.avis.logging.Log.info; import static org.avis.logging.Log.shouldLog; import static org.avis.logging.Log.warn; /** * Listens for incoming federation connections and establishes links * for them. After accepting a connection and successfully handshaking * with a FedConnRqst/FedConnRply, it creates and hands over * processing to a Link. * * @see Link * @see Connector * * @author Matthew Phillips */ public class Acceptor implements IoHandler, Closeable { /** * Avis-specific NACK code used when an invalid server domain is * detected (NACK codes in range 2500-2599 are allocated for * implementation-specific use). */ public static final int INVALID_DOMAIN = 2500; protected Router router; protected Options options; protected String serverDomain; protected FederationClasses federationClasses; protected Set listenUris; protected Set links; protected Set listenAddresses; protected volatile boolean closing; @SuppressWarnings("unchecked") public Acceptor (Router router, String serverDomain, FederationClasses federationClasses, Set uris, Options options) throws IOException { this.router = router; this.serverDomain = serverDomain; this.federationClasses = federationClasses; this.listenUris = uris; this.listenAddresses = addressesFor (uris); this.options = options; this.links = new HashSet (); long requestTimeout = options.getInt ("Federation.Request-Timeout") * 1000; long keepaliveInterval = options.getInt ("Federation.Keepalive-Interval") * 1000; DefaultIoFilterChainBuilder filters = new DefaultIoFilterChainBuilder (); filters.addLast ("codec", FederationFrameCodec.FILTER); filters.addLast ("requestTracker", new RequestTrackingFilter (requestTimeout)); filters.addLast ("liveness", new LivenessFilter (keepaliveInterval, requestTimeout)); router.bind (uris, this, filters, (Filter)options.get ("Federation.Require-Authenticated")); if (shouldLog (DIAGNOSTIC)) { for (EwafURI uri : uris) { diagnostic ("Federator listening on " + uri + " " + addressesFor (uri), this); } } } public synchronized void close () { if (closing) return; closing = true; for (Link link : links) link.close (); links.clear (); SocketAcceptor socketAcceptor = router.socketAcceptor (); for (InetSocketAddress address : listenAddresses) { // wait for pending messages to be written // todo check that this really flushes messages for (IoSession session : socketAcceptor.getManagedSessions (address)) session.close ().join (10000); socketAcceptor.unbind (address); } } public Set listenURIs () { return listenUris; } public Set listenAddresses () { return listenAddresses; } /** * Simulate a hang by stopping all responses to messages. */ public void hang () { for (InetSocketAddress address : listenAddresses) { for (IoSession session : router.socketAcceptor ().getManagedSessions (address)) { session.getFilterChain ().remove ("requestTracker"); session.getFilterChain ().remove ("liveness"); } } closing = true; } private void handleMessage (IoSession session, Message message) { switch (message.typeId ()) { case FedConnRqst.ID: handleFedConnRqst (session, (FedConnRqst)message); break; case ErrorMessage.ID: logError ((ErrorMessage)message, this); session.close (); break; default: warn ("Unexpected handshake message from connecting remote " + "federator at " + hostIdFor (session) + " (disconnecting): " + message.name (), this); session.close (); } } private void handleFedConnRqst (IoSession session, FedConnRqst message) { InetAddress remoteHost = remoteHostAddressFor (session); String hostName = remoteHost.getCanonicalHostName (); if (message.versionMajor != VERSION_MAJOR || message.versionMinor > VERSION_MINOR) { String disconnMessage = "Incompatible federation protocol version: " + message.versionMajor + "." + message.versionMinor + " not compatible with this federator's " + VERSION_MAJOR + "." + VERSION_MINOR; warn ("Rejected federation request from " + hostName + ": " + disconnMessage, this); send (session, new Nack (message, PROT_INCOMPAT, disconnMessage)).addListener (CLOSE); } else { Link existingLink = linkForDomain (message.serverDomain); FederationClass fedClass = federationClasses.classFor (remoteHost); if (existingLink != null) { nackInvalidDomain (session, message, "using a federation domain already in use by \"" + hostIdFor (existingLink.session ()), "Server domain " + message.serverDomain + " already in use"); } else if (fedClass.allowsNothing ()) { nackInvalidDomain (session, message, "no provide/subscribe defined for its hostname/server " + "domain", "No federation import/export allowed for host"); } else if (message.serverDomain.equalsIgnoreCase (serverDomain)) { nackInvalidDomain (session, message, "using the same server domain the remote router", "Server domain is the same as the remote router's"); } else { send (session, new FedConnRply (message, serverDomain)); info ("Federation incoming link established with \"" + hostIdFor (session) + "\", remote server domain \"" + message.serverDomain + "\", federation class \"" + fedClass.name + "\"", this); createLink (session, message.serverDomain, hostName, fedClass); } } } /** * Send a NACK for invalid server domain. */ private void nackInvalidDomain (IoSession session, FedConnRqst message, String logMessage, String nackMessage) { warn ("Remote federator has been denied connection due to " + logMessage + ", host = " + hostIdFor (session) + ", server domain = " + message.serverDomain, this); send (session, new Nack (message, INVALID_DOMAIN, nackMessage)).addListener (CLOSE); } private synchronized void createLink (IoSession session, String remoteServerDomain, String remoteHost, FederationClass federationClass) { Link link = new Link (router, session, federationClass, serverDomain, remoteServerDomain, remoteHost); session.setAttribute ("federationLink", link); links.add (link); } private synchronized void removeLink (Link link) { links.remove (link); } private synchronized Link linkForDomain (String domain) { for (Link link : links) { if (link.remoteServerDomain ().equalsIgnoreCase (domain)) return link; } return null; } private static Link linkFor (IoSession session) { return (Link)session.getAttribute ("federationLink"); } private WriteFuture send (IoSession session, Message message) { return Federation.send (session, serverDomain, message); } // IoHandler public void sessionCreated (IoSession session) throws Exception { // federators have 20 seconds to send a FedConnRqst session.setIdleTime (READER_IDLE, 20); setMaxFrameLengthFor (session, options.getInt ("Packet.Max-Length")); } public void sessionOpened (IoSession session) throws Exception { if (closing) session.close (); else logSessionOpened (session, "incoming", this); } public void messageReceived (IoSession session, Object theMessage) throws Exception { if (closing) return; Message message = (Message)theMessage; logMessageReceived (message, session, this); Link link = linkFor (session); if (link == null) handleMessage (session, message); else if (!link.isClosed ()) link.handleMessage (message); } public void sessionClosed (IoSession session) throws Exception { Link link = linkFor (session); if (link != null) { if (!link.isClosed ()) { warn ("Remote host \"" + hostIdFor (session) + "\" closed incoming federation link with no warning", this); link.close (); } else { info ("Federation link with \"" + hostIdFor (session) + "\" disconnected", this); } removeLink (link); } } public void exceptionCaught (IoSession session, Throwable cause) throws Exception { logMinaException (cause, this); } public void messageSent (IoSession session, Object message) throws Exception { // zip } public void sessionIdle (IoSession session, IdleStatus status) throws Exception { if (status == READER_IDLE && linkFor (session) == null) { warn ("Disconnecting incoming federation connection from " + hostIdFor (session) + " due to failure to send connect request", this); session.close (); } } } avis-1.2.2/server/src/main/org/avis/router/0000755000175000017500000000000011147555544020411 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/router/SubscriptionMatch.java0000644000175000017500000000320211147555544024712 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongList; /** * Represents the result of matching a subscription against a message. * * @author Matthew Phillips */ class SubscriptionMatch { private static final long [] EMPTY = new long [0]; /** Securely matched subscription ID's */ public final LongArrayList secure; /** Insecurely matched subscription ID's */ public final LongArrayList insecure; public SubscriptionMatch () { this.secure = new LongArrayList (); this.insecure = new LongArrayList (); } public long [] secure () { return toArray (secure); } public long [] insecure () { return toArray (insecure); } public boolean matched () { return !insecure.isEmpty () || !secure.isEmpty (); } private static long [] toArray (LongList ids) { if (ids.isEmpty ()) return EMPTY; else return ids.toLongArray (); } } avis-1.2.2/server/src/main/org/avis/router/CloseListener.java0000644000175000017500000000176211147555544024035 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; /** * Interface for listeners to router shutdown. * * @author Matthew Phillips */ public interface CloseListener { /** * Invoked when the router is about to shut down. * * @param router The router. * * @see Router#close() */ public void routerClosing (Router router); } avis-1.2.2/server/src/main/org/avis/router/RouterOptionSet.java0000644000175000017500000000322511147555544024403 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import org.avis.config.OptionSet; import org.avis.config.OptionTypeFromString; import org.avis.config.OptionTypeURI; import org.avis.io.InetAddressFilter; import org.avis.util.Filter; import static org.avis.common.Common.DEFAULT_PORT; import static org.avis.io.Net.uri; import static org.avis.router.ConnectionOptionSet.CONNECTION_OPTION_SET; /** * The configuration options accepted by the router. * * @author Matthew Phillips */ public class RouterOptionSet extends OptionSet { public RouterOptionSet () { add ("Port", 1, DEFAULT_PORT, 65535); add ("Listen", "elvin://0.0.0.0"); add ("IO.Idle-Connection-Timeout", 1, 15, Integer.MAX_VALUE); add ("IO.Use-Direct-Buffers", false); add ("TLS.Keystore", new OptionTypeURI (), uri ("")); add ("TLS.Keystore-Passphrase", ""); add ("Require-Authenticated", new OptionTypeFromString (InetAddressFilter.class), Filter.MATCH_NONE); inheritFrom (CONNECTION_OPTION_SET); } }avis-1.2.2/server/src/main/org/avis/router/RouterOptions.java0000644000175000017500000000542211147555544024113 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.HashSet; import java.util.Properties; import java.util.Set; import org.avis.common.ElvinURI; import org.avis.common.InvalidURIException; import org.avis.config.Options; import org.avis.util.IllegalConfigOptionException; import static org.avis.common.ElvinURI.defaultProtocol; import static org.avis.common.ElvinURI.secureProtocol; import static org.avis.util.Text.split; /** * Options used to configure the Avis router. * * @author Matthew Phillips */ public class RouterOptions extends Options { public RouterOptions () { this (new RouterOptionSet ()); } public RouterOptions (RouterOptionSet optionSet) { super (optionSet); } /** * Shortcut to create an option set with a given "Port" setting. */ public RouterOptions (int port) { this (); set ("Port", port); } /** * Shortcut to create an option set from an initial set of * properties. * * @param properties The initial settings. * * @see #setAll(Properties) */ public RouterOptions (Properties properties) { this (); setAll (properties); } /** * Generate the set of URI's the server should bind to as specified * by the Listen setting. */ public Set listenURIs () { Set uris = new HashSet (); ElvinURI defaultUri = new ElvinURI ("0.0.0.0", getInt ("Port")); for (String listenItem : split (getString ("Listen"), "\\s+")) { try { ElvinURI uri = new ElvinURI (listenItem, defaultUri); if (uri.protocol.equals (defaultProtocol ()) || uri.protocol.equals (secureProtocol ())) { uris.add (uri); } else { throw new IllegalConfigOptionException ("Listen", "Avis only supports protocols: " + defaultProtocol () + " and " + secureProtocol () + ": " + listenItem); } } catch (InvalidURIException ex) { throw new IllegalConfigOptionException ("Listen", ex.getMessage ()); } } return uris; } }avis-1.2.2/server/src/main/org/avis/router/ClientConnectionOptionSet.java0000644000175000017500000000717711147555544026373 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.HashMap; import java.util.Map; import org.avis.config.Options; import org.avis.util.IllegalConfigOptionException; import static org.avis.io.LegacyConnectionOptions.legacyToNew; /** * Extends the Avis connection option set to be used against the * options sent by router clients. Adds code to support legacy * compatibility and make value checking stricter. * * @author Matthew Phillips */ public class ClientConnectionOptionSet extends ConnectionOptionSet { public static final ClientConnectionOptionSet CLIENT_CONNECTION_OPTION_SET = new ClientConnectionOptionSet (); /** * Generate the set of accepted connection options for reporting to * a client. Handles removal of invalid options, echoing of actual * defaults for values out of range and legacy backward * compatibility. * * @param connectionOptions The connection option set. * @param requestedOptions The original requested set of options. * * @return The accepted set suitable for reporting to client as per * the client connection option spec. */ public Map accepted (Options connectionOptions, Map requestedOptions) { HashMap accepted = new HashMap (); for (String requestedOption : requestedOptions.keySet ()) { String option = legacyToNew (requestedOption); if (isDefined (option)) { Object value = connectionOptions.peek (option); if (value == null) value = defaults.get (option); /* * Special handling for old router.coalesce-delay, which has the * opposite meaning to its replacement, TCP.Send-Immediately. */ if (requestedOption.equals ("router.coalesce-delay")) { if (value.equals (0)) value = 1; else if (value.equals (1)) value = 0; } accepted.put (requestedOption, value); } } return accepted; } /** * Override validation to add legacy support and to simply not * include invalid options rather than explode violently. Also * removes auto value conversion. */ @Override protected void validateAndSet (Options options, String option, Object value) throws IllegalConfigOptionException { /* * Special handling for old router.coalesce-delay, which has the * opposite meaning to its replacement, TCP.Send-Immediately. */ if (option.equals ("router.coalesce-delay")) { if (value.equals (0)) value = 1; else if (value.equals (1)) value = 0; } option = legacyToNew (option); // validate and set, or simply discard value if (validate (option, value) == null) set (options, option, value); } } avis-1.2.2/server/src/main/org/avis/router/BlacklistFilter.java0000644000175000017500000000343111147555544024333 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.mina.common.IoFilter; import org.apache.mina.common.IoFilterAdapter; import org.apache.mina.common.IoSession; import org.avis.util.Filter; import static org.avis.logging.Log.diagnostic; /** * An IO filter that blocks hosts matching a given selection filter. * * @author Matthew Phillips */ public class BlacklistFilter extends IoFilterAdapter implements IoFilter { private Filter blacklist; public BlacklistFilter (Filter blacklist) { this.blacklist = blacklist; } @Override public void sessionOpened (NextFilter nextFilter, IoSession session) throws Exception { InetAddress address = ((InetSocketAddress)session.getRemoteAddress ()).getAddress (); if (blacklist.matches (address)) { diagnostic ("Refusing non-TLS connection from host " + address + " due to it matching the hosts requiring authentication", this); session.close (); } else { nextFilter.sessionOpened (session); } } } avis-1.2.2/server/src/main/org/avis/router/ClientConnectionOptions.java0000644000175000017500000000511611147555544026071 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Map; import org.avis.config.Options; import static java.util.Collections.emptyMap; import static org.avis.io.LegacyConnectionOptions.newToLegacy; import static org.avis.router.ClientConnectionOptionSet.CLIENT_CONNECTION_OPTION_SET; /** * Avis router connection options. * * @author Matthew Phillips * * @see ConnectionOptionSet */ public class ClientConnectionOptions extends Options { private static final Map EMPTY_OPTIONS = emptyMap (); private Map requested; public ClientConnectionOptions () { this (null, EMPTY_OPTIONS); } public ClientConnectionOptions (Map requested) { this (null, requested); } /** * Create a new instance from a set of requested values. * * @param defaultOptions The default option values for options that * are not set, usually set by the router. May be * null for standard defaults. * @param requested The requested set of values, usually from the * client creating the connection. */ public ClientConnectionOptions (Options defaultOptions, Map requested) { super (CLIENT_CONNECTION_OPTION_SET); if (defaultOptions != null) addDefaults (defaultOptions); this.requested = requested; setAll (requested); } /** * Generate the set of accepted client options. * * @see ClientConnectionOptionSet#accepted(Options, Map) */ public Map accepted () { return CLIENT_CONNECTION_OPTION_SET.accepted (this, requested); } /** * Set an option and its legacy option (if any). */ public void setWithLegacy (String option, Object value) { set (option, value); set (newToLegacy (option), value); } } avis-1.2.2/server/src/main/org/avis/router/ConnectionOptionSet.java0000644000175000017500000000744311147555544025230 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import org.avis.config.OptionSet; import static org.avis.common.Common.K; import static org.avis.common.Common.MAX; import static org.avis.common.Common.MB; /** * Defines Avis client connection options. *

      * * From Sec 7.5: * *

       *      Name                        |  Type    |  Min   Default      Max
       *      ----------------------------+----------+-------------------------
       *      Attribute.Max-Count         |  int32   |    64     256     2**31
       *      Attribute.Name.Max-Length   |  int32   |    64    2048     2**31
       *      Attribute.Opaque.Max-Length |  int32   |    1K      1M     2**31
       *      Attribute.String.Max-Length |  int32   |    1K      1M     2**31
       *      Packet.Max-Length           |  int32   |
       *      Receive-Queue.Drop-Policy   |  string  |
       *      Receive-Queue.Max-Length    |  int32   |
       *      Send-Queue.Drop-Policy      |  string  |
       *      Send-Queue.Max-Length       |  int32   |
       *      Subscription.Max-Count      |  int32   |
       *      Subscription.Max-Length     |  int32   |
       *      Supported-Key-Schemes       |  string  |
       *      ----------------------------+----------+-------------------------
       * 
      * * @author Matthew Phillips */ public class ConnectionOptionSet extends OptionSet { public static final ConnectionOptionSet CONNECTION_OPTION_SET = new ConnectionOptionSet (); public ConnectionOptionSet () { // ------------ Options required for all Elvin implementations add ("Packet.Max-Length", 1*K, 2*MB, 10*MB); /* * todo: we only enforce max packet length, which by implication * limits the values below. The correct min, default, max values * are currently commented out and replaced with MAX to avoid * lying to clients that actually care about these. */ // add ("Attribute.Max-Count", 64, 256, MAX); // add ("Attribute.Name.Max-Length", 64, 2*K, MAX); // add ("Attribute.Opaque.Max-Length", 1*K, 1*MB, MAX); // add ("Attribute.String.Max-Length", 1*K, 1*MB, MAX); // add ("Subscription.Max-Count", 1*K, 2*K, MAX); // add ("Subscription.Max-Length", 1*K, 2*K, MAX); add ("Attribute.Max-Count", MAX, MAX, MAX); add ("Attribute.Name.Max-Length", MAX, MAX, MAX); add ("Attribute.Opaque.Max-Length", MAX, MAX, MAX); add ("Attribute.String.Max-Length", MAX, MAX, MAX); add ("Subscription.Max-Count", 16, 2*K, 2*K); add ("Subscription.Max-Length", 1*K, 2*K, 4*K); add ("Receive-Queue.Max-Length", 1*K, 1*MB, 1*MB); // todo: enforce following queue-related options add ("Receive-Queue.Drop-Policy", "oldest", "newest", "largest", "fail"); add ("Send-Queue.Drop-Policy", "oldest", "newest", "largest", "fail"); add ("Send-Queue.Max-Length", MAX, MAX, MAX); add ("Supported-Key-Schemes", "SHA-1"); add ("TCP.Send-Immediately", 0, 0, 1); // ------------ Avis-specific options // Max connection keys for ntfn/sub add ("Connection.Max-Keys", 0, 1*K, 1*K); add ("Subscription.Max-Keys", 0, 256, 1*K); } } avis-1.2.2/server/src/main/org/avis/router/NoConnectionException.java0000644000175000017500000000162711147555544025535 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import org.apache.mina.filter.codec.ProtocolCodecException; public class NoConnectionException extends ProtocolCodecException { public NoConnectionException (String message) { super (message); } } avis-1.2.2/server/src/main/org/avis/router/Connection.java0000644000175000017500000001517511147555544023364 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import org.avis.config.Options; import org.avis.security.Keys; import static org.avis.security.DualKeyScheme.Subset.CONSUMER; import static org.avis.security.DualKeyScheme.Subset.PRODUCER; /** * Stores the state needed for a client's connection to the router. * Thread access is managed via a single writer/multiple reader lock. * * @author Matthew Phillips */ class Connection { /** * Connection options established on construction (immutable). */ public ClientConnectionOptions options; /** * Connection-wide subscription keys that apply to all * subscriptions. Teat as immutable once assigned. */ public Keys subscriptionKeys; /** * Connection-wide notification keys that apply to all * notifications. Treat as immutable once assigned. */ public Keys notificationKeys; /** * The client's subscription set. Maps subscription ID's to their * Subscription instance. */ public Long2ObjectOpenHashMap subscriptions; /** * Single writer/multiple reader lock. */ private ReentrantReadWriteLock lock; /** * Create a new connection instance. * * @param defaultOptions The default connection options. * * @param requestedOptions The client's requested option values. * @param subscriptionKeys The client's initial global subscription * key collection. * @param notificationKeys The client's initial global notification * key collection. */ public Connection (Options defaultOptions, Map requestedOptions, Keys subscriptionKeys, Keys notificationKeys) { this.subscriptions = new Long2ObjectOpenHashMap (); this.subscriptionKeys = subscriptionKeys; this.notificationKeys = notificationKeys; this.options = new ClientConnectionOptions (defaultOptions, requestedOptions); this.lock = new ReentrantReadWriteLock (true); subscriptionKeys.hashPrivateKeysForRole (CONSUMER); notificationKeys.hashPrivateKeysForRole (PRODUCER); } /** * Mark connection as closed. OK to call more than once. */ public void close () { // use null options as marker for closed connection options = null; } public boolean isOpen () { return options != null; } /** * Lock the connection for writing. There can be only one writer and * zero readers at any one time. */ public void lockWrite () { lock.writeLock ().lock (); } public void unlockWrite () { lock.writeLock ().unlock (); } /** * Lock the connection for reading. There can be any number of * readers and zero writers at any one time. */ public void lockRead () { lock.readLock ().lock (); } public void unlockRead () { lock.readLock ().unlock (); } public void addSubscription (Subscription sub) { subscriptions.put (sub.id, sub); } public Subscription removeSubscription (long subscriptionId) { return subscriptions.remove (subscriptionId); } /** * Test if subscriptions are at or exceed the limit set by the * Subscription.Max-Count connection option. */ public boolean subscriptionsFull () { return subscriptions.size () >= options.getInt ("Subscription.Max-Count"); } public boolean subscriptionTooLong (String expr) { return expr.length () > options.getInt ("Subscription.Max-Length"); } public boolean connectionKeysFull (Keys ntfnKeys, Keys subKeys) { int maxKeys = options.getInt ("Connection.Max-Keys"); return ntfnKeys.size () > maxKeys || subKeys.size () > maxKeys; } public boolean subscriptionKeysFull (Keys keys) { return keys.size () > options.getInt ("Subscription.Max-Keys"); } /** * Match a given set of attributes against this connection's * subscriptions and return the ID's of those subscriptions that * match. * * @param attributes The attributes to match. * @param globalKeys The set of notification keys that apply to all * notifications. * @param messageKeys The set of keys provided for the current * notification. Either these keys or globalKeys must match * a subscription's keys for a secure match to apply. * @param deliverInsecure If true, insecure matches are acceptable * for subscriptions that allow insecure delivery. * * @return The match result. */ public SubscriptionMatch matchSubscriptions (Map attributes, Keys globalKeys, Keys messageKeys, boolean deliverInsecure) { SubscriptionMatch matches = new SubscriptionMatch (); for (Subscription subscription : subscriptions.values ()) { /* * Check message/global keys against global subscription keys * and keys for the current subscription. */ boolean secureMatch = subscriptionKeys.match (globalKeys) || subscriptionKeys.match (messageKeys) || subscription.keys.match (globalKeys) || subscription.keys.match (messageKeys); if (secureMatch || (deliverInsecure && subscription.acceptInsecure)) { if (subscription.matches (attributes)) { if (secureMatch) matches.secure.add (subscription.id); else matches.insecure.add (subscription.id); } } } return matches; } public Subscription subscriptionFor (long id) throws InvalidSubscriptionException { Subscription subscription = subscriptions.get (id); if (subscription != null) return subscription; else throw new InvalidSubscriptionException ("No subscription with ID " + id); } } avis-1.2.2/server/src/main/org/avis/router/Main.java0000644000175000017500000001547611147555544022155 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Properties; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import org.avis.common.ElvinURI; import org.avis.federation.EwafURI; import org.avis.federation.FederationManager; import org.avis.federation.FederationOptionSet; import org.avis.logging.Log; import org.avis.util.IllegalCommandLineOption; import org.avis.util.IllegalConfigOptionException; import static org.avis.federation.FederationManager.federationManagerFor; import static org.avis.io.Net.addressesFor; import static org.avis.logging.Log.DIAGNOSTIC; import static org.avis.logging.Log.TRACE; import static org.avis.logging.Log.alarm; import static org.avis.logging.Log.diagnostic; import static org.avis.logging.Log.enableLogging; import static org.avis.logging.Log.info; import static org.avis.logging.Log.shouldLog; import static org.avis.logging.Log.warn; import static org.avis.util.CommandLine.intArg; import static org.avis.util.CommandLine.stringArg; import static org.avis.util.Streams.fileStream; import static org.avis.util.Streams.propertiesFrom; import static org.avis.util.Streams.resourceStream; /** * Invokes the Avis router from the command line. * * @author Matthew Phillips */ public class Main { private static final String USAGE = "Usage: avisd [-h] [-v] [-vv] [-p port] [-c file]\n\n" + " -h : This text\n" + " -v and -vv : Increase verbosity\n" + " -p port : Set port to listen on\n" + " -c file : Load config from file\n"; public static void main (String [] args) throws Exception { Log.setApplicationName ("Avis"); enableLogging (TRACE, false); enableLogging (DIAGNOSTIC, false); Properties avisProperties = readAvisProperties (); System.getProperties ().putAll (avisProperties); info ("Avis event router version " + avisProperties.getProperty ("avis.router.version"), Main.class); try { final Router router = start (args); Runtime.getRuntime ().addShutdownHook (new Thread () { public void run () { info ("Shutting down...", Main.class); router.close (); } }); for (ElvinURI uri : router.listenURIs ()) { for (InetSocketAddress address : addressesFor (uri)) info ("Router listening on " + address + " (" + uri + ")", Main.class); } if (router.options ().getBoolean ("Federation.Activated")) { for (EwafURI uri : federationManagerFor (router).listenURIs ()) { for (InetSocketAddress address : addressesFor (uri)) { info ("Federator listening on " + address + " (" + uri + ")", Main.class); } } } } catch (Throwable ex) { if (ex instanceof IllegalArgumentException) { if (ex instanceof IllegalCommandLineOption) { alarm (ex.getMessage (), Main.class); System.err.println (); System.err.println (USAGE); } else { alarm ("Error in router configuration: " + ex.getMessage (), Main.class); } exit (1); } else { alarm ("Error starting router: " + ex.getMessage (), Main.class); if (shouldLog (DIAGNOSTIC)) ex.printStackTrace (); exit (2); } } } /** * Create and start a router with a given set of command line * arguments. * * @param args The command line. * * @return The new router instance. * * @throws IllegalConfigOptionException * @throws IOException */ public static Router start (String... args) throws IllegalConfigOptionException, IOException { RouterOptionSet routerOptionSet = new RouterOptionSet (); // add federation options to router's option set routerOptionSet.inheritFrom (FederationOptionSet.OPTION_SET); RouterOptions config = new RouterOptions (routerOptionSet); parseCommandLine (args, config); Router router = new Router (config); if (config.getBoolean ("Federation.Activated")) new FederationManager (router, config); return router; } private static void parseCommandLine (String [] args, RouterOptions config) throws IllegalConfigOptionException, IllegalCommandLineOption { for (int i = 0; i < args.length; i++) { String arg = args [i]; try { if (arg.equals ("-v")) { enableLogging (DIAGNOSTIC, true); } else if (arg.equals ("-vv")) { enableLogging (DIAGNOSTIC, true); enableLogging (TRACE, true); } else if (arg.equals ("-p")) { config.set ("Port", intArg (args, ++i)); } else if (arg.equals ("-c")) { File configFile = new File (stringArg (args, ++i)).getAbsoluteFile (); config.setAll (propertiesFrom (fileStream (configFile))); config.setRelativeDirectory (configFile.getParentFile ()); diagnostic ("Read configuration from " + configFile, Main.class); } else { throw new IllegalCommandLineOption ("Unknown command line option: \"" + arg + "\""); } } catch (IOException ex) { throw new IllegalCommandLineOption ("Error in command line option: \"" + arg + "\": " + ex.getMessage ()); } } } private static void exit (int errorCode) { info ("Exiting on error", Main.class); System.exit (errorCode); } private static Properties readAvisProperties () throws IOException { Properties properties; try { properties = propertiesFrom (resourceStream ("/avis.properties")); } catch (IOException ex) { warn ("Failed to load Avis property file: " + ex.getMessage (), Main.class); properties = new Properties (); } if (!properties.containsKey ("avis.router.version")) properties.put ("avis.router.version", ""); return properties; } } avis-1.2.2/server/src/main/org/avis/router/InvalidSubscriptionException.java0000644000175000017500000000154411147555544027132 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; class InvalidSubscriptionException extends RuntimeException { public InvalidSubscriptionException (String message) { super (message); } } avis-1.2.2/server/src/main/org/avis/router/NotifyListener.java0000644000175000017500000000222111147555544024227 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import org.avis.io.messages.Notify; import org.avis.security.Keys; /** * Interface for listeners to router Notify messages. * * @author Matthew Phillips */ public interface NotifyListener { /** * Invoked when the router has received a Notify message for * delivery. * * @param message The message. * @param keys The global notification keys that apply to the message. */ public void notifyReceived (Notify message, Keys keys); } avis-1.2.2/server/src/main/org/avis/router/Router.java0000644000175000017500000011334511147555544022543 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; import java.security.GeneralSecurityException; import java.security.KeyStore; import org.apache.mina.common.DefaultIoFilterChainBuilder; import org.apache.mina.common.ExceptionMonitor; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoHandler; import org.apache.mina.common.IoSession; import org.apache.mina.common.ThreadModel; import org.apache.mina.common.WriteFuture; import org.apache.mina.filter.ReadThrottleFilterBuilder; import org.apache.mina.filter.codec.ProtocolCodecException; import org.apache.mina.filter.executor.ExecutorFilter; import org.apache.mina.transport.socket.nio.SocketAcceptor; import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; import org.avis.common.ElvinURI; import org.avis.config.Options; import org.avis.io.ClientFrameCodec; import org.avis.io.ExceptionMonitorLogger; import org.avis.io.FrameTooLargeException; import org.avis.io.messages.ConfConn; import org.avis.io.messages.ConnRply; import org.avis.io.messages.ConnRqst; import org.avis.io.messages.Disconn; import org.avis.io.messages.DisconnRply; import org.avis.io.messages.DisconnRqst; import org.avis.io.messages.ErrorMessage; import org.avis.io.messages.Message; import org.avis.io.messages.Nack; import org.avis.io.messages.Notify; import org.avis.io.messages.NotifyDeliver; import org.avis.io.messages.NotifyEmit; import org.avis.io.messages.QuenchPlaceHolder; import org.avis.io.messages.SecRply; import org.avis.io.messages.SecRqst; import org.avis.io.messages.SubAddRqst; import org.avis.io.messages.SubDelRqst; import org.avis.io.messages.SubModRqst; import org.avis.io.messages.SubRply; import org.avis.io.messages.TestConn; import org.avis.io.messages.UNotify; import org.avis.io.messages.XidMessage; import org.avis.security.Keys; import org.avis.subscription.parser.ConstantExpressionException; import org.avis.subscription.parser.ParseException; import org.avis.util.ConcurrentHashSet; import org.avis.util.Filter; import org.avis.util.IllegalConfigOptionException; import org.avis.util.ListenerList; import org.avis.util.Text; import static java.lang.Runtime.getRuntime; import static java.lang.System.currentTimeMillis; import static java.lang.Thread.sleep; import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.mina.common.ByteBuffer.setUseDirectBuffers; import static org.apache.mina.common.IdleStatus.READER_IDLE; import static org.apache.mina.common.IoFutureListener.CLOSE; import static org.avis.common.Common.CLIENT_VERSION_MAJOR; import static org.avis.common.Common.CLIENT_VERSION_MINOR; import static org.avis.common.Common.DEFAULT_PORT; import static org.avis.io.FrameCodec.setMaxFrameLengthFor; import static org.avis.io.LegacyConnectionOptions.setWithLegacy; import static org.avis.io.Net.addressesFor; import static org.avis.io.Net.enableTcpNoDelay; import static org.avis.io.TLS.toPassphrase; import static org.avis.io.messages.Disconn.REASON_PROTOCOL_VIOLATION; import static org.avis.io.messages.Disconn.REASON_SHUTDOWN; import static org.avis.io.messages.Nack.EMPTY_ARGS; import static org.avis.io.messages.Nack.EXP_IS_TRIVIAL; import static org.avis.io.messages.Nack.IMPL_LIMIT; import static org.avis.io.messages.Nack.NOT_IMPL; import static org.avis.io.messages.Nack.NO_SUCH_SUB; import static org.avis.io.messages.Nack.PARSE_ERROR; import static org.avis.io.messages.Nack.PROT_INCOMPAT; import static org.avis.logging.Log.TRACE; import static org.avis.logging.Log.alarm; import static org.avis.logging.Log.diagnostic; import static org.avis.logging.Log.shouldLog; import static org.avis.logging.Log.trace; import static org.avis.logging.Log.warn; import static org.avis.router.ConnectionOptionSet.CONNECTION_OPTION_SET; import static org.avis.security.DualKeyScheme.Subset.CONSUMER; import static org.avis.security.DualKeyScheme.Subset.PRODUCER; import static org.avis.security.Keys.EMPTY_KEYS; import static org.avis.subscription.parser.SubscriptionParserBase.expectedTokensFor; import static org.avis.util.Text.formatNotification; import static org.avis.util.Text.idFor; public class Router implements IoHandler, Closeable { static { // route MINA exceptions to log ExceptionMonitor.setInstance (ExceptionMonitorLogger.INSTANCE); } private static final String ROUTER_VERSION = System.getProperty ("avis.router.version", ""); private RouterOptions routerOptions; private ExecutorService executor; private SocketAcceptor acceptor; private KeyStore keystore; private volatile boolean closing; private ConcurrentHashSet sessions; private ListenerList notifyListeners; private ListenerList closeListeners; /** * Create an instance with default configuration. */ public Router () throws IOException { this (DEFAULT_PORT); } /** * Shortcut to create an instance listening to localhost:port. */ public Router (int port) throws IOException { this (new RouterOptions (port)); } /** * Create a new instance. * * @param options The router configuration options. Note that, due * to a MINA limitation, the IO.Use-Direct-Buffers * option applies globally, so using multiple router * instances with this option set to different values * will clash. * * @throws IOException if an network error during router * initialisation. * @throws IllegalConfigOptionException If an option in the configuratiion * options is invalid. */ @SuppressWarnings ("unchecked") public Router (RouterOptions options) throws IOException, IllegalConfigOptionException { this.notifyListeners = new ListenerList (NotifyListener.class, "notifyReceived", Notify.class, Keys.class); this.closeListeners = new ListenerList (CloseListener.class, "routerClosing", Router.class); this.routerOptions = options; this.sessions = new ConcurrentHashSet (); this.executor = newCachedThreadPool (); this.acceptor = new SocketAcceptor (getRuntime ().availableProcessors () + 1, executor); setUseDirectBuffers (options.getBoolean ("IO.Use-Direct-Buffers")); /* * Setup IO filter chain with codec and then thread pool. NOTE: * The read throttling system needs an ExecutorFilter to glom * onto: it's not clear that we gain any other benefit from it * since notification processing is non-blocking. See * http://mina.apache.org/configuring-thread-model.html. */ DefaultIoFilterChainBuilder filters = new DefaultIoFilterChainBuilder (); filters.addLast ("codec", ClientFrameCodec.FILTER); filters.addLast ("threadPool", new ExecutorFilter (executor)); bind (options.listenURIs (), this, filters, (Filter)routerOptions.get ("Require-Authenticated")); } /** * Bind to a set of URI's. This can be used by plugins to bind to * network addresses using the same network setup as the router * would, including TLS parameters. * * @param uris The URI's to listen to. * @param handler The IO handler. * @param baseFilters The IO filters used by all connection types. * @param authRequired Hosts matched by this filter must be * successfully authenticated via TLS or will be * refused access. * * @throws IOException if an error occurred during binding. */ public void bind (Set uris, IoHandler handler, DefaultIoFilterChainBuilder baseFilters, Filter authRequired) throws IOException { SocketAcceptorConfig defaultAcceptorConfig = createAcceptorConfig (createStandardFilters (baseFilters, authRequired)); SocketAcceptorConfig secureAcceptorConfig = null; // lazy init'ed for (ElvinURI uri : uris) { SocketAcceptorConfig bindConfig; if (uri.isSecure ()) { if (secureAcceptorConfig == null) { secureAcceptorConfig = createAcceptorConfig (createSecureFilters (baseFilters, authRequired, false)); } bindConfig = secureAcceptorConfig; } else { bindConfig = defaultAcceptorConfig; } for (InetSocketAddress address : addressesFor (uri)) acceptor.bind (address, handler, bindConfig); } } /** * Create default MINA config for incoming connections. * * @param filters The base set of common filters. * @param authRequired The set of hosts that must connect via * authenticated connections and should be * blacklisted from non authenticated access. */ private SocketAcceptorConfig createAcceptorConfig (DefaultIoFilterChainBuilder filters) { SocketAcceptorConfig defaultAcceptorConfig = new SocketAcceptorConfig (); defaultAcceptorConfig.setReuseAddress (true); defaultAcceptorConfig.setThreadModel (ThreadModel.MANUAL); defaultAcceptorConfig.setFilterChainBuilder (filters); return defaultAcceptorConfig; } /** * Create the filters used for standard, non secured links. * * @param commonFilters The initial set of filters to add to. * @param authRequired The hosts for which authentication is * required. For standard link these hosts are denied * connection. * @return The new filter set. */ public DefaultIoFilterChainBuilder createStandardFilters (DefaultIoFilterChainBuilder commonFilters, Filter authRequired) { if (authRequired != Filter.MATCH_NONE) { commonFilters = (DefaultIoFilterChainBuilder)commonFilters.clone (); commonFilters.addFirst ("blacklist", new BlacklistFilter (authRequired)); } return commonFilters; } /** * Create the filters used for TLS-secured links. * * @param commonFilters The initial set of filters to add to. * @param authRequired The hosts for which authentication is * required. * @param clientMode True if the TLS filter should be in client mode. * * @return The new filter set. */ public DefaultIoFilterChainBuilder createSecureFilters (DefaultIoFilterChainBuilder commonFilters, Filter authRequired, boolean clientMode) throws IOException { DefaultIoFilterChainBuilder secureFilters = (DefaultIoFilterChainBuilder)commonFilters.clone (); secureFilters.addFirst ("security", new SecurityFilter (keystore (), routerOptions.getString ("TLS.Keystore-Passphrase"), authRequired, clientMode)); return secureFilters; } /** * Lazy load the router's keystore. */ private KeyStore keystore () throws IOException { if (keystore == null) { URI keystoreUri = (URI)routerOptions.get ("TLS.Keystore"); if (keystoreUri.toString ().length () == 0) { throw new IOException ("Cannot use TLS without a keystore: " + "see TLS.Keystore configuration option"); } InputStream keystoreStream = routerOptions.toAbsoluteURI (keystoreUri).toURL ().openStream (); try { keystore = KeyStore.getInstance ("JKS"); keystore.load (keystoreStream, toPassphrase (routerOptions.getString ("TLS.Keystore-Passphrase"))); } catch (GeneralSecurityException ex) { throw new IOException ("Failed to load TLS keystore: " + ex.getMessage ()); } finally { keystoreStream.close (); } } return keystore; } /** * Close all connections synchronously. Close listeners are notified * before shutdown commences. May be called more than once with no * effect. */ public void close () { synchronized (this) { if (closing) return; closing = true; } closeListeners.fire (this); closeListeners = null; Disconn disconnMessage = new Disconn (REASON_SHUTDOWN); for (IoSession session : sessions) { Connection connection = peekConnectionFor (session); session.suspendRead (); if (connection != null) { connection.lockWrite (); try { if (connection.isOpen ()) { send (session, disconnMessage).addListener (CLOSE); connection.close (); } } finally { connection.unlockWrite (); } } } waitForAllSessionsClosed (); acceptor.unbindAll (); executor.shutdown (); try { if (!executor.awaitTermination (15, SECONDS)) warn ("Failed to cleanly shut down thread pool", this); } catch (InterruptedException ex) { diagnostic ("Interrupted while waiting for shutdown", this, ex); } } private void waitForAllSessionsClosed () { long finish = currentTimeMillis () + 20000; try { while (!sessions.isEmpty () && currentTimeMillis () < finish) sleep (100); } catch (InterruptedException ex) { Thread.currentThread ().interrupt (); } if (!sessions.isEmpty ()) { warn ("Sessions took too long to close " + "(" + sessions.size () + " still open)", this); } sessions.clear (); } /** * The shared executor thread pool used by the router. Plugins may * share this. */ public ExecutorService executor () { return executor; } /** * The router's MINA socket acceptor. Plugins may share this. */ public SocketAcceptor socketAcceptor () { return acceptor; } public Set listenURIs () { return routerOptions.listenURIs (); } public Options options () { return routerOptions; } /** * Used for testing to simulate server hanging: server stops * responding to messages but keeps connection open. * * @see #testSimulateUnhang() */ public void testSimulateHang () { // cause messageReceived () to stop processing closing = true; } /** * Undo the effect of {@link #testSimulateHang()}. */ public void testSimulateUnhang () { closing = false; } /** * Add a listener that will be invoked when the router is about to * close down. * * @see #removeCloseListener(CloseListener) */ public void addCloseListener (CloseListener listener) { synchronized (closeListeners) { closeListeners.add (listener); } } /** * Undo the effect of {@link #addCloseListener(CloseListener)}. */ public void removeCloseListener (CloseListener listener) { synchronized (closeListeners) { closeListeners.remove (listener); } } /** * Get a list of the current close event listeners. */ public List closeListeners () { return closeListeners.asList (); } /** * Add a listener that will be invoked whenever a Notify message is * handled for delivery. * * @see #removeNotifyListener(NotifyListener) * @see #injectNotify(Notify) */ public void addNotifyListener (NotifyListener listener) { synchronized (notifyListeners) { notifyListeners.add (listener); } } /** * Undo the effect of {@link #addNotifyListener(NotifyListener)}. */ public void removeNotifyListener (NotifyListener listener) { synchronized (notifyListeners) { notifyListeners.remove (listener); } } // IoHandler interface public void messageReceived (IoSession session, Object messageObject) throws Exception { if (closing || connectionClosed (session)) return; if (shouldLog (TRACE)) { trace ("Server got message from " + Text.idFor (session) + ": " + messageObject, this); } Message message = (Message)messageObject; try { switch (message.typeId ()) { case ConnRqst.ID: handleConnRqst (session, (ConnRqst)message); break; case DisconnRqst.ID: handleDisconnRqst (session, (DisconnRqst)message); break; case SubAddRqst.ID: handleSubAddRqst (session, (SubAddRqst)message); break; case SubModRqst.ID: handleSubModRqst (session, (SubModRqst)message); break; case SubDelRqst.ID: handleSubDelRqst (session, (SubDelRqst)message); break; case NotifyEmit.ID: handleNotifyEmit (session, (NotifyEmit)message); break; case SecRqst.ID: handleSecRqst (session, (SecRqst)message); break; case TestConn.ID: handleTestConn (session); break; case UNotify.ID: handleUnotify ((UNotify)message); break; case ErrorMessage.ID: handleError (session, (ErrorMessage)message); break; case QuenchPlaceHolder.ID: handleQuench (session, (QuenchPlaceHolder)message); break; default: warn ("Server got an unhandleable message type: " + message, this); } } catch (ProtocolCodecException ex) { /* * A message processing method detected a protocol violation * e.g. attempt to remove non existent subscription. */ disconnectProtocolViolation (session, message, ex.getMessage (), ex); } } private void handleConnRqst (IoSession session, ConnRqst message) throws ProtocolCodecException { if (peekConnectionFor (session) != null) throw new ProtocolCodecException ("Already connected"); Connection connection = new Connection (routerOptions, message.options, message.subscriptionKeys, message.notificationKeys); int maxKeys = connection.options.getInt ("Connection.Max-Keys"); if (message.versionMajor != CLIENT_VERSION_MAJOR || message.versionMinor > CLIENT_VERSION_MINOR) { send (session, new Nack (message, PROT_INCOMPAT, "Max supported protocol version is " + + CLIENT_VERSION_MAJOR + '.' + CLIENT_VERSION_MINOR + ": use a connection URI like " + "elvin:" + CLIENT_VERSION_MAJOR + '.' + CLIENT_VERSION_MINOR + "//hostname to specify " + "protocol version")); } else if (message.notificationKeys.size () > maxKeys || message.subscriptionKeys.size () > maxKeys) { nackLimit (session, message, "Too many keys"); } else { updateTcpSendImmediately (session, connection.options); updateQueueLength (session, connection); Map options = connection.options.accepted (); // add router ID setWithLegacy (options, "Vendor-Identification", "Avis " + ROUTER_VERSION); connection.lockWrite (); try { setConnection (session, connection); send (session, new ConnRply (message, options)); } finally { connection.unlockWrite (); } } } /** * NOTE: the spec says it's a violation to add the same key more * than once or to remove a non-existent key (sec 7.4.8) and * should be reported as a protocol violation. Avis currently * doesn't enforce this since neither of these cases has any * effect on its key collections and the check would add overhead. */ private void handleSecRqst (IoSession session, SecRqst message) throws NoConnectionException { Connection connection = writeableConnectionFor (session); try { message.addNtfnKeys.hashPrivateKeysForRole (PRODUCER); message.delNtfnKeys.hashPrivateKeysForRole (PRODUCER); message.addSubKeys.hashPrivateKeysForRole (CONSUMER); message.delSubKeys.hashPrivateKeysForRole (CONSUMER); Keys newNtfnKeys = connection.notificationKeys.delta (message.addNtfnKeys, message.delNtfnKeys); Keys newSubKeys = connection.subscriptionKeys.delta (message.addSubKeys, message.delSubKeys); if (connection.connectionKeysFull (newNtfnKeys, newSubKeys)) { nackLimit (session, message, "Too many keys"); } else { connection.notificationKeys = newNtfnKeys; connection.subscriptionKeys = newSubKeys; send (session, new SecRply (message)); } } finally { connection.unlockWrite (); } } private void handleDisconnRqst (IoSession session, DisconnRqst message) throws NoConnectionException { Connection connection = writeableConnectionFor (session); try { connection.close (); send (session, new DisconnRply (message)).addListener (CLOSE); } finally { connection.unlockWrite (); } } private void handleSubAddRqst (IoSession session, SubAddRqst message) throws NoConnectionException { Connection connection = writeableConnectionFor (session); try { if (connection.subscriptionsFull ()) { nackLimit (session, message, "Too many subscriptions"); } else if (connection.subscriptionTooLong (message.subscriptionExpr)) { nackLimit (session, message, "Subscription too long"); } else if (connection.subscriptionKeysFull (message.keys)) { nackLimit (session, message, "Too many keys"); } else { Subscription subscription = new Subscription (message.subscriptionExpr, message.keys, message.acceptInsecure); connection.addSubscription (subscription); send (session, new SubRply (message, subscription.id)); } } catch (ParseException ex) { nackParseError (session, message, message.subscriptionExpr, ex); } finally { connection.unlockWrite (); } } private void handleSubModRqst (IoSession session, SubModRqst message) throws NoConnectionException { Connection connection = writeableConnectionFor (session); try { Subscription subscription = connection.subscriptionFor (message.subscriptionId); message.addKeys.hashPrivateKeysForRole (CONSUMER); message.delKeys.hashPrivateKeysForRole (CONSUMER); Keys newKeys = subscription.keys.delta (message.addKeys, message.delKeys); if (connection.subscriptionKeysFull (newKeys)) { nackLimit (session, message, "Too many keys"); } else if (connection.subscriptionTooLong (message.subscriptionExpr)) { nackLimit (session, message, "Subscription too long"); } else { if (message.subscriptionExpr.length () > 0) subscription.updateExpression (message.subscriptionExpr); subscription.keys = newKeys; subscription.acceptInsecure = message.acceptInsecure; send (session, new SubRply (message, subscription.id)); } } catch (ParseException ex) { nackParseError (session, message, message.subscriptionExpr, ex); } catch (InvalidSubscriptionException ex) { nackNoSub (session, message, message.subscriptionId, ex.getMessage ()); } finally { connection.unlockWrite (); } } private void handleSubDelRqst (IoSession session, SubDelRqst message) throws NoConnectionException { Connection connection = writeableConnectionFor (session); try { if (connection.removeSubscription (message.subscriptionId) != null) send (session, new SubRply (message, message.subscriptionId)); else nackNoSub (session, message, message.subscriptionId, "Invalid subscription ID"); } finally { connection.unlockWrite (); } } private void handleNotifyEmit (IoSession session, NotifyEmit message) throws NoConnectionException { if (shouldLog (TRACE)) logNotification (session, message); message.keys.hashPrivateKeysForRole (PRODUCER); deliverNotification (message, connectionFor (session).notificationKeys); } private void handleUnotify (UNotify message) { if (shouldLog (TRACE)) logNotification (null, message); message.keys.hashPrivateKeysForRole (PRODUCER); deliverNotification (message, EMPTY_KEYS); } /** * Inject a notification from an outside producer. */ public void injectNotify (Notify message) { deliverNotification (message, EMPTY_KEYS); } /** * Deliver a notification message to subscribers. * * @param message The message (a UNotify or a NotifyEmit). * @param notificationKeys The global notification keys that apply * to the message. These are in addition to any keys * attached to the message itself. */ private void deliverNotification (Notify message, Keys notificationKeys) { for (IoSession session : sessions) { Connection connection = peekConnectionFor (session); if (connection == null) continue; connection.lockRead (); try { if (!connection.isOpen ()) continue; SubscriptionMatch matches = connection.matchSubscriptions (message.attributes, notificationKeys, message.keys, message.deliverInsecure); if (matches.matched ()) { if (shouldLog (TRACE)) { trace ("Delivering notification " + idFor (message) + " to client " + idFor (session), this); } send (session, new NotifyDeliver (message.attributes, matches.secure (), matches.insecure ())); } } catch (RuntimeException ex) { /* * Do not allow "normal" runtime exceptions to abort delivery * to other clients. Log and continue to next client. */ alarm ("Exception while delivering notification", this, ex); } finally { connection.unlockRead (); } } if (notifyListeners.hasListeners ()) notifyListeners.fire (message, notificationKeys); } private static void handleTestConn (IoSession session) { // if no other outgoing messages are waiting, send a confirm message if (session.getScheduledWriteRequests () == 0) send (session, ConfConn.INSTANCE); } private static void handleQuench (IoSession session, QuenchPlaceHolder message) { diagnostic ("Rejecting quench request from client: quench is not supported", Router.class); send (session, new Nack (message, NOT_IMPL, "Quench not supported")); } private static void handleError (IoSession session, ErrorMessage errorMessage) { String message; if (errorMessage.error instanceof FrameTooLargeException) { // add helpful advisory for client that exceeds max frame size message = errorMessage.error.getMessage () + ". Use the Packet.Max-Length connection option to increase the " + "maximum notification size."; } else { message = errorMessage.error.getMessage (); } disconnectProtocolViolation (session, errorMessage.cause, message, null); } /** * Handle a protocol violation by a client disconnecting with the * REASON_PROTOCOL_VIOLATION code. * * @param session The client session. * @param cause The message that caused the violation. * @param diagnosticMessage The diagnostic sent back to the client. * @throws NoConnectionException */ private static void disconnectProtocolViolation (IoSession session, Message cause, String diagnosticMessage, Throwable error) { if (diagnosticMessage == null) diagnosticMessage = "Frame format error"; warn ("Disconnecting client " + idFor (session) + " due to protocol violation: " + diagnosticMessage, Router.class); if (error != null) diagnostic ("Decode stack trace", Router.class, error); Connection connection = peekConnectionFor (session); if (connection != null) { connection.lockWrite (); try { connection.close (); } finally { connection.unlockWrite (); } } // send Disconn and close send (session, new Disconn (REASON_PROTOCOL_VIOLATION, diagnosticMessage)).addListener (CLOSE); } /** * Handle the TCP.Send-Immediately connection option if set. */ private static void updateTcpSendImmediately (IoSession session, Options options) { if (!enableTcpNoDelay (session, options.getInt ("TCP.Send-Immediately") != 0)) { options.remove ("TCP.Send-Immediately"); } } /** * Update the receive/send queue lengths based on connection * options. Currently only implements Receive-Queue.Max-Length using * MINA's ReadThrottleFilterBuilder filter. */ private static void updateQueueLength (IoSession session, Connection connection) { ReadThrottleFilterBuilder readThrottle = (ReadThrottleFilterBuilder)session.getAttribute ("readThrottle"); readThrottle.setMaximumConnectionBufferSize (connection.options.getInt ("Receive-Queue.Max-Length")); } /** * Send a NACK response for a parse error with error info. */ private static void nackParseError (IoSession session, XidMessage inReplyTo, String expr, ParseException ex) { int code; Object [] args = EMPTY_ARGS; String message; if (ex instanceof ConstantExpressionException) { code = EXP_IS_TRIVIAL; message = ex.getMessage (); } else { code = PARSE_ERROR; if (ex.currentToken == null) { // handle ParseException with no token info message = ex.getMessage (); args = new Object [] {0, ""}; } else { // use token info to generate a better error message args = new Object [] {ex.currentToken.next.beginColumn, ex.currentToken.next.image}; /* * NB: we could use %1 and %2 to refer to args in the message * here, but why make it harder for the client? */ message = "Parse error at column " + args [0] + ", token \"" + args [1] + "\": expected: " + expectedTokensFor (ex); } } diagnostic ("Subscription add/modify failed with parse error: " + message, Router.class); diagnostic ("Subscription was: " + expr, Router.class); send (session, new Nack (inReplyTo, code, message, args)); } /** * Send a NACK due to a blown limit, e.g. Subscription.Max-Count. */ private static void nackLimit (IoSession session, XidMessage inReplyTo, String message) { send (session, new Nack (inReplyTo, IMPL_LIMIT, message)); } /** * Send a NACK due to an invalid subscription ID. */ private static void nackNoSub (IoSession session, XidMessage inReplyTo, long subscriptionId, String message) { send (session, new Nack (inReplyTo, NO_SUCH_SUB, message, subscriptionId)); } public void exceptionCaught (IoSession session, Throwable ex) throws Exception { if (ex instanceof IOException) diagnostic ("IO exception while processing message", this, ex); else alarm ("Server exception", this, ex); } public void messageSent (IoSession session, Object message) throws Exception { // zip } /** * NB this can be called *after* close () is completed sometimes. */ public void sessionClosed (IoSession session) throws Exception { if (shouldLog (TRACE)) trace ("Server session " + Text.idFor (session) + " closed", this); sessions.remove (session); Connection connection = peekConnectionFor (session); if (connection != null) { connection.lockWrite (); try { if (connection.isOpen ()) { diagnostic ("Client disconnected without warning", this); connection.close (); } } finally { connection.unlockWrite (); } } } public void sessionCreated (IoSession session) throws Exception { // client has this long to connect or UNotify session.setIdleTime (READER_IDLE, routerOptions.getInt ("IO.Idle-Connection-Timeout")); // install read throttle ReadThrottleFilterBuilder readThrottle = new ReadThrottleFilterBuilder (); readThrottle.setMaximumConnectionBufferSize (CONNECTION_OPTION_SET.defaults.getInt ("Receive-Queue.Max-Length")); readThrottle.attach (session.getFilterChain ()); session.setAttribute ("readThrottle", readThrottle); // set default max length for connectionless sessions setMaxFrameLengthFor (session, CONNECTION_OPTION_SET.defaults.getInt ("Packet.Max-Length")); sessions.add (session); } public void sessionIdle (IoSession session, IdleStatus status) throws Exception { // close idle sessions that we haven't seen a ConnRqst for yet if (status == READER_IDLE && peekConnectionFor (session) == null) { diagnostic ("Client " + Text.idFor (session) + " waited too long to connect: closing session", this); session.close (); } } public void sessionOpened (IoSession session) throws Exception { diagnostic ("Server session " + Text.idFor (session) + " opened for connection on " + session.getServiceAddress () + (isSecure (session) ? " (using TLS)" : ""), this); } private static void setConnection (IoSession session, Connection connection) { session.setAttachment (connection); setMaxFrameLengthFor (session, connection.options.getInt ("Packet.Max-Length")); } /** * Get the (open) connection associated with a session or throw * NoConnectionException. */ private static Connection connectionFor (IoSession session) throws NoConnectionException { Connection connection = (Connection)session.getAttachment (); if (connection == null) throw new NoConnectionException ("No connection established for session"); else if (!connection.isOpen ()) throw new NoConnectionException ("Connection is closed"); else return connection; } /** * Like connectionFor () but also acquires a write lock. * * @throws NoConnectionException if there is no connection for the * session or the connection is not open. */ private static Connection writeableConnectionFor (IoSession session) throws NoConnectionException { Connection connection = connectionFor (session); connection.lockWrite (); if (!connection.isOpen ()) { connection.unlockWrite (); throw new NoConnectionException ("Connection is closed"); } return connection; } private static WriteFuture send (IoSession session, Message message) { if (shouldLog (TRACE)) { trace ("Server sent message to " + Text.idFor (session) + ": " + message, Router.class); } return session.write (message); } private void logNotification (IoSession session, Notify message) { trace ("Notification " + idFor (message) + " from client " + idFor (session) + ":\n" + formatNotification (message.attributes), this); } /** * Get the connection associated with a session or null for no connection. */ private static Connection peekConnectionFor (IoSession session) { return (Connection)session.getAttachment (); } /** * Test if connection is closed or underlying session is closing. */ private static boolean connectionClosed (IoSession session) { Connection connection = peekConnectionFor (session); return session.isClosing () || (connection != null && !connection.isOpen ()); } public static boolean isSecure (IoSession session) { return session.getServiceConfig ().getFilterChain ().contains (SecurityFilter.class); } } avis-1.2.2/server/src/main/org/avis/router/SecurityFilter.java0000644000175000017500000001263211147555544024235 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.net.InetAddress; import java.net.InetSocketAddress; import java.security.KeyStore; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoFilter; import org.apache.mina.common.IoFilterChain; import org.apache.mina.common.IoSession; import org.apache.mina.filter.SSLFilter; import org.avis.util.Filter; import static org.avis.io.TLS.sslContextFor; import static org.avis.logging.Log.diagnostic; /** * A front end filter that wraps a MINA SSL filter for secure * connections. This allows the policy for determining which hosts * must be authenticated to be determined on a per host basis. * * @author Matthew Phillips */ public class SecurityFilter implements IoFilter { private KeyStore keystore; private String keystorePassphrase; private Filter authRequired; private boolean clientMode; public SecurityFilter (KeyStore keystore, String keystorePassphrase, Filter authRequired, boolean clientMode) { this.keystore = keystore; this.keystorePassphrase = keystorePassphrase; this.authRequired = authRequired; this.clientMode = clientMode; } private SSLFilter ssfFilterFor (IoSession session) throws Exception { SSLFilter filter = (SSLFilter)session.getAttribute ("securityFilterSSL"); if (filter == null) { InetAddress address = ((InetSocketAddress)session.getRemoteAddress ()).getAddress (); boolean needAuth = authRequired.matches (address); diagnostic ("Host " + address + " connecting via TLS " + (needAuth ? "needs authentication" : "does not require authentication"), this); filter = new SSLFilter (sslContextFor (keystore, keystorePassphrase, needAuth)); filter.setUseClientMode (clientMode); filter.setNeedClientAuth (needAuth); session.setAttribute ("securityFilterSSL", filter); } return filter; } public void init () throws Exception { // zip } public void destroy () throws Exception { // zip } public void sessionCreated (NextFilter nextFilter, IoSession session) throws Exception { ssfFilterFor (session).sessionCreated (nextFilter, session); } public void exceptionCaught (NextFilter nextFilter, IoSession session, Throwable cause) throws Exception { ssfFilterFor (session).exceptionCaught (nextFilter, session, cause); } public void filterClose (NextFilter nextFilter, IoSession session) throws Exception { ssfFilterFor (session).filterClose (nextFilter, session); } public void filterWrite (NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { ssfFilterFor (session).filterWrite (nextFilter, session, writeRequest); } public void messageReceived (NextFilter nextFilter, IoSession session, Object message) throws Exception { ssfFilterFor (session).messageReceived (nextFilter, session, message); } public void messageSent (NextFilter nextFilter, IoSession session, Object message) throws Exception { ssfFilterFor (session).messageSent (nextFilter, session, message); } public void onPostAdd (IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { ssfFilterFor (parent.getSession ()).onPostAdd (parent, name, nextFilter); } public void onPostRemove (IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { ssfFilterFor (parent.getSession ()).onPostRemove (parent, name, nextFilter); } public void onPreAdd (IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { ssfFilterFor (parent.getSession ()).onPreAdd (parent, name, nextFilter); } public void onPreRemove (IoFilterChain parent, String name, NextFilter nextFilter) throws Exception { ssfFilterFor (parent.getSession ()).onPreRemove (parent, name, nextFilter); } public void sessionClosed (NextFilter nextFilter, IoSession session) throws Exception { ssfFilterFor (session).sessionClosed (nextFilter, session); } public void sessionIdle (NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception { ssfFilterFor (session).sessionIdle (nextFilter, session, status); } public void sessionOpened (NextFilter nextFilter, IoSession session) throws Exception { ssfFilterFor (session).sessionOpened (nextFilter, session); } } avis-1.2.2/server/src/main/org/avis/router/Subscription.java0000644000175000017500000000422711147555544023745 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.router; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.io.StringReader; import org.avis.security.Keys; import org.avis.subscription.ast.Node; import org.avis.subscription.parser.ParseException; import org.avis.subscription.parser.SubscriptionParser; import static org.avis.security.DualKeyScheme.Subset.CONSUMER; import static org.avis.subscription.ast.Node.TRUE; /** * Represents a client's subscription. * * @author Matthew Phillips */ class Subscription { private static final AtomicLong idCounter = new AtomicLong (); public long id; public String expr; public boolean acceptInsecure; public Keys keys; private Node ast; public Subscription (String expr, Keys keys, boolean acceptInsecure) throws ParseException { this.expr = expr; this.keys = keys; this.acceptInsecure = acceptInsecure; this.ast = parse (expr); this.id = nextId (); keys.hashPrivateKeysForRole (CONSUMER); } public void updateExpression (String subscriptionExpr) throws ParseException { ast = parse (subscriptionExpr); expr = subscriptionExpr; } public boolean matches (Map attributes) { return ast.evaluate (attributes) == TRUE; } private static Node parse (String expr) throws ParseException { return new SubscriptionParser (new StringReader (expr)).parseAndValidate (); } private static long nextId () { return idCounter.incrementAndGet (); } } avis-1.2.2/server/src/main/org/avis/config/0000755000175000017500000000000011147555544020336 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/config/OptionTypeString.java0000644000175000017500000000351011147555544024501 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.util.HashSet; import java.util.Set; import org.avis.util.IllegalConfigOptionException; import static java.util.Arrays.asList; public class OptionTypeString extends OptionType { public static final OptionTypeString ANY_STRING_OPTION = new OptionTypeString (); protected Set validValues; public OptionTypeString () { this (null); } public OptionTypeString (String defaultValue, String... validValues) { this.validValues = new HashSet (asList (validValues)); this.validValues.add (defaultValue); } public OptionTypeString (Set validValues) { this.validValues = validValues; } @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { return value.toString (); } @Override public String validate (String option, Object value) { if (value instanceof String) { if (validValues != null && !validValues.contains (value)) return "Value must be one of: " + validValues.toString (); else return null; } else { return "Value must be a string"; } } }avis-1.2.2/server/src/main/org/avis/config/Options.java0000644000175000017500000002370511147555544022643 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.Map.Entry; import java.io.File; import java.net.URI; import org.avis.util.IllegalConfigOptionException; import static java.lang.String.CASE_INSENSITIVE_ORDER; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; import static org.avis.config.OptionSet.EMPTY_OPTION_SET; /** * Defines a set of configuration options. The options are validated * against an {@link OptionSet}. Default values are taken from the * option set, but may be overridden/added in this instance using * {@link #addDefaults(Options)}. * * @author Matthew Phillips */ public class Options implements Iterable> { protected Map values; protected List defaults; protected OptionSet optionSet; protected File relativeDirectory; public Options () { this (EMPTY_OPTION_SET); } public Options (OptionSet optionSet) { this.values = new TreeMap (CASE_INSENSITIVE_ORDER); this.defaults = new ArrayList (); this.optionSet = optionSet; this.relativeDirectory = new File (System.getProperty ("user.dir")); } public OptionSet optionSet () { return optionSet; } public void setRelativeDirectory (String directory) { setRelativeDirectory (new File (directory)); } /** * Set the directory used to resolve relative file paths and URI's * given in the config. This can be used so that relative paths * specified in the config resolve relative to the config itself * rather than the current working directory (which is the default). */ public void setRelativeDirectory (File directory) { this.relativeDirectory = directory; } public Iterator> iterator () { return values.entrySet ().iterator (); } /** * Add a set of options as defaults. Any overlapping values override * existing option defaults in this set and the validating option * set. */ public void addDefaults (Options newDefaults) { defaults.add (0, newDefaults); } /** * Set a bulk lot of options. This is equivalent to calling * {@link #set(String, Object)} for each entry. * * @throws IllegalConfigOptionException */ public void setAll (Map options) throws IllegalConfigOptionException { for (Entry entry : options.entrySet ()) set (entry.getKey (), entry.getValue ()); } /** * Set a bulk lot of options from java.util.Properties source. * * @throws IllegalConfigOptionException */ public void setAll (Properties properties) throws IllegalConfigOptionException { for (Entry entry : properties.entrySet ()) set ((String)entry.getKey (), entry.getValue ()); } /** * Get an integer option. * * @param option The option name. * @return The value. * * @throws IllegalConfigOptionException if the option is not defined or is * not an integer. * * @see #get(String) */ public int getInt (String option) throws IllegalConfigOptionException { Object value = get (option); if (value instanceof Integer) return (Integer)value; else throw new IllegalConfigOptionException (option, "Not an integer"); } /** * Get a string option. * * @param option The option name. * @return The value. * * @throws IllegalConfigOptionException if the option is not defined or is * not a string. * * @see #get(String) */ public String getString (String option) throws IllegalConfigOptionException { Object value = get (option); if (value instanceof String) return (String)value; else throw new IllegalConfigOptionException (option, "Not a string"); } /** * Get a boolean option. * * @param option The option name. * @return The value. * * @throws IllegalConfigOptionException if the option is not defined or is * not a boolean. * * @see #get(String) */ public boolean getBoolean (String option) throws IllegalConfigOptionException { Object value = get (option); if (value instanceof Boolean) return (Boolean)value; else throw new IllegalConfigOptionException (option, "Not a boolean"); } /** * Get a value for a parameterised option. e.g. * "Federation.Subscribe[Internal]". * * @param option The option, minus the parameters. * * @return The value of the option, mapping parameters to values. */ public Map getParamOption (String option) { return OptionTypeParam.getParamOption (this, option); } /** * Get an option value that is a set. * * @param The type of item in the set. * * @param option The option name. * @param type The type of option. This is somewhat bogus, but * needed by the compiler to check generic types. * @return The set value. * * @throws IllegalConfigOptionException if the value is not a set. */ @SuppressWarnings({"unchecked"}) public Set getSet (String option, Class type) { Object value = get (option); if (value instanceof Set) return (Set)value; else throw new IllegalConfigOptionException (option, "Not a set"); } /** * Get the absolute value of a URI option, resolved against the * {@linkplain #setRelativeDirectory(File) current directory} if * needed. e.g. "file.txt" resolves to something like * "file:/home/user/file.txt", whereas "http://host/file.txt" is * untouched. * * @param option The option name. * * @return An absolute URI. * * @see #toAbsoluteURI(URI) */ public URI getAbsoluteURI (String option) { Object value = get (option); if (value instanceof URI) return toAbsoluteURI ((URI)value); else throw new IllegalConfigOptionException (option, "Not a URI"); } /** * Resolve a URI against the * {@linkplain #setRelativeDirectory(File) current directory} if * needed. e.g. "file.txt" resolves to something like * "file:/home/user/file.txt", whereas "http://host/file.txt" is * untouched. * * @param uri The URI to resolve. * * @return An absolute URI. */ public URI toAbsoluteURI (URI uri) { if (uri.isAbsolute ()) return uri; else return relativeDirectory.toURI ().resolve (uri); } /** * Get the value of an option, searching defaults if needed. * * @param option The option name * @return The value. * * @throws IllegalConfigOptionException if the option is not defined. * * @see #peek(String) * @see #set(String, Object) * @see #isDefined(String) */ public Object get (String option) throws IllegalConfigOptionException { Object value = peek (option); if (value != null) return value; else throw new IllegalConfigOptionException (option, "Undefined option"); } /** * Same as get(), but returns null if no value found rather than * throwing an exception. * * @see #get(String) */ public Object peek (String option) { Object value = values.get (option); // look in defaults on this option set if (value == null) { for (Options options : defaults) { value = options.peek (option); if (value != null) break; } } // look in option set for defaults if (value == null) value = optionSet.peekDefaultValue (option); return value; } /** * Set the value of an option. * * @param option The option name. * @param value The option value. * * @throws IllegalConfigOptionException if the option is not defined or * the value is invalid. * * @see #get(String) * @see #remove(String) * @see OptionSet#validateAndSet(Options, String, Object) */ public void set (String option, Object value) throws IllegalConfigOptionException { if (value == null) throw new IllegalConfigOptionException (option, "Value cannot be null"); OptionSet set = optionSet.findOptionSetFor (option); /* * If no option set found, fall back on this one in case * validateAndSet () can do something clever */ if (set == null) set = optionSet; set.validateAndSet (this, option, value); } /** * Undo the effect of set (). * * @param option The option to remove. * * @see #set(String, Object) */ public void remove (String option) { values.remove (option); } /** * Test if an option is defined. */ public boolean isDefined (String option) { return peek (option) != null; } /** * Return an unmodifiable, live set of the options just for this * instance (not including defaults). */ public Set options () { return unmodifiableSet (values.keySet ()); } /** * Return an unmodifiable, live map containing all the options and * values in this instance (not including defaults). */ public Map asMap () { return unmodifiableMap (values); } } avis-1.2.2/server/src/main/org/avis/config/OptionType.java0000644000175000017500000000454511147555544023323 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import static org.avis.util.Text.className; import org.avis.util.IllegalConfigOptionException; /** * Defines a type of option in an OptionSet. * * @author Matthew Phillips */ public abstract class OptionType { /** * Attempt to convert a value to be compatible with this option type. * * @param option The option. * @param value The value. * @return The converted value or just value if none needed. * * @throws IllegalConfigOptionException if value is not convertible. */ public abstract Object convert (String option, Object value) throws IllegalConfigOptionException; /** * Check that a value is valid for this option. * * @param option The option. * @param value The value. * * @return Error text if not valid, null if OK. */ public abstract String validate (String option, Object value); /** * Utility for validate () to call if it just needs a given type. * * @param value The value. * @param type The required type. * * @return The validation response. */ protected String validateType (Object value, Class type) { return validateType (value, type, className (type)); } /** * Utility for validate () to call if it just needs a given type. * * @param value The value. * @param type The required type. * @param typeName A readable name for the type. * * @return The validation response. */ protected String validateType (Object value, Class type, String typeName) { return type.isAssignableFrom (value.getClass ()) ? null : "Value must be a " + typeName + ": " + className (value); } } avis-1.2.2/server/src/main/org/avis/config/OptionTypeParam.java0000644000175000017500000001120211147555544024270 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.avis.util.IllegalConfigOptionException; import org.avis.util.Pair; import static java.util.Collections.emptyList; /** * A parameterised option. These options have a Map as a value, * mapping parameters to their values (which may themselves be maps * for multi-dimensional options). */ public class OptionTypeParam extends OptionType { private static final List EMPTY_STRING_LIST = emptyList (); private OptionType subOption; private int paramCount; public OptionTypeParam (OptionType option) { this (option, 1); } public OptionTypeParam (OptionType subOption, int paramCount) { this.subOption = subOption; this.paramCount = paramCount; } @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { return value; } @Override public String validate (String option, Object value) { return validateType (value, Map.class); } @SuppressWarnings("unchecked") public Object updateValue (Options options, String option, String baseOption, List params, Object value) { if (params.size () != paramCount) { throw new IllegalConfigOptionException (option, "Parameters required: " + paramCount); } value = subOption.convert (option, value); String valid = subOption.validate (option, value); if (valid != null) throw new IllegalConfigOptionException (option, valid); // create/lookup base Map value Map baseValue = (Map)options.get (baseOption); if (baseValue.isEmpty ()) baseValue = new HashMap (); // create/lookup any sub-map's Map map = baseValue; for (int i = 0; i < paramCount - 1; i++) map = createEntry (map, params.get (i)); // store value map.put (params.get (params.size () - 1), value); return baseValue; } @SuppressWarnings("unchecked") public static Map getParamOption (Options options, String option) { OptionType type = options.optionSet ().optionTypeFor (option); if (type instanceof OptionTypeParam) return (Map)options.get (option); else throw new IllegalConfigOptionException (option, "Not a parameterised option"); } /** * Split a parameterised option into a (base option, params) pair. */ public static Pair> splitOptionParam (String option) { int index = option.indexOf ('['); if (index == -1) { if (option.indexOf (']') == -1) return new Pair> (option, EMPTY_STRING_LIST); else throw new IllegalConfigOptionException (option, "Orphan ]"); } String base = option.substring (0, index); List params = new ArrayList (); while (index < option.length ()) { int end = option.indexOf (']', index); if (end == -1) throw new IllegalConfigOptionException (option, "Missing ]"); params.add (option.substring (index + 1, end)); index = end + 1; if (index < option.length () && option.charAt (index) != '[') throw new IllegalConfigOptionException (option, "Junk in parameter list"); } return new Pair> (base, params); } @SuppressWarnings("unchecked") private static Map createEntry (Map map, String key) { Map entry = (Map)map.get (key); if (entry == null) { entry = new HashMap (); map.put (key, entry); } return entry; } }avis-1.2.2/server/src/main/org/avis/config/OptionTypeSet.java0000644000175000017500000000442411147555544023773 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.util.HashSet; import java.util.Set; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.avis.util.IllegalConfigOptionException; import static org.avis.util.Text.split; import static org.avis.util.Text.stripBackslashes; /** * An option that turns space-separated items in string values into a * set of values by using a string constructor of a type. Backslash * escape can be used to quote spaces. */ public class OptionTypeSet extends OptionType { private Constructor constructor; public OptionTypeSet (Class setValueType) { try { this.constructor = setValueType.getConstructor (String.class); } catch (Exception ex) { throw new IllegalArgumentException ("No constructor taking a string"); } } @Override public String validate (String option, Object value) { return validateType (value, Set.class); } @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { try { Set values = new HashSet (); // split using regex that allows any space not preceeded by \ for (String item : split (value.toString ().trim (), "((? * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import org.avis.util.IllegalConfigOptionException; public class OptionTypeInt extends OptionType { protected int min; protected int max; public OptionTypeInt (int min, int max) { this.min = min; this.max = max; } @Override public Object convert (String option, Object value) { if (value instanceof Integer) return value; try { String text = value.toString ().toLowerCase (); int unit = 1; if (text.endsWith ("m")) { unit = 1024*1024; text = text.substring (0, text.length () - 1); } else if (text.endsWith ("k")) { unit = 1024; text = text.substring (0, text.length () - 1); } return Integer.parseInt (text) * unit; } catch (NumberFormatException ex) { throw new IllegalConfigOptionException (option, "\"" + value + "\" is not a valid integer"); } } @Override public String validate (String option, Object value) { if (value instanceof Integer) { int intValue = (Integer)value; if (intValue >= min && intValue <= max) return null; else return "Value must be in range " + min + ".." + max; } else { return "Value is not an integer"; } } }avis-1.2.2/server/src/main/org/avis/config/OptionSet.java0000644000175000017500000002405611147555544023134 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.avis.util.IllegalConfigOptionException; import org.avis.util.Pair; import static org.avis.config.OptionTypeParam.splitOptionParam; import static java.lang.String.CASE_INSENSITIVE_ORDER; /** * Defines the set of valid options for an {@link Options} instance. * An option set can inherit from one or more subsets. An option set * includes the valid option names, value type, valid value ranges and * default values. Option names are stored in a case-preserving * manner, but are matched case-insensitively. * * @author Matthew Phillips */ public class OptionSet { public static final OptionSet EMPTY_OPTION_SET = new OptionSet (); /** The default values for each option. */ public final Options defaults; /** The inherited sets. */ protected List inherited; /** Maps option names to validation info. */ protected Map optionTypes; public OptionSet () { this.defaults = new Options (this); this.inherited = new ArrayList (); this.optionTypes = new TreeMap (CASE_INSENSITIVE_ORDER); } public OptionSet (OptionSet inheritedOptions) { this (); inherited.add (inheritedOptions); } /** * Inherit from a given option set. */ public void inheritFrom (OptionSet optionSet) { inherited.add (optionSet); } /** * Look for the default value specified by this option set or any of * its inherited sets. * * @param option The option to find a value for. * * @return The value, or null if none found. */ public Object peekDefaultValue (String option) { Object value = defaults.values.get (option); if (value == null) { for (OptionSet superSet : inherited) { value = superSet.defaults.values.get (option); if (value != null) break; } } return value; } /** * Define an int-valued option. * * @param option The option name. * @param min The minimum value * @param defaultValue The default value * @param max The maximum value */ public void add (String option, int min, int defaultValue, int max) { add (option, new OptionTypeInt (min, max), defaultValue); } public void add (String option, boolean defaultValue) { add (option, OptionTypeBoolean.INSTANCE, defaultValue); } /** * Define a string-valued option that can take any value. * * @param option The option name. * @param defaultValue The default value. */ public void add (String option, String defaultValue) { add (option, new OptionTypeString (), defaultValue); } /** * Define a string-valued option. * * @param option The option name. * @param defaultValue The default value. * @param values Valid values (other than default). */ public void add (String option, String defaultValue, String... values) { add (option, new OptionTypeString (defaultValue, values), defaultValue); } /** * Add an option with a default value. */ protected void add (String option, OptionType type, Object defaultValue) { optionTypes.put (option, type); set (defaults, option, defaultValue); } /** * Test if value is valid for a given option (does not set the value). * * @see #validate(String, Object) */ public final boolean isValid (String option, Object value) { return validate (option, value) == null; } /** * Test if a given option name is defined by this set or a subset. */ public boolean isDefined (String option) { return findOptionType (option) != null; } /** * Get the maximum value for an int option. */ public int getMaxValue (String name) throws IllegalConfigOptionException { return intOption (name).max; } /** * Get the minimum value for an int option. */ public int getMinValue (String name) throws IllegalConfigOptionException { return intOption (name).min; } private OptionTypeInt intOption (String name) throws IllegalConfigOptionException { OptionType info = findOptionType (name); if (info instanceof OptionTypeInt) return (OptionTypeInt)info; else throw new IllegalConfigOptionException (name, "Not an integer value"); } /** * Test if value is valid for a given option in this set or any * inherited sets. (does not set the value). * * @return Null if valid, a message describing why the value is * invalid otherwise. */ public final String validate (String option, Object value) { String message = null; if (optionTypes.containsKey (option)) { message = testValid (option, value); } else { for (OptionSet inheritedSet : inherited) { message = inheritedSet.testValid (option, value); // if one inherited set accepts the option, we're done if (message == null) break; } } return message; } /** * Called by {@link Options#set(String, Object)} to validate and set * the value. If the value is valid, it should be set with a call to * {@link #set(Options, String, Object)}, otherwise an * IllegalOptionException should be thrown. This method is also * responsible for any automatic value conversion (see * {@link OptionType#convert(String, Object)}). *

      * Subclasses may override to customise validation behaviour. * * @param options The options to update. * @param option The option. * @param value The value to validate and set. * * @throws IllegalConfigOptionException if the value or option are not * valid. * * @see #validate(String, Object) */ protected void validateAndSet (Options options, String option, Object value) { Pair> optionItems = splitOptionParam (option); OptionType type = optionTypeFor (optionItems.item1); if (type instanceof OptionTypeParam) { // allow param option to create/update param values value = ((OptionTypeParam)type).updateValue (options, option, optionItems.item1, optionItems.item2, value); } else { if (!optionItems.item2.isEmpty ()) { throw new IllegalConfigOptionException (option, "Cannot specify parameters for option"); } } value = type.convert (option, value); String message = type.validate (option, value); if (message == null) set (options, optionItems.item1, value); else throw new IllegalConfigOptionException (option, message); } /** * Set a value in the options with no validation. This is usually * called to set validated values from * {@link #validateAndSet(Options, String, Object)}. */ protected final void set (Options options, String option, Object value) { options.values.put (option, value); } /** * Check the validity of an option/value pair against this set only * (no inherited checks). * * @see #validate(String, Object) */ private String testValid (String option, Object value) { return optionTypeFor (option).validate (option, value); } /** * Shortcut to {@link OptionType#convert(String, Object)}. * * @throws IllegalConfigOptionException if option not defined or value is * invalid. */ public Object convert (String option, Object value) throws IllegalConfigOptionException { return optionTypeFor (option).convert (option, value); } /** * Recursively look for the first option set that has a mapping for * a given option. */ protected OptionSet findOptionSetFor (String option) { if (peekOptionTypeFor (option) != null) { return this; } else { for (OptionSet superset : inherited) { OptionSet set = superset.findOptionSetFor (option); if (set != null) return set; } return null; } } /** * Lookup the option type for a given option in this set only (no * recursion). This defaults to optionTypes.get (option), but can be * overridden to extend how options map to types. */ protected OptionType peekOptionTypeFor (String option) { Pair> optionItems = splitOptionParam (option); return optionTypes.get (optionItems.item1); } /** * Get the option type for a given option. * * @throws IllegalConfigOptionException if option is not defined. */ public OptionType optionTypeFor (String option) throws IllegalConfigOptionException { OptionType type = findOptionType (option); if (type == null) throw new IllegalConfigOptionException (option, "Undefined option"); return type; } /** * Recursively search this set and subsets for an option's type. * * @param option Name of option. Must be lower case. */ private OptionType findOptionType (String option) { OptionType optionType = optionTypes.get (option); if (optionType == null) { for (OptionSet inheritedSet : inherited) { optionType = inheritedSet.findOptionType (option); if (optionType != null) break; } } return optionType; } } avis-1.2.2/server/src/main/org/avis/config/OptionTypeValueExpr.java0000644000175000017500000000266111147555544025154 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import org.avis.util.IllegalConfigOptionException; import org.avis.util.InvalidFormatException; import org.avis.util.Text; import static org.avis.util.Text.stringToValue; /** * An option that uses {@link Text#stringToValue(String)} to convert * its value. * * @author Matthew Phillips */ public class OptionTypeValueExpr extends OptionType { @Override public String validate (String option, Object value) { return null; } @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { try { return stringToValue (value.toString ()); } catch (InvalidFormatException ex) { throw new IllegalConfigOptionException (option, ex.getMessage ()); } } } avis-1.2.2/server/src/main/org/avis/config/OptionTypeFromString.java0000644000175000017500000000421311147555544025326 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.avis.util.IllegalConfigOptionException; /** * An option type that uses the string constructor of a value type to * convert strings to values of that type. This can be used for any * option whose values can be converted from strings. * * @author Matthew Phillips */ public class OptionTypeFromString extends OptionType { private Class valueType; private Constructor constructor; public OptionTypeFromString (Class valueType) { this.valueType = valueType; try { this.constructor = valueType.getConstructor (String.class); } catch (Exception ex) { throw new IllegalArgumentException ("No constructor taking a string"); } } @Override public String validate (String option, Object value) { return validateType (value, valueType); } @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { if (valueType.isAssignableFrom (value.getClass ())) return value; try { return constructor.newInstance (value.toString ()); } catch (InvocationTargetException ex) { throw new IllegalConfigOptionException (option, ex.getCause ().getMessage ()); } catch (Exception ex) { throw new IllegalConfigOptionException (option, ex.toString ()); } } } avis-1.2.2/server/src/main/org/avis/config/OptionTypeBoolean.java0000644000175000017500000000305611147555544024617 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import org.avis.util.IllegalConfigOptionException; public class OptionTypeBoolean extends OptionType { public static final OptionTypeBoolean INSTANCE = new OptionTypeBoolean (); @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { if (value instanceof Boolean) return value; String v = value.toString ().trim ().toLowerCase (); if (v.equals ("1") || v.equals ("true") || v.equals ("yes")) return true; else if (v.equals ("0") || v.equals ("false") || v.equals ("no")) return false; else throw new IllegalConfigOptionException (option, "\"" + value + "\" is not a valid true/false boolean"); } @Override public String validate (String option, Object value) { return value instanceof Boolean ? null : "Value must be true/false"; } }avis-1.2.2/server/src/main/org/avis/config/OptionTypeURI.java0000644000175000017500000000300311147555544023667 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.config; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import org.avis.util.IllegalConfigOptionException; /** * A URI-valued option. * * @author Matthew Phillips */ public class OptionTypeURI extends OptionType { @Override public Object convert (String option, Object value) throws IllegalConfigOptionException { try { if (value instanceof URI) return value; else if (value instanceof URL) return ((URL)value).toURI (); else return new URI (value.toString ()); } catch (URISyntaxException ex) { throw new IllegalConfigOptionException (option, "\"" + value + "\" is not a valid URI"); } } @Override public String validate (String option, Object value) { return validateType (value, URI.class); } } avis-1.2.2/server/src/main/org/avis/tools/0000755000175000017500000000000011147555544020231 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/tools/DumpHostAddresses.java0000644000175000017500000000356011147555544024501 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.tools; import java.util.Enumeration; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; /** * Utility to dump network addresses to console for debugging. * * @author Matthew Phillips */ public class DumpHostAddresses { public static void main (String [] args) throws IOException { System.out.println ("local host name: " + InetAddress.getLocalHost ()); for (Enumeration i = NetworkInterface.getNetworkInterfaces (); i.hasMoreElements (); ) { NetworkInterface ni = i.nextElement (); for (Enumeration j = ni.getInetAddresses (); j.hasMoreElements (); ) { InetAddress address = j.nextElement (); System.out.println ("-------"); System.out.println ("host name: " + address.getCanonicalHostName ()); System.out.println ("loopback: " + address.isLoopbackAddress ()); System.out.println ("link local: " + address.isLinkLocalAddress ()); System.out.println ("multicast: " + address.isMulticastAddress ()); System.out.println ("site local: " + address.isSiteLocalAddress ()); } } } } avis-1.2.2/server/src/main/org/avis/subscription/0000755000175000017500000000000011147555544021615 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/subscription/parser/0000755000175000017500000000000012200662610023071 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/subscription/parser/SubscriptionParserConstants.java0000644000175000017500000000377211147555544031523 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. SubscriptionParserConstants.java */ package org.avis.subscription.parser; public interface SubscriptionParserConstants { int EOF = 0; int IDENTIFIER = 5; int LETTER = 6; int ID_CHAR = 7; int BACKSLASH = 8; int INTEGER_LITERAL = 9; int DECIMAL_LITERAL = 10; int HEX_LITERAL = 11; int HEX_DIGIT = 12; int OCTAL_LITERAL = 13; int REAL_LITERAL = 14; int EXPONENT = 15; int STRING_LITERAL = 16; int DQUOTE_STRING_LITERAL = 17; int SQUOTE_STRING_LITERAL = 18; int BACKSLASH_EXPR = 19; int DEFAULT = 0; String[] tokenImage = { "", "\" \"", "\"\\t\"", "\"\\n\"", "\"\\r\"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "\"||\"", "\"^^\"", "\"&&\"", "\"!\"", "\">\"", "\">=\"", "\"<\"", "\"<=\"", "\"==\"", "\"!=\"", "\"-\"", "\"+\"", "\"*\"", "\"/\"", "\"%\"", "\"<<\"", "\">>\"", "\">>>\"", "\"|\"", "\"^\"", "\"&\"", "\"~\"", "\"(\"", "\")\"", "\",\"", }; } avis-1.2.2/server/src/main/org/avis/subscription/parser/SubscriptionParserTokenManager.java0000644000175000017500000004576111147555544032126 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. SubscriptionParserTokenManager.java */ package org.avis.subscription.parser; import java.util.*; import java.util.regex.PatternSyntaxException; import org.avis.subscription.ast.*; import org.avis.subscription.ast.nodes.*; @SuppressWarnings("all") public class SubscriptionParserTokenManager implements SubscriptionParserConstants { public java.io.PrintStream debugStream = System.out; public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } private final int jjStopStringLiteralDfa_0(int pos, long active0) { switch (pos) { default : return -1; } } private final int jjStartNfa_0(int pos, long active0) { return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); } private final int jjStopAtPos(int pos, int kind) { jjmatchedKind = kind; jjmatchedPos = pos; return pos + 1; } private final int jjStartNfaWithStates_0(int pos, int kind, int state) { jjmatchedKind = kind; jjmatchedPos = pos; try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { return pos + 1; } return jjMoveNfa_0(state, pos + 1); } private final int jjMoveStringLiteralDfa0_0() { switch(curChar) { case 33: jjmatchedKind = 23; return jjMoveStringLiteralDfa1_0(0x20000000L); case 37: return jjStopAtPos(0, 34); case 38: jjmatchedKind = 40; return jjMoveStringLiteralDfa1_0(0x400000L); case 40: return jjStopAtPos(0, 42); case 41: return jjStopAtPos(0, 43); case 42: return jjStopAtPos(0, 32); case 43: return jjStopAtPos(0, 31); case 44: return jjStopAtPos(0, 44); case 45: return jjStopAtPos(0, 30); case 47: return jjStopAtPos(0, 33); case 60: jjmatchedKind = 26; return jjMoveStringLiteralDfa1_0(0x808000000L); case 61: return jjMoveStringLiteralDfa1_0(0x10000000L); case 62: jjmatchedKind = 24; return jjMoveStringLiteralDfa1_0(0x3002000000L); case 94: jjmatchedKind = 39; return jjMoveStringLiteralDfa1_0(0x200000L); case 124: jjmatchedKind = 38; return jjMoveStringLiteralDfa1_0(0x100000L); case 126: return jjStopAtPos(0, 41); default : return jjMoveNfa_0(0, 0); } } private final int jjMoveStringLiteralDfa1_0(long active0) { try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { jjStopStringLiteralDfa_0(0, active0); return 1; } switch(curChar) { case 38: if ((active0 & 0x400000L) != 0L) return jjStopAtPos(1, 22); break; case 60: if ((active0 & 0x800000000L) != 0L) return jjStopAtPos(1, 35); break; case 61: if ((active0 & 0x2000000L) != 0L) return jjStopAtPos(1, 25); else if ((active0 & 0x8000000L) != 0L) return jjStopAtPos(1, 27); else if ((active0 & 0x10000000L) != 0L) return jjStopAtPos(1, 28); else if ((active0 & 0x20000000L) != 0L) return jjStopAtPos(1, 29); break; case 62: if ((active0 & 0x1000000000L) != 0L) { jjmatchedKind = 36; jjmatchedPos = 1; } return jjMoveStringLiteralDfa2_0(active0, 0x2000000000L); case 94: if ((active0 & 0x200000L) != 0L) return jjStopAtPos(1, 21); break; case 124: if ((active0 & 0x100000L) != 0L) return jjStopAtPos(1, 20); break; default : break; } return jjStartNfa_0(0, active0); } private final int jjMoveStringLiteralDfa2_0(long old0, long active0) { if (((active0 &= old0)) == 0L) return jjStartNfa_0(0, old0); try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { jjStopStringLiteralDfa_0(1, active0); return 2; } switch(curChar) { case 62: if ((active0 & 0x2000000000L) != 0L) return jjStopAtPos(2, 37); break; default : break; } return jjStartNfa_0(1, active0); } private final void jjCheckNAdd(int state) { if (jjrounds[state] != jjround) { jjstateSet[jjnewStateCnt++] = state; jjrounds[state] = jjround; } } private final void jjAddStates(int start, int end) { do { jjstateSet[jjnewStateCnt++] = jjnextStates[start]; } while (start++ != end); } private final void jjCheckNAddTwoStates(int state1, int state2) { jjCheckNAdd(state1); jjCheckNAdd(state2); } private final void jjCheckNAddStates(int start, int end) { do { jjCheckNAdd(jjnextStates[start]); } while (start++ != end); } private final void jjCheckNAddStates(int start) { jjCheckNAdd(jjnextStates[start]); jjCheckNAdd(jjnextStates[start + 1]); } static final long[] jjbitVec0 = { 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL }; static final long[] jjbitVec1 = { 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL }; private final int jjMoveNfa_0(int startState, int curPos) { int[] nextStates; int startsAt = 0; jjnewStateCnt = 28; int i = 1; jjstateSet[0] = startState; int j, kind = 0x7fffffff; for (;;) { if (++jjround == 0x7fffffff) ReInitRounds(); if (curChar < 64) { long l = 1L << curChar; MatchLoop: do { switch(jjstateSet[--i]) { case 0: if ((0x3ff000000000000L & l) != 0L) jjCheckNAddTwoStates(8, 9); else if (curChar == 39) jjCheckNAddStates(0, 2); else if (curChar == 34) jjCheckNAddStates(3, 5); if ((0x3fe000000000000L & l) != 0L) { if (kind > 9) kind = 9; jjCheckNAddTwoStates(6, 7); } else if (curChar == 48) { if (kind > 9) kind = 9; jjCheckNAddStates(6, 8); } break; case 1: if ((0xffffec7a00000000L & l) == 0L) break; if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); break; case 3: if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); break; case 5: if ((0x3fe000000000000L & l) == 0L) break; if (kind > 9) kind = 9; jjCheckNAddTwoStates(6, 7); break; case 6: if ((0x3ff000000000000L & l) == 0L) break; if (kind > 9) kind = 9; jjCheckNAddTwoStates(6, 7); break; case 8: if ((0x3ff000000000000L & l) != 0L) jjCheckNAddTwoStates(8, 9); break; case 9: if (curChar == 46) jjCheckNAdd(10); break; case 10: if ((0x3ff000000000000L & l) == 0L) break; if (kind > 14) kind = 14; jjCheckNAddTwoStates(10, 11); break; case 12: if ((0x280000000000L & l) != 0L) jjCheckNAdd(13); break; case 13: if ((0x3ff000000000000L & l) == 0L) break; if (kind > 14) kind = 14; jjCheckNAdd(13); break; case 14: if (curChar == 34) jjCheckNAddStates(3, 5); break; case 15: if ((0xfffffffbffffffffL & l) != 0L) jjCheckNAddStates(3, 5); break; case 17: jjCheckNAddStates(3, 5); break; case 18: if (curChar == 34 && kind > 16) kind = 16; break; case 19: if (curChar == 39) jjCheckNAddStates(0, 2); break; case 20: if ((0xffffff7fffffffffL & l) != 0L) jjCheckNAddStates(0, 2); break; case 22: jjCheckNAddStates(0, 2); break; case 23: if (curChar == 39 && kind > 16) kind = 16; break; case 24: if (curChar != 48) break; if (kind > 9) kind = 9; jjCheckNAddStates(6, 8); break; case 26: if ((0x3ff000000000000L & l) == 0L) break; if (kind > 9) kind = 9; jjCheckNAddTwoStates(26, 7); break; case 27: if ((0xff000000000000L & l) == 0L) break; if (kind > 9) kind = 9; jjCheckNAddTwoStates(27, 7); break; default : break; } } while(i != startsAt); } else if (curChar < 128) { long l = 1L << (curChar & 077); MatchLoop: do { switch(jjstateSet[--i]) { case 0: if ((0x7fffffe87fffffeL & l) != 0L) { if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); } else if (curChar == 92) jjCheckNAdd(3); break; case 1: if ((0xffffffffc7ffffffL & l) == 0L) break; if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); break; case 2: if (curChar == 92) jjCheckNAdd(3); break; case 3: if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); break; case 4: if (curChar == 92) jjCheckNAdd(3); break; case 7: if ((0x100000001000L & l) != 0L && kind > 9) kind = 9; break; case 11: if ((0x2000000020L & l) != 0L) jjAddStates(9, 10); break; case 15: if ((0xffffffffefffffffL & l) != 0L) jjCheckNAddStates(3, 5); break; case 16: if (curChar == 92) jjstateSet[jjnewStateCnt++] = 17; break; case 17: jjCheckNAddStates(3, 5); break; case 20: if ((0xffffffffefffffffL & l) != 0L) jjCheckNAddStates(0, 2); break; case 21: if (curChar == 92) jjstateSet[jjnewStateCnt++] = 22; break; case 22: jjCheckNAddStates(0, 2); break; case 25: if ((0x100000001000000L & l) != 0L) jjCheckNAdd(26); break; case 26: if ((0x7e0000007eL & l) == 0L) break; if (kind > 9) kind = 9; jjCheckNAddTwoStates(26, 7); break; default : break; } } while(i != startsAt); } else { int hiByte = (int)(curChar >> 8); int i1 = hiByte >> 6; long l1 = 1L << (hiByte & 077); int i2 = (curChar & 0xff) >> 6; long l2 = 1L << (curChar & 077); MatchLoop: do { switch(jjstateSet[--i]) { case 1: if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) break; if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); break; case 3: if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) break; if (kind > 5) kind = 5; jjCheckNAddTwoStates(1, 2); break; case 15: case 17: if (jjCanMove_1(hiByte, i1, i2, l1, l2)) jjCheckNAddStates(3, 5); break; case 20: case 22: if (jjCanMove_1(hiByte, i1, i2, l1, l2)) jjCheckNAddStates(0, 2); break; default : break; } } while(i != startsAt); } if (kind != 0x7fffffff) { jjmatchedKind = kind; jjmatchedPos = curPos; kind = 0x7fffffff; } ++curPos; if ((i = jjnewStateCnt) == (startsAt = 28 - (jjnewStateCnt = startsAt))) return curPos; try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { return curPos; } } } static final int[] jjnextStates = { 20, 21, 23, 15, 16, 18, 25, 27, 7, 12, 13, }; private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) { switch(hiByte) { case 0: return ((jjbitVec0[i2] & l2) != 0L); default : return false; } } private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) { switch(hiByte) { case 0: return ((jjbitVec0[i2] & l2) != 0L); default : if ((jjbitVec1[i1] & l1) != 0L) return true; return false; } } public static final String[] jjstrLiteralImages = { "", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "\174\174", "\136\136", "\46\46", "\41", "\76", "\76\75", "\74", "\74\75", "\75\75", "\41\75", "\55", "\53", "\52", "\57", "\45", "\74\74", "\76\76", "\76\76\76", "\174", "\136", "\46", "\176", "\50", "\51", "\54", }; public static final String[] lexStateNames = { "DEFAULT", }; static final long[] jjtoToken = { 0x1ffffff14221L, }; static final long[] jjtoSkip = { 0x1eL, }; protected SimpleCharStream input_stream; private final int[] jjrounds = new int[28]; private final int[] jjstateSet = new int[56]; protected char curChar; public SubscriptionParserTokenManager(SimpleCharStream stream){ if (SimpleCharStream.staticFlag) throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); input_stream = stream; } public SubscriptionParserTokenManager(SimpleCharStream stream, int lexState){ this(stream); SwitchTo(lexState); } public void ReInit(SimpleCharStream stream) { jjmatchedPos = jjnewStateCnt = 0; curLexState = defaultLexState; input_stream = stream; ReInitRounds(); } private final void ReInitRounds() { int i; jjround = 0x80000001; for (i = 28; i-- > 0;) jjrounds[i] = 0x80000000; } public void ReInit(SimpleCharStream stream, int lexState) { ReInit(stream); SwitchTo(lexState); } public void SwitchTo(int lexState) { if (lexState >= 1 || lexState < 0) throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); else curLexState = lexState; } protected Token jjFillToken() { Token t = Token.newToken(jjmatchedKind); t.kind = jjmatchedKind; String im = jjstrLiteralImages[jjmatchedKind]; t.image = (im == null) ? input_stream.GetImage() : im; t.beginLine = input_stream.getBeginLine(); t.beginColumn = input_stream.getBeginColumn(); t.endLine = input_stream.getEndLine(); t.endColumn = input_stream.getEndColumn(); return t; } int curLexState = 0; int defaultLexState = 0; int jjnewStateCnt; int jjround; int jjmatchedPos; int jjmatchedKind; public Token getNextToken() { int kind; Token specialToken = null; Token matchedToken; int curPos = 0; EOFLoop : for (;;) { try { curChar = input_stream.BeginToken(); } catch(java.io.IOException e) { jjmatchedKind = 0; matchedToken = jjFillToken(); return matchedToken; } try { input_stream.backup(0); while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) curChar = input_stream.BeginToken(); } catch (java.io.IOException e1) { continue EOFLoop; } jjmatchedKind = 0x7fffffff; jjmatchedPos = 0; curPos = jjMoveStringLiteralDfa0_0(); if (jjmatchedKind != 0x7fffffff) { if (jjmatchedPos + 1 < curPos) input_stream.backup(curPos - jjmatchedPos - 1); if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { matchedToken = jjFillToken(); return matchedToken; } else { continue EOFLoop; } } int error_line = input_stream.getEndLine(); int error_column = input_stream.getEndColumn(); String error_after = null; boolean EOFSeen = false; try { input_stream.readChar(); input_stream.backup(1); } catch (java.io.IOException e1) { EOFSeen = true; error_after = curPos <= 1 ? "" : input_stream.GetImage(); if (curChar == '\n' || curChar == '\r') { error_line++; error_column = 0; } else error_column++; } if (!EOFSeen) { input_stream.backup(1); error_after = curPos <= 1 ? "" : input_stream.GetImage(); } throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); } } } avis-1.2.2/server/src/main/org/avis/subscription/parser/SubscriptionParser.java0000644000175000017500000006643011147555544027626 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. SubscriptionParser.java */ package org.avis.subscription.parser; import java.util.*; import java.util.regex.PatternSyntaxException; import org.avis.subscription.ast.*; import org.avis.subscription.ast.nodes.*; @SuppressWarnings ("all") public class SubscriptionParser extends SubscriptionParserBase implements SubscriptionParserConstants { Node doParse () throws ParseException { return Start (); } final public Node Start() throws ParseException { Node node; node = SubExp(); jj_consume_token(0); {if (true) return node;} throw new Error("Missing return statement in function"); } final public Node SubExp() throws ParseException { {if (true) return BoolOrExp ();} throw new Error("Missing return statement in function"); } final public Node BoolOrExp() throws ParseException { Node node1; Node node2; Or or = null; node1 = BoolXorExp(); label_1: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 20: ; break; default: jj_la1[0] = jj_gen; break label_1; } jj_consume_token(20); node2 = BoolXorExp(); if (or == null) node1 = or = new Or (node1); or.addChild (node2); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node BoolXorExp() throws ParseException { Node node1; Node node2; Xor xor = null; node1 = BoolAndExp(); label_2: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 21: ; break; default: jj_la1[1] = jj_gen; break label_2; } jj_consume_token(21); node2 = BoolAndExp(); if (xor == null) node1 = xor = new Xor (node1); xor.addChild (node2); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node BoolAndExp() throws ParseException { Node node1; Node node2; And and = null; node1 = BoolExp(); label_3: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 22: ; break; default: jj_la1[2] = jj_gen; break label_3; } jj_consume_token(22); node2 = BoolExp(); if (and == null) node1 = and = new And (node1); and.addChild (node2); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node BoolExp() throws ParseException { Node node1; Node node2; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 23: jj_consume_token(23); node1 = BoolExp(); {if (true) return new Not (node1);} break; case IDENTIFIER: case INTEGER_LITERAL: case REAL_LITERAL: case STRING_LITERAL: case 30: case 31: case 41: case 42: node1 = MathExp(); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 24: case 25: case 26: case 27: case 28: case 29: switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 24: jj_consume_token(24); node2 = MathExp(); {if (true) return new Compare (node1, node2, 1, false);} break; case 25: jj_consume_token(25); node2 = MathExp(); {if (true) return new Compare (node1, node2, 1, true);} break; case 26: jj_consume_token(26); node2 = MathExp(); {if (true) return new Compare (node1, node2, -1, false);} break; case 27: jj_consume_token(27); node2 = MathExp(); {if (true) return new Compare (node1, node2, -1, true);} break; case 28: jj_consume_token(28); node2 = MathExp(); {if (true) return new Compare (node1, node2, 0, true);} break; case 29: jj_consume_token(29); node2 = MathExp(); {if (true) return new Not (new Compare (node1, node2, 0, true));} break; default: jj_la1[3] = jj_gen; jj_consume_token(-1); throw new ParseException(); } break; default: jj_la1[4] = jj_gen; ; } {if (true) return node1;} break; default: jj_la1[5] = jj_gen; jj_consume_token(-1); throw new ParseException(); } throw new Error("Missing return statement in function"); } final public Node MathExp() throws ParseException { Node node1; node1 = MathAddExp(); label_4: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 30: ; break; default: jj_la1[6] = jj_gen; break label_4; } jj_consume_token(30); node1 = new MathMinus (node1, MathAddExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathAddExp() throws ParseException { Node node1; node1 = MathMultExp(); label_5: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 31: ; break; default: jj_la1[7] = jj_gen; break label_5; } jj_consume_token(31); node1 = new MathPlus (node1, MathMultExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathMultExp() throws ParseException { Node node1; node1 = MathDivExp(); label_6: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 32: ; break; default: jj_la1[8] = jj_gen; break label_6; } jj_consume_token(32); node1 = new MathMult (node1, MathDivExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathDivExp() throws ParseException { Node node1; node1 = MathModExp(); label_7: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 33: ; break; default: jj_la1[9] = jj_gen; break label_7; } jj_consume_token(33); node1 = new MathDiv (node1, MathModExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathModExp() throws ParseException { Node node1; node1 = MathBitShiftLeftExp(); label_8: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 34: ; break; default: jj_la1[10] = jj_gen; break label_8; } jj_consume_token(34); node1 = new MathMod (node1, MathBitShiftLeftExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathBitShiftLeftExp() throws ParseException { Node node1; node1 = MathBitShiftRightExp(); label_9: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 35: ; break; default: jj_la1[11] = jj_gen; break label_9; } jj_consume_token(35); node1 = new MathBitShiftLeft (node1, MathBitShiftRightExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathBitShiftRightExp() throws ParseException { Node node1; node1 = MathBitLogShiftRightExp(); label_10: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 36: ; break; default: jj_la1[12] = jj_gen; break label_10; } jj_consume_token(36); node1 = new MathBitShiftRight (node1, MathBitLogShiftRightExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathBitLogShiftRightExp() throws ParseException { Node node1; node1 = MathBitOrExp(); label_11: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 37: ; break; default: jj_la1[13] = jj_gen; break label_11; } jj_consume_token(37); node1 = new MathBitLogShiftRight (node1, MathBitOrExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathBitOrExp() throws ParseException { Node node1; node1 = MathBitXorExp(); label_12: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 38: ; break; default: jj_la1[14] = jj_gen; break label_12; } jj_consume_token(38); node1 = new MathBitOr (node1, MathBitXorExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathBitXorExp() throws ParseException { Node node1; node1 = MathBitAndExp(); label_13: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 39: ; break; default: jj_la1[15] = jj_gen; break label_13; } jj_consume_token(39); node1 = new MathBitXor (node1, MathBitAndExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node MathBitAndExp() throws ParseException { Node node1; node1 = ValueExp(); label_14: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 40: ; break; default: jj_la1[16] = jj_gen; break label_14; } jj_consume_token(40); node1 = new MathBitAnd (node1, ValueExp ()); } {if (true) return node1;} throw new Error("Missing return statement in function"); } final public Node ValueExp() throws ParseException { Node node; if (jj_2_1(2)) { // differentiate between Name and Function node = Function(); {if (true) return node;} } else { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IDENTIFIER: node = Name(); {if (true) return node;} break; case INTEGER_LITERAL: case REAL_LITERAL: node = NumLiteral(); {if (true) return node;} break; case STRING_LITERAL: node = StringLiteral(); {if (true) return node;} break; case 30: jj_consume_token(30); node = ValueExp(); {if (true) return new MathUnaryMinus (node);} break; case 31: jj_consume_token(31); node = ValueExp(); {if (true) return node;} break; case 41: jj_consume_token(41); node = ValueExp(); {if (true) return new MathBitInvert (node);} break; case 42: jj_consume_token(42); node = SubExp(); jj_consume_token(43); {if (true) return node;} break; default: jj_la1[17] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } throw new Error("Missing return statement in function"); } final public Node Function() throws ParseException { Token t; Node node; String func; t = jj_consume_token(IDENTIFIER); jj_consume_token(42); func = t.image; if (func.equals ("begins-with")) node = StrBeginsWith.create (StringCompareArgs ()); else if (func.equals ("ends-with")) node = StrEndsWith.create (StringCompareArgs ()); else if (func.equals ("contains")) node = StrContains.create (StringCompareArgs ()); else if (func.equals ("regex")) node = StrRegex.create (StringCompareArgs ()); else if (func.equals ("fold-case")) node = new StrFoldCase (StringValue ()); else if (func.equals ("equals")) node = Compare.createEquals (CompareArgs ()); else if (func.equals ("require")) node = new Require (Name ()); else if (func.equals ("int32")) node = new Type (Name (), Integer.class); else if (func.equals ("int64")) node = new Type (Name (), Long.class); else if (func.equals ("real64")) node = new Type (Name (), Double.class); else if (func.equals ("string")) node = new Type (Name (), String.class); else if (func.equals ("opaque")) node = new Type (Name (), byte [].class); else if (func.equals ("nan")) node = new Nan (Name ()); else if (func.equals ("wildcard")) node = StrWildcard.create (StringCompareArgs ()); else if (func.equals ("size")) node = new Size (Name ()); else if (func.equals ("decompose") || func.equals ("decompose-compat")) node = new StrUnicodeDecompose (StringValue (), func.equals ("decompose") ? StrUnicodeDecompose.Mode.DECOMPOSE : StrUnicodeDecompose.Mode.DECOMPOSE_COMPAT); else { node = null; AnyArgs (); // error recover by skipping args } jj_consume_token(43); if (node == null) {if (true) throw new ParseException ("Unknown function: " + func);} else {if (true) return node;} throw new Error("Missing return statement in function"); } final public Node StringValue() throws ParseException { Node node; if (jj_2_2(2)) { node = Function(); } else { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case STRING_LITERAL: node = StringLiteral(); break; case IDENTIFIER: node = Name(); break; default: jj_la1[18] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } {if (true) return node;} throw new Error("Missing return statement in function"); } // Args for the compare() function final public List CompareArgs() throws ParseException { ArrayList args = new ArrayList (); args.add (StringValue ()); label_15: while (true) { jj_consume_token(44); args.add (MathExp ()); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 44: ; break; default: jj_la1[19] = jj_gen; break label_15; } } {if (true) return args;} throw new Error("Missing return statement in function"); } // Args for any string comparison function final public List StringCompareArgs() throws ParseException { ArrayList args = new ArrayList (); args.add (StringValue ()); label_16: while (true) { jj_consume_token(44); args.add (StringLiteral ()); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 44: ; break; default: jj_la1[20] = jj_gen; break label_16; } } {if (true) return args;} throw new Error("Missing return statement in function"); } // Match any arguments. Used for error recovery final public void AnyArgs() throws ParseException { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IDENTIFIER: case INTEGER_LITERAL: case REAL_LITERAL: case STRING_LITERAL: switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IDENTIFIER: case STRING_LITERAL: StringValue(); break; case INTEGER_LITERAL: case REAL_LITERAL: NumLiteral(); break; default: jj_la1[21] = jj_gen; jj_consume_token(-1); throw new ParseException(); } label_17: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 44: ; break; default: jj_la1[22] = jj_gen; break label_17; } jj_consume_token(44); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IDENTIFIER: case STRING_LITERAL: StringValue(); break; case INTEGER_LITERAL: case REAL_LITERAL: NumLiteral(); break; default: jj_la1[23] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } break; default: jj_la1[24] = jj_gen; ; } } final public Const NumLiteral() throws ParseException { Token t; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case INTEGER_LITERAL: t = jj_consume_token(INTEGER_LITERAL); Number value; char lastChar = t.image.charAt (t.image.length () - 1); if (lastChar == 'l' || lastChar == 'L') value = Long.decode (t.image.substring (0, t.image.length () - 1)); else value = Integer.decode (t.image); {if (true) return new Const (value);} break; case REAL_LITERAL: t = jj_consume_token(REAL_LITERAL); {if (true) return new Const (new Double (t.image));} break; default: jj_la1[25] = jj_gen; jj_consume_token(-1); throw new ParseException(); } throw new Error("Missing return statement in function"); } final public Field Name() throws ParseException { Token t; t = jj_consume_token(IDENTIFIER); {if (true) return new Field (stripBackslashes (t.image));} throw new Error("Missing return statement in function"); } final public Const StringLiteral() throws ParseException { Token t; t = jj_consume_token(STRING_LITERAL); {if (true) return new Const (stripBackslashes (t.image.substring (1, t.image.length () - 1)));} throw new Error("Missing return statement in function"); } final private boolean jj_2_1(int xla) { jj_la = xla; jj_lastpos = jj_scanpos = token; try { return !jj_3_1(); } catch(LookaheadSuccess ls) { return true; } finally { jj_save(0, xla); } } final private boolean jj_2_2(int xla) { jj_la = xla; jj_lastpos = jj_scanpos = token; try { return !jj_3_2(); } catch(LookaheadSuccess ls) { return true; } finally { jj_save(1, xla); } } final private boolean jj_3_2() { if (jj_3R_18()) return true; return false; } final private boolean jj_3_1() { if (jj_3R_18()) return true; return false; } final private boolean jj_3R_18() { if (jj_scan_token(IDENTIFIER)) return true; if (jj_scan_token(42)) return true; return false; } public SubscriptionParserTokenManager token_source; SimpleCharStream jj_input_stream; public Token token, jj_nt; private int jj_ntk; private Token jj_scanpos, jj_lastpos; private int jj_la; public boolean lookingAhead = false; private boolean jj_semLA; private int jj_gen; final private int[] jj_la1 = new int[26]; static private int[] jj_la1_0; static private int[] jj_la1_1; static { jj_la1_0(); jj_la1_1(); } private static void jj_la1_0() { jj_la1_0 = new int[] {0x100000,0x200000,0x400000,0x3f000000,0x3f000000,0xc0814220,0x40000000,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0014220,0x10020,0x0,0x0,0x14220,0x0,0x14220,0x14220,0x4200,}; } private static void jj_la1_1() { jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x100,0x600,0x0,0x1000,0x1000,0x0,0x1000,0x0,0x0,0x0,}; } final private JJCalls[] jj_2_rtns = new JJCalls[2]; private boolean jj_rescan = false; private int jj_gc = 0; public SubscriptionParser(java.io.InputStream stream) { this(stream, null); } public SubscriptionParser(java.io.InputStream stream, String encoding) { try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } token_source = new SubscriptionParserTokenManager(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for (int i = 0; i < 26; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } public void ReInit(java.io.InputStream stream) { ReInit(stream, null); } public void ReInit(java.io.InputStream stream, String encoding) { try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } token_source.ReInit(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for (int i = 0; i < 26; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } public SubscriptionParser(java.io.Reader stream) { jj_input_stream = new SimpleCharStream(stream, 1, 1); token_source = new SubscriptionParserTokenManager(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for (int i = 0; i < 26; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } public void ReInit(java.io.Reader stream) { jj_input_stream.ReInit(stream, 1, 1); token_source.ReInit(jj_input_stream); token = new Token(); jj_ntk = -1; jj_gen = 0; for (int i = 0; i < 26; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } public SubscriptionParser(SubscriptionParserTokenManager tm) { token_source = tm; token = new Token(); jj_ntk = -1; jj_gen = 0; for (int i = 0; i < 26; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } public void ReInit(SubscriptionParserTokenManager tm) { token_source = tm; token = new Token(); jj_ntk = -1; jj_gen = 0; for (int i = 0; i < 26; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } final private Token jj_consume_token(int kind) throws ParseException { Token oldToken; if ((oldToken = token).next != null) token = token.next; else token = token.next = token_source.getNextToken(); jj_ntk = -1; if (token.kind == kind) { jj_gen++; if (++jj_gc > 100) { jj_gc = 0; for (int i = 0; i < jj_2_rtns.length; i++) { JJCalls c = jj_2_rtns[i]; while (c != null) { if (c.gen < jj_gen) c.first = null; c = c.next; } } } return token; } token = oldToken; jj_kind = kind; throw generateParseException(); } static private final class LookaheadSuccess extends java.lang.Error { } final private LookaheadSuccess jj_ls = new LookaheadSuccess(); final private boolean jj_scan_token(int kind) { if (jj_scanpos == jj_lastpos) { jj_la--; if (jj_scanpos.next == null) { jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); } else { jj_lastpos = jj_scanpos = jj_scanpos.next; } } else { jj_scanpos = jj_scanpos.next; } if (jj_rescan) { int i = 0; Token tok = token; while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; } if (tok != null) jj_add_error_token(kind, i); } if (jj_scanpos.kind != kind) return true; if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls; return false; } final public Token getNextToken() { if (token.next != null) token = token.next; else token = token.next = token_source.getNextToken(); jj_ntk = -1; jj_gen++; return token; } final public Token getToken(int index) { Token t = lookingAhead ? jj_scanpos : token; for (int i = 0; i < index; i++) { if (t.next != null) t = t.next; else t = t.next = token_source.getNextToken(); } return t; } final private int jj_ntk() { if ((jj_nt=token.next) == null) return (jj_ntk = (token.next=token_source.getNextToken()).kind); else return (jj_ntk = jj_nt.kind); } private java.util.Vector jj_expentries = new java.util.Vector(); private int[] jj_expentry; private int jj_kind = -1; private int[] jj_lasttokens = new int[100]; private int jj_endpos; private void jj_add_error_token(int kind, int pos) { if (pos >= 100) return; if (pos == jj_endpos + 1) { jj_lasttokens[jj_endpos++] = kind; } else if (jj_endpos != 0) { jj_expentry = new int[jj_endpos]; for (int i = 0; i < jj_endpos; i++) { jj_expentry[i] = jj_lasttokens[i]; } boolean exists = false; for (java.util.Enumeration e = jj_expentries.elements(); e.hasMoreElements();) { int[] oldentry = (int[])(e.nextElement()); if (oldentry.length == jj_expentry.length) { exists = true; for (int i = 0; i < jj_expentry.length; i++) { if (oldentry[i] != jj_expentry[i]) { exists = false; break; } } if (exists) break; } } if (!exists) jj_expentries.addElement(jj_expentry); if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind; } } public ParseException generateParseException() { jj_expentries.removeAllElements(); boolean[] la1tokens = new boolean[45]; for (int i = 0; i < 45; i++) { la1tokens[i] = false; } if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } for (int i = 0; i < 26; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1< jj_gen) { jj_la = p.arg; jj_lastpos = jj_scanpos = p.first; switch (i) { case 0: jj_3_1(); break; case 1: jj_3_2(); break; } } p = p.next; } while (p != null); } catch(LookaheadSuccess ls) { } } jj_rescan = false; } final private void jj_save(int index, int xla) { JJCalls p = jj_2_rtns[index]; while (p.gen > jj_gen) { if (p.next == null) { p = p.next = new JJCalls(); break; } p = p.next; } p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla; } static final class JJCalls { int gen; Token first; int arg; JJCalls next; } } avis-1.2.2/server/src/main/org/avis/subscription/parser/ConstantExpressionException.java0000644000175000017500000000166111147555544031510 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.parser; /** * Thrown by parser when subscription expression is constant. */ public class ConstantExpressionException extends ParseException { public ConstantExpressionException (String message) { super (message); } } avis-1.2.2/server/src/main/org/avis/subscription/parser/ParseException.java0000644000175000017500000001571611147555544026717 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */ package org.avis.subscription.parser; /** * This exception is thrown when parse errors are encountered. * You can explicitly create objects of this exception type by * calling the method generateParseException in the generated * parser. * * You can modify this class to customize your error reporting * mechanisms so long as you retain the public fields. */ public class ParseException extends Exception { /** * This constructor is used by the method "generateParseException" * in the generated parser. Calling this constructor generates * a new object of this type with the fields "currentToken", * "expectedTokenSequences", and "tokenImage" set. The boolean * flag "specialConstructor" is also set to true to indicate that * this constructor was used to create this object. * This constructor calls its super class with the empty string * to force the "toString" method of parent class "Throwable" to * print the error message in the form: * ParseException: */ public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal ) { super(""); specialConstructor = true; currentToken = currentTokenVal; expectedTokenSequences = expectedTokenSequencesVal; tokenImage = tokenImageVal; } /** * The following constructors are for use by you for whatever * purpose you can think of. Constructing the exception in this * manner makes the exception behave in the normal way - i.e., as * documented in the class "Throwable". The fields "errorToken", * "expectedTokenSequences", and "tokenImage" do not contain * relevant information. The JavaCC generated code does not use * these constructors. */ public ParseException() { super(); specialConstructor = false; } public ParseException(String message) { super(message); specialConstructor = false; } /** * This variable determines which constructor was used to create * this object and thereby affects the semantics of the * "getMessage" method (see below). */ protected boolean specialConstructor; /** * This is the last token that has been consumed successfully. If * this object has been created due to a parse error, the token * followng this token will (therefore) be the first error token. */ public Token currentToken; /** * Each entry in this array is an array of integers. Each array * of integers represents a sequence of tokens (by their ordinal * values) that is expected at this point of the parse. */ public int[][] expectedTokenSequences; /** * This is a reference to the "tokenImage" array of the generated * parser within which the parse error occurred. This array is * defined in the generated ...Constants interface. */ public String[] tokenImage; /** * This method has the standard behavior when this object has been * created using the standard constructors. Otherwise, it uses * "currentToken" and "expectedTokenSequences" to generate a parse * error message and returns it. If this object has been created * due to a parse error, and you do not catch it (it gets thrown * from the parser), then this method is called during the printing * of the final stack trace, and hence the correct error message * gets displayed. */ public String getMessage() { if (!specialConstructor) { return super.getMessage(); } StringBuffer expected = new StringBuffer(); int maxSize = 0; for (int i = 0; i < expectedTokenSequences.length; i++) { if (maxSize < expectedTokenSequences[i].length) { maxSize = expectedTokenSequences[i].length; } for (int j = 0; j < expectedTokenSequences[i].length; j++) { expected.append(tokenImage[expectedTokenSequences[i][j]]).append(" "); } if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { expected.append("..."); } expected.append(eol).append(" "); } String retval = "Encountered \""; Token tok = currentToken.next; for (int i = 0; i < maxSize; i++) { if (i != 0) retval += " "; if (tok.kind == 0) { retval += tokenImage[0]; break; } retval += add_escapes(tok.image); tok = tok.next; } retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; retval += "." + eol; if (expectedTokenSequences.length == 1) { retval += "Was expecting:" + eol + " "; } else { retval += "Was expecting one of:" + eol + " "; } retval += expected.toString(); return retval; } /** * The end of line string for this machine. */ protected String eol = System.getProperty("line.separator", "\n"); /** * Used to convert raw characters to their escaped version * when these raw version cannot be used as part of an ASCII * string literal. */ protected String add_escapes(String str) { StringBuffer retval = new StringBuffer(); char ch; for (int i = 0; i < str.length(); i++) { switch (str.charAt(i)) { case 0 : continue; case '\b': retval.append("\\b"); continue; case '\t': retval.append("\\t"); continue; case '\n': retval.append("\\n"); continue; case '\f': retval.append("\\f"); continue; case '\r': retval.append("\\r"); continue; case '\"': retval.append("\\\""); continue; case '\'': retval.append("\\\'"); continue; case '\\': retval.append("\\\\"); continue; default: if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { String s = "0000" + Integer.toString(ch, 16); retval.append("\\u" + s.substring(s.length() - 4, s.length())); } else { retval.append(ch); } continue; } } return retval.toString(); } } avis-1.2.2/server/src/main/org/avis/subscription/parser/SubscriptionParserBase.java0000644000175000017500000001020711147555544030410 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.parser; import java.util.regex.PatternSyntaxException; import java.io.StringReader; import org.avis.subscription.ast.IllegalChildException; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.nodes.Const; import org.avis.util.InvalidFormatException; import org.avis.util.Text; public abstract class SubscriptionParserBase { /** * Convenience method to fully parse and validate a subscription * expression. * * @param subExpr The subscription expression. * * @return The root of the AST. * * @throws ParseException if the expression is invalid. */ public static Node parse (String subExpr) throws ParseException { return new SubscriptionParser (new StringReader (subExpr)).parseAndValidate (); } abstract Node doParse () throws ParseException; /** * Parse an expression with syntax and basic type checking. Does not check * for boolean result type or const-ness. See {@link #parseAndValidate} if * you want a guaranteed correct subscription expression. */ public Node parse () throws ParseException { try { return doParse (); } catch (IllegalChildException ex) { throw new ParseException ("Illegal expression: " + ex.getMessage ()); } catch (TokenMgrError ex) { throw new ParseException (ex.getMessage ()); } catch (PatternSyntaxException ex) { // regex () had an invalid pattern throw new ParseException ("Invalid regex \"" + ex.getPattern () + "\": " + ex.getDescription ()); } } /** * Execute {@link #parse()} and validate the result is a * type-correct, non-constant subscription expression. * * @throws ParseException if parse () fails or if the resulting * expression is not a valid subscription. * @throws ConstantExpressionException if the expression is contant. */ public Node parseAndValidate () throws ParseException, ConstantExpressionException { Node node = parse ().inlineConstants (); if (node.evalType () == Boolean.class) { if (!(node instanceof Const)) return node; else throw new ConstantExpressionException ("Expression is constant: " + node.expr ()); } else { throw new ParseException ("Expression does not evaluate to boolean: " + node.expr ()); } } /** * Strip backslashed codes from a string. */ protected static String stripBackslashes (String text) throws ParseException { try { return Text.stripBackslashes (text); } catch (InvalidFormatException ex) { throw new ParseException ("Error in string: " + ex.getMessage ()); } } protected static String className (Class type) { return Text.className (type); } /** * Generate a string describing the expected tokens from token info * in a parse exception. Cribbed from the ParseException.getMessage () * method. */ public static String expectedTokensFor (ParseException ex) { StringBuilder expected = new StringBuilder (); boolean first = true; for (int i = 0; i < ex.expectedTokenSequences.length; i++) { if (!first) expected.append (", "); first = false; for (int j = 0; j < ex.expectedTokenSequences [i].length; j++) expected.append (ex.tokenImage [ex.expectedTokenSequences [i] [j]]); } return expected.toString (); } } avis-1.2.2/server/src/main/org/avis/subscription/parser/Token.java0000644000175000017500000000644011147555544025040 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */ package org.avis.subscription.parser; /** * Describes the input token stream. */ public class Token { /** * An integer that describes the kind of this token. This numbering * system is determined by JavaCCParser, and a table of these numbers is * stored in the file ...Constants.java. */ public int kind; /** * beginLine and beginColumn describe the position of the first character * of this token; endLine and endColumn describe the position of the * last character of this token. */ public int beginLine, beginColumn, endLine, endColumn; /** * The string image of the token. */ public String image; /** * A reference to the next regular (non-special) token from the input * stream. If this is the last token from the input stream, or if the * token manager has not read tokens beyond this one, this field is * set to null. This is true only if this token is also a regular * token. Otherwise, see below for a description of the contents of * this field. */ public Token next; /** * This field is used to access special tokens that occur prior to this * token, but after the immediately preceding regular (non-special) token. * If there are no such special tokens, this field is set to null. * When there are more than one such special token, this field refers * to the last of these special tokens, which in turn refers to the next * previous special token through its specialToken field, and so on * until the first special token (whose specialToken field is null). * The next fields of special tokens refer to other special tokens that * immediately follow it (without an intervening regular token). If there * is no such token, this field is null. */ public Token specialToken; /** * Returns the image. */ public String toString() { return image; } /** * Returns a new Token object, by default. However, if you want, you * can create and return subclass objects based on the value of ofKind. * Simply add the cases to the switch for all those special cases. * For example, if you have a subclass of Token called IDToken that * you want to create if ofKind is ID, simlpy add something like : * * case MyParserConstants.ID : return new IDToken(); * * to the following switch statement. Then you can cast matchedToken * variable to the appropriate type and use it in your lexical actions. */ public static final Token newToken(int ofKind) { switch(ofKind) { default : return new Token(); } } } avis-1.2.2/server/src/main/org/avis/subscription/parser/SimpleCharStream.java0000644000175000017500000002715611147555544027172 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 4.0 */ package org.avis.subscription.parser; /** * An implementation of interface CharStream, where the stream is assumed to * contain only ASCII characters (without unicode processing). */ @SuppressWarnings("all") public class SimpleCharStream { public static final boolean staticFlag = false; int bufsize; int available; int tokenBegin; public int bufpos = -1; protected int bufline[]; protected int bufcolumn[]; protected int column = 0; protected int line = 1; protected boolean prevCharIsCR = false; protected boolean prevCharIsLF = false; protected java.io.Reader inputStream; protected char[] buffer; protected int maxNextCharInd = 0; protected int inBuf = 0; protected int tabSize = 8; protected void setTabSize(int i) { tabSize = i; } protected int getTabSize(int i) { return tabSize; } protected void ExpandBuff(boolean wrapAround) { char[] newbuffer = new char[bufsize + 2048]; int newbufline[] = new int[bufsize + 2048]; int newbufcolumn[] = new int[bufsize + 2048]; try { if (wrapAround) { System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); buffer = newbuffer; System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); bufline = newbufline; System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); bufcolumn = newbufcolumn; maxNextCharInd = (bufpos += (bufsize - tokenBegin)); } else { System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); buffer = newbuffer; System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); bufline = newbufline; System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); bufcolumn = newbufcolumn; maxNextCharInd = (bufpos -= tokenBegin); } } catch (Throwable t) { throw new Error(t.getMessage()); } bufsize += 2048; available = bufsize; tokenBegin = 0; } protected void FillBuff() throws java.io.IOException { if (maxNextCharInd == available) { if (available == bufsize) { if (tokenBegin > 2048) { bufpos = maxNextCharInd = 0; available = tokenBegin; } else if (tokenBegin < 0) bufpos = maxNextCharInd = 0; else ExpandBuff(false); } else if (available > tokenBegin) available = bufsize; else if ((tokenBegin - available) < 2048) ExpandBuff(true); else available = tokenBegin; } int i; try { if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) { inputStream.close(); throw new java.io.IOException(); } else maxNextCharInd += i; return; } catch(java.io.IOException e) { --bufpos; backup(0); if (tokenBegin == -1) tokenBegin = bufpos; throw e; } } public char BeginToken() throws java.io.IOException { tokenBegin = -1; char c = readChar(); tokenBegin = bufpos; return c; } protected void UpdateLineColumn(char c) { column++; if (prevCharIsLF) { prevCharIsLF = false; line += (column = 1); } else if (prevCharIsCR) { prevCharIsCR = false; if (c == '\n') { prevCharIsLF = true; } else line += (column = 1); } switch (c) { case '\r' : prevCharIsCR = true; break; case '\n' : prevCharIsLF = true; break; case '\t' : column--; column += (tabSize - (column % tabSize)); break; default : break; } bufline[bufpos] = line; bufcolumn[bufpos] = column; } public char readChar() throws java.io.IOException { if (inBuf > 0) { --inBuf; if (++bufpos == bufsize) bufpos = 0; return buffer[bufpos]; } if (++bufpos >= maxNextCharInd) FillBuff(); char c = buffer[bufpos]; UpdateLineColumn(c); return (c); } /** * @deprecated * @see #getEndColumn */ public int getColumn() { return bufcolumn[bufpos]; } /** * @deprecated * @see #getEndLine */ public int getLine() { return bufline[bufpos]; } public int getEndColumn() { return bufcolumn[bufpos]; } public int getEndLine() { return bufline[bufpos]; } public int getBeginColumn() { return bufcolumn[tokenBegin]; } public int getBeginLine() { return bufline[tokenBegin]; } public void backup(int amount) { inBuf += amount; if ((bufpos -= amount) < 0) bufpos += bufsize; } public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { inputStream = dstream; line = startline; column = startcolumn - 1; available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; bufcolumn = new int[buffersize]; } public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn) { this(dstream, startline, startcolumn, 4096); } public SimpleCharStream(java.io.Reader dstream) { this(dstream, 1, 1, 4096); } public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { inputStream = dstream; line = startline; column = startcolumn - 1; if (buffer == null || buffersize != buffer.length) { available = bufsize = buffersize; buffer = new char[buffersize]; bufline = new int[buffersize]; bufcolumn = new int[buffersize]; } prevCharIsLF = prevCharIsCR = false; tokenBegin = inBuf = maxNextCharInd = 0; bufpos = -1; } public void ReInit(java.io.Reader dstream, int startline, int startcolumn) { ReInit(dstream, startline, startcolumn, 4096); } public void ReInit(java.io.Reader dstream) { ReInit(dstream, 1, 1, 4096); } public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException { this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); } public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException { this(dstream, encoding, startline, startcolumn, 4096); } public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn) { this(dstream, startline, startcolumn, 4096); } public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException { this(dstream, encoding, 1, 1, 4096); } public SimpleCharStream(java.io.InputStream dstream) { this(dstream, 1, 1, 4096); } public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException { ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); } public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException { ReInit(dstream, encoding, 1, 1, 4096); } public void ReInit(java.io.InputStream dstream) { ReInit(dstream, 1, 1, 4096); } public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException { ReInit(dstream, encoding, startline, startcolumn, 4096); } public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) { ReInit(dstream, startline, startcolumn, 4096); } public String GetImage() { if (bufpos >= tokenBegin) return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); else return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); } public char[] GetSuffix(int len) { char[] ret = new char[len]; if ((bufpos + 1) >= len) System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); else { System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); } return ret; } public void Done() { buffer = null; bufline = null; bufcolumn = null; } /** * Method to adjust line and column numbers for the start of a token. */ public void adjustBeginLineColumn(int newLine, int newCol) { int start = tokenBegin; int len; if (bufpos >= tokenBegin) { len = bufpos - tokenBegin + inBuf + 1; } else { len = bufsize - tokenBegin + bufpos + 1 + inBuf; } int i = 0, j = 0, k = 0; int nextColDiff = 0, columnDiff = 0; while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) { bufline[j] = newLine; nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; bufcolumn[j] = newCol + columnDiff; columnDiff = nextColDiff; i++; } if (i < len) { bufline[j] = newLine++; bufcolumn[j] = newCol + columnDiff; while (i++ < len) { if (bufline[j = start % bufsize] != bufline[++start % bufsize]) bufline[j] = newLine++; else bufline[j] = newLine; } } line = bufline[j]; column = bufcolumn[j]; } } avis-1.2.2/server/src/main/org/avis/subscription/parser/TokenMgrError.java0000644000175000017500000001150111147555544026512 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */ package org.avis.subscription.parser; @SuppressWarnings("all") public class TokenMgrError extends Error { /* * Ordinals for various reasons why an Error of this type can be thrown. */ /** * Lexical error occured. */ static final int LEXICAL_ERROR = 0; /** * An attempt wass made to create a second instance of a static token manager. */ static final int STATIC_LEXER_ERROR = 1; /** * Tried to change to an invalid lexical state. */ static final int INVALID_LEXICAL_STATE = 2; /** * Detected (and bailed out of) an infinite loop in the token manager. */ static final int LOOP_DETECTED = 3; /** * Indicates the reason why the exception is thrown. It will have * one of the above 4 values. */ int errorCode; /** * Replaces unprintable characters by their espaced (or unicode escaped) * equivalents in the given string */ protected static final String addEscapes(String str) { StringBuffer retval = new StringBuffer(); char ch; for (int i = 0; i < str.length(); i++) { switch (str.charAt(i)) { case 0 : continue; case '\b': retval.append("\\b"); continue; case '\t': retval.append("\\t"); continue; case '\n': retval.append("\\n"); continue; case '\f': retval.append("\\f"); continue; case '\r': retval.append("\\r"); continue; case '\"': retval.append("\\\""); continue; case '\'': retval.append("\\\'"); continue; case '\\': retval.append("\\\\"); continue; default: if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { String s = "0000" + Integer.toString(ch, 16); retval.append("\\u" + s.substring(s.length() - 4, s.length())); } else { retval.append(ch); } continue; } } return retval.toString(); } /** * Returns a detailed message for the Error when it is thrown by the * token manager to indicate a lexical error. * Parameters : * EOFSeen : indicates if EOF caused the lexicl error * curLexState : lexical state in which this error occured * errorLine : line number when the error occured * errorColumn : column number when the error occured * errorAfter : prefix that was seen before this error occured * curchar : the offending character * Note: You can customize the lexical error message by modifying this method. */ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { return("Lexical error at line " + errorLine + ", column " + errorColumn + ". Encountered: " + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") + "after : \"" + addEscapes(errorAfter) + "\""); } /** * You can also modify the body of this method to customize your error messages. * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not * of end-users concern, so you can return something like : * * "Internal Error : Please file a bug report .... " * * from this method for such cases in the release version of your parser. */ public String getMessage() { return super.getMessage(); } /* * Constructors of various flavors follow. */ public TokenMgrError() { } public TokenMgrError(String message, int reason) { super(message); errorCode = reason; } public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); } } avis-1.2.2/server/src/main/org/avis/subscription/parser/SubscriptionParser.jj0000644000175000017500000002362111147555544027303 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * JavaCC spec for the Avis subscription parser. Requires JavaCC 4.0 or later. */ options { JDK_VERSION = "1.5"; STATIC = false; UNICODE_INPUT = true; } PARSER_BEGIN (SubscriptionParser) package org.avis.subscription.parser; import java.util.*; import java.util.regex.PatternSyntaxException; import org.avis.subscription.ast.*; import org.avis.subscription.ast.nodes.*; @SuppressWarnings ("all") public class SubscriptionParser extends SubscriptionParserBase { Node doParse () throws ParseException { return Start (); } } PARSER_END (SubscriptionParser) SKIP : { " " | "\t" | "\n" | "\r" } TOKEN : /* IDENTIFIERS */ { < IDENTIFIER: (|) (||)* > | < #LETTER: [ "a"-"z", "A"-"Z", "_"] > | < #ID_CHAR: [ "\u0021", "\u0023"-"\u0026", "\u002a"-"\u002b", "\u002d"-"\u005a", "\u005e"-"\u00ff" ] > | < #BACKSLASH: "\\" ~[] > } TOKEN : /* LITERALS */ { < INTEGER_LITERAL: (["l","L"])? | (["l","L"])? | (["l","L"])? > | < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* > | < #HEX_LITERAL: "0" ["x","X"] ()+ > | < #HEX_DIGIT: ["0"-"9","a"-"f","A"-"F"] > | < #OCTAL_LITERAL: "0" (["0"-"7"])* > | < REAL_LITERAL: (["0"-"9"])+ "." (["0"-"9"])+ ()? > | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | < STRING_LITERAL: | > | < #DQUOTE_STRING_LITERAL: "\"" (~["\"", "\\"] | )* "\"" > | < #SQUOTE_STRING_LITERAL: "'" (~["'", "\\"] | )* "'" > | < #BACKSLASH_EXPR: "\\" ~[] > } Node Start () : { Node node; } { node = SubExp () { return node; } } Node SubExp (): {} { { return BoolOrExp (); } } Node BoolOrExp (): { Node node1; Node node2; Or or = null; } { node1 = BoolXorExp () ("||" node2 = BoolXorExp () { if (or == null) node1 = or = new Or (node1); or.addChild (node2); })* { return node1; } } Node BoolXorExp (): { Node node1; Node node2; Xor xor = null; } { node1 = BoolAndExp () ("^^" node2 = BoolAndExp () { if (xor == null) node1 = xor = new Xor (node1); xor.addChild (node2); })* { return node1; } } Node BoolAndExp (): { Node node1; Node node2; And and = null; } { node1 = BoolExp () ("&&" node2 = BoolExp () { if (and == null) node1 = and = new And (node1); and.addChild (node2); })* { return node1; } } Node BoolExp () : { Node node1; Node node2; } { "!" node1 = BoolExp () {return new Not (node1);} | node1 = MathExp () ( ">" node2 = MathExp () {return new Compare (node1, node2, 1, false);} | ">=" node2 = MathExp () {return new Compare (node1, node2, 1, true);} | "<" node2 = MathExp () {return new Compare (node1, node2, -1, false);} | "<=" node2 = MathExp () {return new Compare (node1, node2, -1, true);} | "==" node2 = MathExp () {return new Compare (node1, node2, 0, true);} | "!=" node2 = MathExp () {return new Not (new Compare (node1, node2, 0, true));} )? {return node1;} } Node MathExp () : { Node node1; } { node1 = MathAddExp () ( "-" { node1 = new MathMinus (node1, MathAddExp ()); } )* { return node1; } } Node MathAddExp () : { Node node1; } { node1 = MathMultExp () ( "+" { node1 = new MathPlus (node1, MathMultExp ()); } )* { return node1; } } Node MathMultExp () : { Node node1; } { node1 = MathDivExp () ( "*" { node1 = new MathMult (node1, MathDivExp ()); } )* { return node1; } } Node MathDivExp () : { Node node1; } { node1 = MathModExp () ( "/" { node1 = new MathDiv (node1, MathModExp ()); } )* { return node1; } } Node MathModExp () : { Node node1; } { node1 = MathBitShiftLeftExp () ( "%" { node1 = new MathMod (node1, MathBitShiftLeftExp ()); } )* { return node1; } } Node MathBitShiftLeftExp () : { Node node1; } { node1 = MathBitShiftRightExp () ( "<<" { node1 = new MathBitShiftLeft (node1, MathBitShiftRightExp ()); } )* { return node1; } } Node MathBitShiftRightExp () : { Node node1; } { node1 = MathBitLogShiftRightExp () ( ">>" { node1 = new MathBitShiftRight (node1, MathBitLogShiftRightExp ()); } )* { return node1; } } Node MathBitLogShiftRightExp () : { Node node1; } { node1 = MathBitOrExp () ( ">>>" { node1 = new MathBitLogShiftRight (node1, MathBitOrExp ()); } )* { return node1; } } Node MathBitOrExp () : { Node node1; } { node1 = MathBitXorExp () ( "|" { node1 = new MathBitOr (node1, MathBitXorExp ()); } )* { return node1; } } Node MathBitXorExp () : { Node node1; } { node1 = MathBitAndExp () ( "^" { node1 = new MathBitXor (node1, MathBitAndExp ()); } )* { return node1; } } Node MathBitAndExp () : { Node node1; } { node1 = ValueExp () ( "&" { node1 = new MathBitAnd (node1, ValueExp ()); } )* { return node1; } } Node ValueExp () : { Node node; } { LOOKAHEAD (2) // differentiate between Name and Function node = Function () {return node;} | node = Name () {return node;} | node = NumLiteral () {return node;} | node = StringLiteral () {return node;} | "-" node = ValueExp () {return new MathUnaryMinus (node);} | "+" node = ValueExp () {return node;} | "~" node = ValueExp () {return new MathBitInvert (node);} | "(" node = SubExp () ")" {return node;} } Node Function () : { Token t; Node node; String func; } { t = "(" { func = t.image; if (func.equals ("begins-with")) node = StrBeginsWith.create (StringCompareArgs ()); else if (func.equals ("ends-with")) node = StrEndsWith.create (StringCompareArgs ()); else if (func.equals ("contains")) node = StrContains.create (StringCompareArgs ()); else if (func.equals ("regex")) node = StrRegex.create (StringCompareArgs ()); else if (func.equals ("fold-case")) node = new StrFoldCase (StringValue ()); else if (func.equals ("equals")) node = Compare.createEquals (CompareArgs ()); else if (func.equals ("require")) node = new Require (Name ()); else if (func.equals ("int32")) node = new Type (Name (), Integer.class); else if (func.equals ("int64")) node = new Type (Name (), Long.class); else if (func.equals ("real64")) node = new Type (Name (), Double.class); else if (func.equals ("string")) node = new Type (Name (), String.class); else if (func.equals ("opaque")) node = new Type (Name (), byte [].class); else if (func.equals ("nan")) node = new Nan (Name ()); else if (func.equals ("wildcard")) node = StrWildcard.create (StringCompareArgs ()); else if (func.equals ("size")) node = new Size (Name ()); else if (func.equals ("decompose") || func.equals ("decompose-compat")) node = new StrUnicodeDecompose (StringValue (), func.equals ("decompose") ? StrUnicodeDecompose.Mode.DECOMPOSE : StrUnicodeDecompose.Mode.DECOMPOSE_COMPAT); else { node = null; AnyArgs (); // error recover by skipping args } } ")" { if (node == null) throw new ParseException ("Unknown function: " + func); else return node; } } Node StringValue () : { Node node; } { ( LOOKAHEAD (2) node = Function () | node = StringLiteral () | node = Name () ) { return node; } } // Args for the compare() function List CompareArgs () : { ArrayList args = new ArrayList (); } { { args.add (StringValue ()); } ( "," { args.add (MathExp ()); } )+ { return args; } } // Args for any string comparison function List StringCompareArgs () : { ArrayList args = new ArrayList (); } { {args.add (StringValue ()); } ("," {args.add (StringLiteral ());} )+ { return args; } } // Match any arguments. Used for error recovery void AnyArgs () : {} { ( ( StringValue () | NumLiteral () ) ( "," (StringValue () | NumLiteral ()) )* )? } Const NumLiteral () : { Token t; } { t= { Number value; char lastChar = t.image.charAt (t.image.length () - 1); if (lastChar == 'l' || lastChar == 'L') value = Long.decode (t.image.substring (0, t.image.length () - 1)); else value = Integer.decode (t.image); return new Const (value); } | t= { return new Const (new Double (t.image)); } } Field Name () : { Token t; } { t= { return new Field (stripBackslashes (t.image)); } } Const StringLiteral () : { Token t; } { t= { return new Const (stripBackslashes (t.image.substring (1, t.image.length () - 1))); } }avis-1.2.2/server/src/main/org/avis/subscription/ast/0000755000175000017500000000000011147555544022404 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/subscription/ast/IllegalChildException.java0000644000175000017500000000215511147555544027446 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; public class IllegalChildException extends IllegalArgumentException { public final Node parent; public final Node child; public IllegalChildException (Node parent, Node child) { this ("Illegal child: " + child.name (), parent, child); } public IllegalChildException (String message, Node parent, Node child) { super (message); this.parent = parent; this.child = child; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/MathIntParentNode.java0000644000175000017500000000366211147555544026602 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import static org.avis.util.Text.className; /** * Base class for math nodes that require integer arguments. * * @author Matthew Phillips */ public abstract class MathIntParentNode extends MathParentNode { public MathIntParentNode (Node child1, Node child2) { super (child1, child2); } @Override public Class evalType () { return Integer.class; } @Override protected String validateChild (Node child) { Class childType = child.evalType (); if (childType == Object.class) { // allow generic nodes such as fields return null; } else if (!(Integer.class.isAssignableFrom (childType) || Long.class.isAssignableFrom (childType))) { return "\"" + expr () + "\" needs an integer as an argument (was " + className (child.evalType ()).toLowerCase () + ")"; } else { return null; } } @Override protected boolean validOperand (Object value) { return value instanceof Integer || value instanceof Long; } @Override protected double evaluateReal64 (double number1, double number2) { throw new UnsupportedOperationException ("Not applicable for Real64 values"); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/StringCompareNode.java0000644000175000017500000000445411147555544026641 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.nodes.Const; /** * Base class for nodes that compare a string expression with a string * argument. * * @author Matthew Phillips */ public abstract class StringCompareNode extends Node { protected Node stringExpr; protected String string; public StringCompareNode (Node stringExpr, Const stringConst) { this (stringExpr, (String)stringConst.value ()); } public StringCompareNode (Node stringExpr, String string) { this.stringExpr = stringExpr; this.string = string; } @Override public Class evalType () { return Boolean.class; } @Override public String presentation () { return name (); } @Override public boolean hasChildren () { return true; } @Override public Collection children () { ArrayList children = new ArrayList (2); children.add (stringExpr); children.add (new Const (string)); return children; } @Override public Object evaluate (Map attrs) { Object value = stringExpr.evaluate (attrs); if (value == null) return BOTTOM; else return evaluate ((String)value, string); } protected abstract boolean evaluate (String string1, String string2); @Override public Node inlineConstants () { stringExpr = stringExpr.inlineConstants (); Object result = evaluate (EMPTY_NOTIFICATION); if (result != BOTTOM) return Const.bool ((Boolean)result); else return this; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/Nodes.java0000644000175000017500000001052411147555544024321 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.List; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.avis.subscription.ast.nodes.Or; import org.avis.util.IndentingWriter; /** * General utilities for node trees. */ public final class Nodes { private Nodes () { // zip } /** * Generate a pretty-printed version of a node tree suitable for * human consumption. Uses {@link Node#presentation()} to generate * text for each node. * * @see #unparse(Node) */ public static String toString (Node node) { IndentingWriter out = new IndentingWriter (new StringWriter ()); print (out, node); return out.toString (); } private static void print (IndentingWriter out, Node node) { out.print (node.presentation ()); if (node.hasChildren ()) { out.indent (); for (Node child : node.children ()) { out.println (); print (out, child); } out.unindent (); } } /** * "Unparse" an AST to a canonical S-expression-like string * expression useful for testing purposes. Uses the * {@link Node#expr()} method to unparse each node. * * e.g. field1 > 2 && field2 == 'hello there' becomes * (&& (> (field 'field1') (int32 2)) * (== (field 'field2') (string 'hello there'))) * * @see Node#expr() * @see #toString(Node) */ public static String unparse (Node node) { StringBuilder str = new StringBuilder (); unparse (str, node); return str.toString (); } private static void unparse (StringBuilder str, Node node) { if (node.hasChildren ()) { str.append ('('); str.append (node.expr ()); for (Node child : node.children ()) { str.append (' '); unparse (str, child); } str.append (')'); } else { str.append (node.expr ()); } } /** * Allow a node that usually operates on two arguments to optionally * operate on any number with an OR conjunction. For example, could * be used to turn begins-with (name, 'value1', 'value2') * into * begins-with (name, 'value1') || begins-with (name, 'value2'). * * @param nodeType The node type to generate. * @param param1 The constructor's first parameter type. * @param param2 The constructor's second parameter type. * @param args A list of arguments for the node (must be >= 2). If * these are longer than 2, then arg0 is paired with * arg1..argN as children to an OR node. * @return Either an instance of T or an Or with T's as children. */ public static Node createConjunction (Class nodeType, Class param1, Class param2, List args) { try { Constructor nodeConstructor = nodeType.getConstructor (param1, param2); if (args.size () == 2) { return nodeConstructor.newInstance (args.get (0), args.get (1)); } else { Node arg0 = args.get (0); Or or = new Or (); for (int i = 1; i < args.size (); i++) or.addChild (nodeConstructor.newInstance (arg0, args.get (i))); return or; } } catch (InvocationTargetException ex) { if (ex.getCause () instanceof RuntimeException) throw (RuntimeException)ex.getCause (); else throw new Error (ex); } catch (Exception ex) { throw new Error (ex); } } } avis-1.2.2/server/src/main/org/avis/subscription/ast/BoolParentNode.java0000644000175000017500000000323311147555544026123 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.Collection; /** * Base class for parent nodes that evaluate to boolean from boolean * children. * * @author Matthew Phillips */ public abstract class BoolParentNode extends ParentNode { public BoolParentNode () { // zip } public BoolParentNode (Node node1) { super (node1); } public BoolParentNode (Node node1, Node node2) { super (node1, node2); } public BoolParentNode (Node... children) { super (children); } public BoolParentNode (Collection children) { super (children); } @Override public String presentation () { return name (); } @Override public Class evalType () { return Boolean.class; } @Override public String validateChild (Node child) { if (child.evalType () != Boolean.class) return expr () + " requires boolean arguments (" + child.expr () + ")"; else return null; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/0000755000175000017500000000000011147555544023514 5ustar dpocockdpocockavis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathMinus.java0000644000175000017500000000253211147555544026266 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathParentNode; import org.avis.subscription.ast.Node; public class MathMinus extends MathParentNode { public MathMinus (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "-"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 - number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 - number2; } @Override protected double evaluateReal64 (double number1, double number2) { return number1 - number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrUnicodeDecompose.java0000644000175000017500000001023011147555544030271 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.avis.subscription.ast.Node; import static java.util.Collections.singleton; /** * @author Matthew Phillips */ public class StrUnicodeDecompose extends Node { /* * Java 5 does not have a public Unicode normalisation API. Rather * than ship a huge Unicode library, we use reflection to access the * non-public sun.text.Normalizer on 1.5, or the public * java.text.Normalizer API in 1.6 onwards. */ private static Method normalizeMethod; private static boolean java5; private static Object modeDecompose; private static Object modeDecomposeCompat; static { try { java5 = false; Class java6Normalizer = Class.forName ("java.text.Normalizer"); Class formClass = Class.forName ("java.text.Normalizer$Form"); normalizeMethod = java6Normalizer.getMethod ("normalize", CharSequence.class, formClass); modeDecompose = formClass.getEnumConstants () [0]; // NFD modeDecomposeCompat = formClass.getEnumConstants () [2]; // NFKD } catch (ClassNotFoundException ex) { // no Normalizer API, fall back on Java 5 workaround java5 = true; try { Class java5Normalizer = Class.forName ("sun.text.Normalizer"); Class modeClass = Class.forName ("sun.text.Normalizer$Mode"); normalizeMethod = java5Normalizer.getMethod ("normalize", String.class, modeClass, Integer.TYPE); modeDecompose = java5Normalizer.getField ("DECOMP").get (null); modeDecomposeCompat = java5Normalizer.getField ("DECOMP_COMPAT").get (null); } catch (Exception ex2) { throw new ExceptionInInitializerError (ex2); } } catch (Exception ex) { throw new ExceptionInInitializerError (ex); } } public static enum Mode { DECOMPOSE, DECOMPOSE_COMPAT; } public final Node stringExpr; public final Mode mode; private final Object normMode; public StrUnicodeDecompose (Node stringExpr, Mode mode) { this.stringExpr = stringExpr; this.mode = mode; this.normMode = mode == Mode.DECOMPOSE ? modeDecompose : modeDecomposeCompat; } @Override public Class evalType () { return String.class; } @Override public Object evaluate (Map attrs) { String result = (String)stringExpr.evaluate (attrs); if (result == null) return BOTTOM; try { if (java5) return normalizeMethod.invoke (null, result, normMode, 0); else return normalizeMethod.invoke (null, result, normMode); } catch (InvocationTargetException ex) { throw new Error (ex.getCause ()); } catch (Exception ex) { throw new Error (ex); } } @Override public String expr () { return mode == Mode.DECOMPOSE ? "decompose" : "decompose-compat"; } @Override public Node inlineConstants () { Object result = evaluate (EMPTY_NOTIFICATION); return result == null ? this : new Const (result); } @Override public String presentation () { return name (); } @Override public boolean hasChildren () { return true; } @Override public Collection children () { return singleton (stringExpr); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/And.java0000644000175000017500000000450311147555544025063 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.BoolParentNode; import org.avis.subscription.ast.Node; import static org.avis.subscription.ast.nodes.Const.CONST_FALSE; import static org.avis.subscription.ast.nodes.Const.CONST_TRUE; public class And extends BoolParentNode { public And (Node node1) { super (node1); } public And (Node node1, Node node2) { super (node1, node2); } public And (Node ...children) { super (children); } public And (Collection children) { super (children); } @Override public String expr () { return "&&"; } @Override public Node inlineConstants () { for (int i = children.size () - 1; i >= 0; i--) { Node child = children.get (i); Node newChild = child.inlineConstants (); Boolean result = (Boolean)newChild.evaluate (EMPTY_NOTIFICATION); if (result == FALSE) return CONST_FALSE; else if (result == TRUE) children.remove (i); else if (child != newChild) children.set (i, newChild); } if (children.isEmpty ()) return CONST_TRUE; else if (children.size () == 1) return children.get (0); else return this; } @Override public Object evaluate (Map attrs) { Boolean value = TRUE; for (int i = 0; i < children.size (); i++) { Object result = children.get (i).evaluate (attrs); if (result == FALSE) return result; else if (result == BOTTOM) value = BOTTOM; } return value; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Type.java0000644000175000017500000000351311147555544025302 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Map; import org.avis.subscription.ast.NameParentNode; /** * Test whether a field is a given type. Can be used for int32(), * int64(), string(), etc functions. * * @author Matthew Phillips */ public class Type extends NameParentNode { public Class type; public Type (Field field, Class type) { this (field.fieldName (), type); } public Type (String field, Class type) { super (field); this.type = type; } @Override public Class evalType () { return Boolean.class; } @Override public Object evaluate (Map attrs) { Object value = attrs.get (name); if (value == null) return BOTTOM; else return type == value.getClass (); } @Override public String expr () { if (type == Integer.class) return "int32"; else if (type == Long.class) return "int64"; else if (type == Double.class) return "real64"; else if (type == String.class) return "string"; else if (type == byte [].class) return "opaque"; else return type.getClass ().getName (); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitShiftLeft.java0000644000175000017500000000240211147555544027516 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathIntParentNode; import org.avis.subscription.ast.Node; public class MathBitShiftLeft extends MathIntParentNode { public MathBitShiftLeft (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "<<"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 << number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 << number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitAnd.java0000644000175000017500000000235511147555544026337 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathIntParentNode; import org.avis.subscription.ast.Node; public class MathBitAnd extends MathIntParentNode { public MathBitAnd (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "&"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 & number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 & number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrBeginsWith.java0000644000175000017500000000266211147555544027121 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.List; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.StringCompareNode; import static org.avis.subscription.ast.Nodes.createConjunction; public class StrBeginsWith extends StringCompareNode { /** * Create from a list of arguments. */ public static Node create (List args) { return createConjunction (StrBeginsWith.class, Node.class, Const.class, args); } public StrBeginsWith (Node stringExpr, Const stringConst) { super (stringExpr, stringConst); } @Override public String expr () { return "begins-with"; } @Override protected boolean evaluate (String string1, String string2) { return string1.startsWith (string2); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathUnaryMinus.java0000644000175000017500000000444111147555544027306 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.IllegalChildException; import org.avis.subscription.ast.Node; import static java.util.Collections.singleton; public class MathUnaryMinus extends Node { private Node child; public MathUnaryMinus (Node child) throws IllegalChildException { Class childType = child.evalType (); if (childType != Object.class && !Number.class.isAssignableFrom (childType)) { throw new IllegalChildException ("Unary minus requires a numeric argument", this, child); } this.child = child; } @Override public String expr () { return "-"; } @Override public String presentation () { return name (); } @Override public Class evalType () { return child.evalType (); } @Override public boolean hasChildren () { return true; } @Override public Collection children () { return singleton (child); } @Override public Node inlineConstants () { child = child.inlineConstants (); Object result = evaluate (EMPTY_NOTIFICATION); if (result != null) return new Const (result); else return this; } @Override public Object evaluate (Map attrs) { Object result = child.evaluate (attrs); if (result instanceof Integer) return -((Integer)result); else if (result instanceof Long) return -((Long)result); else if (result instanceof Double) return -((Double)result); else return BOTTOM; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Compare.java0000644000175000017500000001255611147555544025756 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.List; import java.util.Map; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.ParentBiNode; import static org.avis.util.Text.className; import static org.avis.util.Numbers.highestPrecision; import static org.avis.util.Numbers.upconvert; /** * Comparison operator that can implement equals, greater than, less * than and any combination of those. *

       *              inequality      equality
       *  -------------------------------------
       *   ==          0              true
       *   <          -1              false
       *   <=         -1              true
       *   >           1              false
       *   >=          1              true
       * 
      * * @author Matthew Phillips */ public class Compare extends ParentBiNode { public int inequality; public boolean equality; /** * Create an "==" compare node from a list of comparable children * (size >= 2). If more than two children, generates an OR wrapper. */ public static Node createEquals (List args) { if (args.size () < 2) { throw new IllegalArgumentException ("Two or more children required"); } else if (args.size () == 2) { return new Compare (args.get (0), args.get (1), 0, true); } else { Node arg0 = args.get (0); Or or = new Or (); for (int i = 1; i < args.size (); i++) or.addChild (new Compare (arg0, args.get (i), 0, true)); return or; } } /** * Create a new instance. e.g. Compare (n1, n2, 1, true) => n1 >= * n2. Compare (n1, n2, -1, false) => n1 < n2. Compare (n1, n2, * 0, true) => n1 == n2. * * @param child1 Left operand. * @param child2 Right operand. * @param inequality > 0 => true if left > right, < 0 => true if left * < right. * @param equality True => true if equal. */ public Compare (Node child1, Node child2, int inequality, boolean equality) { this.inequality = inequality; this.equality = equality; init (child1, child2); } @Override protected String validateChild (Node child) { Class childType = child.evalType (); if (childType == Object.class) { // allow generic nodes such as fields return null; } else if (childType != Number.class && (childType == Boolean.class || !Comparable.class.isAssignableFrom (childType))) { return expr () + " cannot have expression of type " + className (childType) + " as an argument"; } else if (child1 != null) { Class evalType = child1.evalType (); if (evalType != Object.class && evalType != childType) { if (!(Number.class.isAssignableFrom (evalType) && Number.class.isAssignableFrom (childType))) { return expr () + ": argument (" + child.expr () + ") cannot be compared to " + "(" + child1.expr () + ")"; } } } return null; } @Override public Class evalType () { return Boolean.class; } @Override public String expr () { StringBuilder str = new StringBuilder (); if (inequality < 0) str.append ('<'); else if (inequality > 0) str.append ('>'); if (equality) { if (str.length () == 0) str.append ("=="); else str.append ('='); } return str.toString (); } @Override public String presentation () { return "Compare: " + expr (); } @Override @SuppressWarnings("unchecked") public Object evaluate (Map attrs) { Object result1 = child1.evaluate (attrs); if (!(result1 instanceof Comparable)) return BOTTOM; Object result2 = child2.evaluate (attrs); if (!(result2 instanceof Comparable)) return BOTTOM; Class class1 = result1.getClass (); Class class2 = result2.getClass (); // check for compatible types if (class1 != class2) { // if numeric, can upconvert if (class1.getSuperclass () == Number.class && class2.getSuperclass () == Number.class) { Class newType = highestPrecision (class1, class2); if (class1 != newType) result1 = upconvert ((Number)result1, class2); else result2 = upconvert ((Number)result2, class1); } else { // incompatible types return BOTTOM; } } int compare = ((Comparable)result1).compareTo (result2); if (compare == 0) return equality; else if (compare < 0) return inequality < 0; else return inequality > 0; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Not.java0000644000175000017500000000344211147555544025122 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.Node; import static java.util.Collections.singleton; public class Not extends Node { private Node child; public Not (Node child) { this.child = child; } @Override public String presentation () { return "Not"; } @Override public String expr () { return "!"; } @Override public Node inlineConstants () { child = child.inlineConstants (); Boolean result = (Boolean)evaluate (EMPTY_NOTIFICATION); if (result != BOTTOM) return Const.bool (result); else return this; } @Override public Object evaluate (Map attrs) { Boolean result = (Boolean)child.evaluate (attrs); if (result == BOTTOM) return BOTTOM; else return !result; } @Override public boolean hasChildren () { return true; } @Override public Collection children () { return singleton (child); } @Override public Class evalType () { return Boolean.class; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitXor.java0000644000175000017500000000235511147555544026405 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathIntParentNode; import org.avis.subscription.ast.Node; public class MathBitXor extends MathIntParentNode { public MathBitXor (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "^"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 ^ number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 ^ number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitLogShiftRight.java0000644000175000017500000000242111147555544030344 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathIntParentNode; import org.avis.subscription.ast.Node; public class MathBitLogShiftRight extends MathIntParentNode { public MathBitLogShiftRight (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return ">>>"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 >>> number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 >>> number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitInvert.java0000644000175000017500000000437411147555544027107 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.avis.subscription.ast.IllegalChildException; import org.avis.subscription.ast.Node; public class MathBitInvert extends Node { private Node child; public MathBitInvert (Node child) throws IllegalChildException { Class childType = child.evalType (); if (childType != Object.class && !(childType == Integer.class || child.evalType () == Long.class)) { throw new IllegalChildException ("~ requires an integer argument", this, child); } this.child = child; } @Override public String expr () { return "~"; } @Override public String presentation () { return name (); } @Override public Class evalType () { return child.evalType (); } @Override public boolean hasChildren () { return true; } @Override public Collection children () { return Collections.singleton (child); } @Override public Node inlineConstants () { child = child.inlineConstants (); Object result = evaluate (EMPTY_NOTIFICATION); if (result != null) return new Const (result); else return this; } @Override public Object evaluate (Map attrs) { Object result = child.evaluate (attrs); if (result instanceof Integer) return (Integer)result ^ 0xFFFFFFFF; else if (result instanceof Long) return (Long)result ^ 0xFFFFFFFFFFFFFFFFL; else return BOTTOM; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Nan.java0000644000175000017500000000246311147555544025100 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Map; import org.avis.subscription.ast.NameParentNode; public class Nan extends NameParentNode { public Nan (Field field) { this (field.fieldName ()); } public Nan (String field) { super (field); } @Override public Class evalType () { return Boolean.class; } @Override public String expr () { return "nan"; } @Override public Object evaluate (Map attrs) { Object value = attrs.get (name); if (!(value instanceof Double)) return BOTTOM; else return ((Double)value).isNaN (); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Field.java0000644000175000017500000000301211147555544025376 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.NameParentNode; import org.avis.subscription.ast.Node; import static java.util.Collections.singleton; public class Field extends NameParentNode { public Field (String name) { super (name); } public String fieldName () { return name; } @Override public String expr () { return "field"; } @Override public Class evalType () { return Object.class; } @Override public Object evaluate (Map attrs) { return attrs.get (name); } /** * Children of name-based nodes are by default Field's, but this * would be recursive for a Field. */ @Override public Collection children () { return singleton ((Node)new Const (name)); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Const.java0000644000175000017500000000650311147555544025451 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Map; import org.avis.subscription.ast.Node; import static org.avis.util.Util.valuesEqual; public class Const extends Node { public static final Const CONST_FALSE = new Const (FALSE); public static final Const CONST_TRUE = new Const (TRUE); public static final Const CONST_BOTTOM = new Const (BOTTOM); public static final Const CONST_ZERO = new Const (0); private Object value; public static Const bool (Boolean value) { if (value == TRUE) return CONST_TRUE; else if (value == FALSE) return CONST_FALSE; else throw new IllegalArgumentException ("Invalid value: " + value); } public static Const string (String string) { return new Const (string); } public static Const int32 (int value) { if (value == 0) return CONST_ZERO; else return new Const (value); } public static Node int64 (long value) { return new Const (value); } public static Node real64 (double value) { return new Const (value); } public Const (Object value) { this.value = value; } @Override public boolean equals (Object obj) { return obj.getClass () == Const.class && valuesEqual (((Const)obj).value, value); } @Override public int hashCode () { return value == null ? 0 : value.hashCode (); } public Object value () { return value; } @Override public Class evalType () { if (value == BOTTOM) return Boolean.class; else return value.getClass (); } @Override public Node inlineConstants () { return this; } @Override public Object evaluate (Map attrs) { return value; } @Override public String expr () { if (value instanceof String) return "'" + value + "'"; else if (value instanceof Number) return numExpr (); else return value.toString (); } @Override public String presentation () { if (value instanceof String) return "Str: \"" + value + '\"'; else if (value instanceof Number) return numPresentation (); else return "Const: " + value; } private String numPresentation () { StringBuilder str = new StringBuilder ("Num: "); str.append (value); if (value instanceof Long) str.append ('L'); else if (value instanceof Double) str.append (" (double)"); return str.toString (); } private String numExpr () { StringBuilder str = new StringBuilder (); str.append (value); if (value instanceof Long) str.append ('L'); return str.toString (); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitOr.java0000644000175000017500000000235211147555544026212 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathIntParentNode; import org.avis.subscription.ast.Node; public class MathBitOr extends MathIntParentNode { public MathBitOr (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "|"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 | number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 | number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrWildcard.java0000644000175000017500000000336411147555544026607 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.List; import java.util.regex.Pattern; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.StringCompareNode; import static org.avis.subscription.ast.Nodes.createConjunction; import static org.avis.util.Wildcard.toPattern; /** * NOTE: no real spec for wildcard exists. This implements ? and * * only, which is what is supported by Mantara elvind 4.4.0. * * @author Matthew Phillips */ public class StrWildcard extends StringCompareNode { private Pattern wildcard; /** * Create from a list of arguments. */ public static Node create (List args) { return createConjunction (StrWildcard.class, Node.class, Const.class, args); } public StrWildcard (Node stringExpr, Const stringConst) { super (stringExpr, stringConst); this.wildcard = toPattern (string); } @Override public String expr () { return "wildcard"; } @Override protected boolean evaluate (String string1, String string2) { return wildcard.matcher (string1).matches (); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Require.java0000644000175000017500000000234311147555544025775 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Map; import org.avis.subscription.ast.NameParentNode; public class Require extends NameParentNode { public Require (Field field) { this (field.fieldName ()); } public Require (String name) { super (name); } @Override public String expr () { return "require"; } @Override public Class evalType () { return Boolean.class; } @Override public Object evaluate (Map attrs) { return attrs.containsKey (name) ? TRUE : BOTTOM; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Or.java0000644000175000017500000000455411147555544024747 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.BoolParentNode; import org.avis.subscription.ast.Node; import static org.avis.subscription.ast.nodes.Const.CONST_FALSE; import static org.avis.subscription.ast.nodes.Const.CONST_TRUE; public class Or extends BoolParentNode { public Or () { super (); } public Or (Node node1) { super (node1); } public Or (Node node1, Node node2) { super (node1, node2); } public Or (Collection children) { super (children); } public Or (Node... children) { super (children); } @Override public String expr () { return "||"; } @Override public Node inlineConstants () { for (int i = children.size () - 1; i >= 0; i--) { Node child = children.get (i); Node newChild = child.inlineConstants (); Boolean result = (Boolean)newChild.evaluate (EMPTY_NOTIFICATION); if (result == TRUE) return CONST_TRUE; else if (result == FALSE) children.remove (i); else if (child != newChild) children.set (i, newChild); } if (children.isEmpty ()) return CONST_FALSE; else if (children.size () == 1) return children.get (0); else return this; } @Override public Object evaluate (Map attrs) { Boolean value = FALSE; for (int i = 0; i < children.size (); i++) { Boolean result = (Boolean)children.get (i).evaluate (attrs); if (result == TRUE) return TRUE; else if (result == BOTTOM) value = BOTTOM; } return value; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Xor.java0000644000175000017500000000402611147555544025131 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.BoolParentNode; import org.avis.subscription.ast.Node; public class Xor extends BoolParentNode { public Xor (Node node1) { super (node1); } public Xor (Node node1, Node node2) { super (node1, node2); } public Xor (Collection children) { super (children); } public Xor (Node... children) { super (children); } @Override public String expr () { return "^^"; } @Override public Node inlineConstants () { for (int i = children.size () - 1; i >= 0; i--) { Node child = children.get (i); Node newChild = child.inlineConstants (); if (child != newChild) children.set (i, newChild); } Boolean result = (Boolean)evaluate (EMPTY_NOTIFICATION); if (result != BOTTOM) return Const.bool (result); else return this; } @Override public Object evaluate (Map attrs) { Boolean value = FALSE; for (int i = 0; i < children.size (); i++) { Object result = children.get (i).evaluate (attrs); if (result == BOTTOM) return BOTTOM; else if (result == TRUE) value = value == TRUE ? FALSE : TRUE; } return value; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrFoldCase.java0000644000175000017500000000335211147555544026533 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.Node; import static java.util.Collections.singleton; public class StrFoldCase extends Node { private Node stringExpr; public StrFoldCase (Node stringExpr) { this.stringExpr = stringExpr; } @Override public Class evalType () { return String.class; } @Override public Object evaluate (Map attrs) { String result = (String)stringExpr.evaluate (attrs); return result == null ? null : result.toLowerCase (); } @Override public String expr () { return "fold-case"; } @Override public String presentation () { return name (); } @Override public Node inlineConstants () { Object result = evaluate (EMPTY_NOTIFICATION); return result == null ? this : new Const (result); } @Override public boolean hasChildren () { return true; } @Override public Collection children () { return singleton (stringExpr); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/Size.java0000644000175000017500000000260011147555544025267 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.Map; import org.avis.subscription.ast.NameParentNode; public class Size extends NameParentNode { public Size (Field field) { this (field.fieldName ()); } public Size (String name) { super (name); } @Override public String expr () { return "size"; } @Override public Class evalType () { return Integer.class; } @Override public Object evaluate (Map attrs) { Object value = attrs.get (name); if (value instanceof byte []) return ((byte [])value).length; else if (value instanceof String) return ((String)value).length (); else return BOTTOM; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathMod.java0000644000175000017500000000252411147555544025713 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathParentNode; import org.avis.subscription.ast.Node; public class MathMod extends MathParentNode { public MathMod (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "%"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 % number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 % number2; } @Override protected double evaluateReal64 (double number1, double number2) { return number1 % number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathDiv.java0000644000175000017500000000252411147555544025716 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathParentNode; import org.avis.subscription.ast.Node; public class MathDiv extends MathParentNode { public MathDiv (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "/"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 / number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 / number2; } @Override protected double evaluateReal64 (double number1, double number2) { return number1 / number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathBitShiftRight.java0000644000175000017500000000240511147555544027704 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathIntParentNode; import org.avis.subscription.ast.Node; public class MathBitShiftRight extends MathIntParentNode { public MathBitShiftRight (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return ">>"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 >> number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 >> number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathMult.java0000644000175000017500000000252711147555544026120 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathParentNode; import org.avis.subscription.ast.Node; public class MathMult extends MathParentNode { public MathMult (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "*"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 * number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 * number2; } @Override protected double evaluateReal64 (double number1, double number2) { return number1 * number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrContains.java0000644000175000017500000000264711147555544026637 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.List; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.StringCompareNode; import static org.avis.subscription.ast.Nodes.createConjunction; public class StrContains extends StringCompareNode { /** * Create from a list of arguments. */ public static Node create (List args) { return createConjunction (StrContains.class, Node.class, Const.class, args); } public StrContains (Node stringExpr, Const stringConst) { super (stringExpr, stringConst); } @Override public String expr () { return "contains"; } @Override protected boolean evaluate (String string1, String string2) { return string1.contains (string2); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrEndsWith.java0000644000175000017500000000265011147555544026600 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.List; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.StringCompareNode; import static org.avis.subscription.ast.Nodes.createConjunction; public class StrEndsWith extends StringCompareNode { /** * Create from a list of arguments. */ public static Node create (List args) { return createConjunction (StrEndsWith.class, Node.class, Const.class, args); } public StrEndsWith (Node stringExpr, Const stringConst) { super (stringExpr, stringConst); } @Override public String expr () { return "ends-with"; } @Override protected boolean evaluate (String string1, String string2) { return string1.endsWith (string2); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/MathPlus.java0000644000175000017500000000252711147555544026122 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import org.avis.subscription.ast.MathParentNode; import org.avis.subscription.ast.Node; public class MathPlus extends MathParentNode { public MathPlus (Node child1, Node child2) { super (child1, child2); } @Override public String expr () { return "+"; } @Override protected int evaluateInt32 (int number1, int number2) { return number1 + number2; } @Override protected long evaluateInt64 (long number1, long number2) { return number1 + number2; } @Override protected double evaluateReal64 (double number1, double number2) { return number1 + number2; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/nodes/StrRegex.java0000644000175000017500000000327111147555544026125 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast.nodes; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.avis.subscription.ast.Node; import org.avis.subscription.ast.StringCompareNode; import static java.util.regex.Pattern.DOTALL; import static java.util.regex.Pattern.compile; import static org.avis.subscription.ast.Nodes.createConjunction; public class StrRegex extends StringCompareNode { private Pattern regex; /** * Create from a list of arguments. */ public static Node create (List args) { return createConjunction (StrRegex.class, Node.class, Const.class, args); } public StrRegex (Node stringExpr, Const stringConst) throws PatternSyntaxException { super (stringExpr, stringConst); this.regex = compile (string, DOTALL); } @Override public String expr () { return "regex"; } @Override protected boolean evaluate (String string1, String string2) { return regex.matcher (string1).find (); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/MathParentNode.java0000644000175000017500000000673211147555544026130 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.Map; import static org.avis.util.Text.className; import static org.avis.util.Numbers.highestPrecision; import static org.avis.util.Numbers.upconvert; /** * Base class for mathematical operator nodes. Subclasses implement * the evaluateXXX () methods to add math operation implementations. * * @author Matthew Phillips */ public abstract class MathParentNode extends ParentBiNode { public MathParentNode (Node child1, Node child2) { super (child1, child2); } @Override protected String validateChild (Node child) { Class childType = child.evalType (); if (childType == Object.class) { // allow generic nodes such as fields return null; } else if (!Number.class.isAssignableFrom (childType)) { return "\"" + expr () + "\" needs a number as an argument (was " + className (child.evalType ()).toLowerCase () + ")"; } else { return null; } } @Override public Class evalType () { return Number.class; } @Override public Object evaluate (Map attrs) { Number number1 = evaluate (child1, attrs); if (number1 == null) return null; Number number2 = evaluate (child2, attrs); if (number2 == null) return null; Class type1 = number1.getClass (); Class type2 = number2.getClass (); // check if upconvert needed if (type1 != type2) { Class newType = highestPrecision (type1, type2); if (type1 != newType) number1 = upconvert (number1, type2); else number2 = upconvert (number2, type1); } try { if (number1 instanceof Integer) return evaluateInt32 ((Integer)number1, (Integer)number2); else if (number1 instanceof Long) return evaluateInt64 ((Long)number1, (Long)number2); else return evaluateReal64 ((Double)number1, (Double)number2); } catch (ArithmeticException ex) { // e.g. div by zero. treat this as a bottom'ing condition return null; } } private Number evaluate (Node child, Map attrs) { Object result = child.evaluate (attrs); if (result == null || !validOperand (result)) return null; else return (Number)result; } /** * Test whether value is a valid operand for this operation. */ protected boolean validOperand (Object value) { return value instanceof Number; } protected abstract int evaluateInt32 (int number1, int number2); protected abstract long evaluateInt64 (long number1, long number2); protected abstract double evaluateReal64 (double number1, double number2); } avis-1.2.2/server/src/main/org/avis/subscription/ast/ParentNode.java0000644000175000017500000000456411147555544025317 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.ArrayList; import java.util.Collection; /** * Base class for nodes with an arbitrary number of children. * * @author Matthew Phillips */ public abstract class ParentNode extends Node { protected ArrayList children; public ParentNode () { this.children = new ArrayList (2); } public ParentNode (Node node1) { this (); addChild (node1); } public ParentNode (Node node1, Node node2) throws IllegalChildException { this (); addChild (node1); addChild (node2); } /** * Add any number of children */ public ParentNode (Node ...children) { this.children = new ArrayList (children.length); for (Node child : children) addChild (child); } /** * Add any number of children */ public ParentNode (Collection children) { this.children = new ArrayList (children.size ()); for (Node child : children) addChild (child); } /** * Called by {@link #addChild(Node)} to validate the child is valid * as a child. * * @param child * * @return An error message if not valid, null if child is OK to be * added. */ protected abstract String validateChild (Node child); public void addChild (Node child) throws IllegalChildException { String error = validateChild (child); if (error == null) children.add (child); else throw new IllegalChildException (error, this, child); } @Override public boolean hasChildren () { return !children.isEmpty (); } @Override public Collection children () { return children; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/ParentBiNode.java0000644000175000017500000000432611147555544025566 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.ArrayList; import java.util.Collection; import org.avis.subscription.ast.nodes.Const; /** * Utility base class for nodes that take only two children. * * @author Matthew Phillips */ public abstract class ParentBiNode extends Node { protected Node child1; protected Node child2; public ParentBiNode () { // zip } public ParentBiNode (Node child1, Node child2) { init (child1, child2); } protected void init (Node newChild1, Node newChild2) { checkChild (newChild1); this.child1 = newChild1; checkChild (newChild2); this.child2 = newChild2; } private void checkChild (Node child) { String error = validateChild (child); if (error != null) throw new IllegalChildException (error, this, child); } protected abstract String validateChild (Node child); @Override public String presentation () { return name (); } @Override public boolean hasChildren () { return true; } @Override public Collection children () { ArrayList children = new ArrayList (2); children.add (child1); children.add (child2); return children; } @Override public Node inlineConstants () { child1 = child1.inlineConstants (); child2 = child2.inlineConstants (); Object result = evaluate (EMPTY_NOTIFICATION); if (result != null) return new Const (result); else return this; } } avis-1.2.2/server/src/main/org/avis/subscription/ast/NameParentNode.java0000644000175000017500000000310711147555544026110 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.Collection; import org.avis.subscription.ast.nodes.Field; import static java.util.Collections.singleton; /** * Base class for nodes that have a field name as their primary child * parameter and whose result is derived from that. * * @author Matthew Phillips */ public abstract class NameParentNode extends Node { public String name; public NameParentNode (Field field) { this (field.fieldName ()); } public NameParentNode (String name) { this.name = name; } @Override public String presentation () { return name (); } @Override public Node inlineConstants () { // name-based nodes will not be inline-able return this; } @Override public boolean hasChildren () { return true; } @Override public Collection children () { return singleton ((Node)new Field (name)); } } avis-1.2.2/server/src/main/org/avis/subscription/ast/Node.java0000644000175000017500000000614211147555544024137 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.avis.subscription.ast; import java.util.Collection; import java.util.Map; import org.avis.subscription.ast.nodes.Const; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static org.avis.util.Text.className; /** * The base class for all nodes in a subscription expression abstract * syntax tree. * * @author Matthew Phillips */ public abstract class Node { public static final Boolean TRUE = Boolean.TRUE; public static final Boolean FALSE = Boolean.FALSE; public static final Boolean BOTTOM = null; protected static final Map EMPTY_NOTIFICATION = emptyMap (); /** * Evaluate the expression tree rooted at this node and return the * result. * * @param attrs The notification to match against. */ public abstract Object evaluate (Map attrs); /** * The type of result that is guaranteed to be generated by {@link * #evaluate(Map)}. */ public abstract Class evalType (); /** * Recursively inline any constant child subexpressions of this node * (i.e. replace any child expressions that always evaluate to a * constant value with that value). If, as a result of inlining, * this node can itself be replaced by a new node (e.g. a * {@link Const} instance) the reference to the replacement node can * be returned instead "this". * * @return Either a reference to the node or a new node that * replaces this one. */ public abstract Node inlineConstants (); /** * A debug-friendly string version of the node. */ public abstract String presentation (); /** * A string version of the node suitable for use in generating a * formal tree expression. * * @see Nodes#unparse(Node) */ public abstract String expr (); /** * Returns {@link #name()}. Subclasses may override. */ @Override public String toString () { return name (); } /** * The human-readable name of the node. Uses the class name as default. */ public String name () { return className (getClass ()); } /** * True if {@link #children()} would return a non-empty child node * collection. */ public boolean hasChildren () { return false; } /** * Get the children of the node (if any). The collection may not be * modified. * * @see #hasChildren() */ public Collection children () { return emptySet (); } } avis-1.2.2/server/src/avis_java_header.txt0000644000175000017500000000125510750123560020417 0ustar dpocockdpocock/* * Avis event router. * * Copyright (C) 2008 Matthew Phillips * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ avis-1.2.2/server/avis-server.properties0000644000175000017500000000036311147553646020214 0ustar dpocockdpocock# The Avis package release: major.minor.release identifies a release package avis.version.major=1 avis.version.minor=2 avis.version.patch=2 avis.release= # The advertised router version avis.router.version=1.2.2 avis.build-date=@build-date@ avis-1.2.2/server/.classpath0000644000175000017500000000102210750123560015567 0ustar dpocockdpocock avis-1.2.2/server/build.xml0000644000175000017500000004422611147460226015446 0ustar dpocockdpocock Avis event router server avis-1.2.2/server/.project0000644000175000017500000000056210750123560015263 0ustar dpocockdpocock avis.server org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature avis-1.2.2/server/packaging/0000755000175000017500000000000010750123550015534 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/0000755000175000017500000000000010750123554017232 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/license.rtf0000644000175000017500000001174410750123554021400 0ustar dpocockdpocock{\rtf1\ansi\ansicpg1252\uc1 \deff0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} {\f5\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Helvetica;}{\f193\froman\fcharset238\fprq2 Times New Roman CE;}{\f194\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f196\froman\fcharset161\fprq2 Times New Roman Greek;} {\f197\froman\fcharset162\fprq2 Times New Roman Tur;}{\f198\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f199\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f200\froman\fcharset186\fprq2 Times New Roman Baltic;} {\f201\fswiss\fcharset238\fprq2 Arial CE;}{\f202\fswiss\fcharset204\fprq2 Arial Cyr;}{\f204\fswiss\fcharset161\fprq2 Arial Greek;}{\f205\fswiss\fcharset162\fprq2 Arial Tur;}{\f206\fswiss\fcharset177\fprq2 Arial (Hebrew);} {\f207\fswiss\fcharset178\fprq2 Arial (Arabic);}{\f208\fswiss\fcharset186\fprq2 Arial Baltic;}{\f233\fswiss\fcharset238\fprq2 Helvetica CE;}{\f234\fswiss\fcharset204\fprq2 Helvetica Cyr;}{\f236\fswiss\fcharset161\fprq2 Helvetica Greek;} {\f237\fswiss\fcharset162\fprq2 Helvetica Tur;}{\f238\fswiss\fcharset177\fprq2 Helvetica (Hebrew);}{\f239\fswiss\fcharset178\fprq2 Helvetica (Arabic);}{\f240\fswiss\fcharset186\fprq2 Helvetica Baltic;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255; \red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0; \red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang3081\langfe1033\cgrid\langnp3081\langfenp1033 \snext0 Normal;}{\*\cs10 \additive Default Paragraph Font;}{\*\cs15 \additive \ul\cf2 \sbasedon10 Hyperlink;}}{\info{\title Avis is Copyright (C) 2007 Matthew Phillips . \par \par Permission to distribute unmodified copies of this Avis package is granted. \par \par The source code to Avis is available under the terms of the GNU General Public License (GPL) \endash see }{\field{\*\fldinst {\f1\fs20\lang1033\langfe1033\langnp1033 HYPERLINK "http://www.gnu.org/copyleft/gpl.html" }{ \f1\fs20\lang1033\langfe1033\langnp1033 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000170000002500000068007400740070003a002f002f007700770077002e0067006e0075002e006f00720067002f0063006f00700079006c006500660074002f00670070006c002e00680074006d006c000000e0c9ea79f9bace118c8200aa004ba90b4a00000068007400 740070003a002f002f007700770077002e0067006e0075002e006f00720067002f0063006f00700079006c006500660074002f00670070006c002e00680074006d006c000000}}}{\fldrslt {\cs15\f1\fs20\ul\cf2\lang1033\langfe1033\langnp1033 http://www.gnu.org/copyleft/gpl.html}}}{ \f1\fs20\lang1033\langfe1033\langnp1033 for the full text of the license. \par \par Please see }{\field{\*\fldinst {\f1\fs20\lang1033\langfe1033\langnp1033 HYPERLINK "http://avis.sourceforge.net" }{\f1\fs20\lang1033\langfe1033\langnp1033 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b02000000170000001c00000068007400740070003a002f002f0061007600690073002e0073006f00750072006300650066006f007200670065002e006e00650074000000e0c9ea79f9bace118c8200aa004ba90b3a00000068007400740070003a002f002f006100760069007300 2e0073006f00750072006300650066006f007200670065002e006e00650074002f000000}}}{\fldrslt {\cs15\f1\fs20\ul\cf2\lang1033\langfe1033\langnp1033 http://avis.sourceforge.net}}}{\f1\fs20\lang1033\langfe1033\langnp1033 for more information. \par }}avis-1.2.2/server/packaging/windows/installer/0000755000175000017500000000000011147554176021240 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/installer/avis.iss.in0000644000175000017500000000635110750123554023323 0ustar dpocockdpocock; Avis Inno Setup installer script [Setup] AppName=Avis AppVerName=Avis @release-name@ AppPublisher=Matthew Phillips AppPublisherURL=http://avis.sourceforge.net AppVersion=@version@ DefaultDirName={pf}\Avis DefaultGroupName=Avis SourceDir=.. OutputDir=..\..\build LicenseFile=license.rtf [Files] ; Make a backup of wrapper config Source: "{app}\config\wrapper.conf"; DestDir: "{app}\config\wrapper.conf.orig"; Flags: external skipifsourcedoesntexist ; Router Source: "..\..\lib\avis-router.jar"; DestDir: "{app}\lib" Source: "..\..\etc\avisd.config"; DestDir: "{app}\config"; Flags: onlyifdoesntexist Source: "..\..\doc\LICENSE.txt"; DestDir: "{app}" ; Client utilities Source: "..\..\..\client\lib\avis-client.jar"; DestDir: "{app}\lib" Source: "..\..\..\client\lib\avis-tools.jar"; DestDir: "{app}\lib" Source: "..\..\..\client\bin\ec.cmd"; DestDir: "{app}\bin" Source: "..\..\..\client\bin\ep.cmd"; DestDir: "{app}\bin" ; Service wrapper Source: "service\lib\wrapper.jar"; DestDir: "{app}\lib" Source: "service\lib\wrapper.dll"; DestDir: "{app}\lib" Source: "service\bin\wrapper.exe"; DestDir: "{app}\bin" Source: "service\bin\avis.cmd"; DestDir: "{app}\bin" Source: "service\bin\install_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\bin\uninstall_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\bin\start_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\bin\stop_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\config\wrapper.conf"; DestDir: "{app}\config"; Flags: onlyifdoesntexist Source: "service\logs\empty.log"; DestDir: "{app}\logs"; DestName: "avis.log"; Flags: onlyifdoesntexist ; JRE Files ; Source: "@jre_installer@"; DestDir: "{app}"; DestName: "jre.exe"; Flags: deleteafterinstall [Tasks] Name: installservice; Description: "Install as a system service" Name: programicon; Description: "Create icons in the Programs menu" [Icons] Name: "{group}\Run Avis In Console"; Filename: "{app}\bin\avis.cmd"; WorkingDir: "{app}\bin"; Tasks: programicon Name: "{group}\Start Avis Service"; Filename: "{app}\bin\start_avis_service.cmd"; WorkingDir: "{app}\bin"; Tasks: programicon Name: "{group}\Stop Avis Service"; Filename: "{app}\bin\stop_avis_service.cmd"; WorkingDir: "{app}\bin"; Tasks: programicon Name: "{group}\Avis Config File"; Filename: "{app}\config\avisd.config"; WorkingDir: "{app}\config"; Tasks: programicon [Run] Filename: "{app}\bin\uninstall_avis_service.cmd"; Description: "Install as a system service"; StatusMsg: "Uninstalling existing Avis service..."; Flags: runhidden; Tasks: installservice Filename: "{app}\bin\install_avis_service.cmd"; Description: "Install as a system service"; StatusMsg: "Installing Avis as a service..."; Flags: runhidden; Tasks: installservice Filename: "{app}\bin\start_avis_service.cmd"; Description: "Start the Avis service"; StatusMsg: "Starting the Avis service..."; Flags: runhidden; Tasks: installservice ; Filename: "{app}\jre.exe"; Parameters: "/s /v""/qn ADDLOCAL=jrecore IEXPLORER=1 MOZILLA=1"" "; Description: "Install Java runtime"; StatusMsg: "Installing Java..."; Check: JavaCheck [UninstallRun] Filename: "{app}\bin\uninstall_avis_service.cmd"; Flags: runhidden [UninstallDelete] Type: filesandordirs; Name: "{app}\logs" avis-1.2.2/server/packaging/windows/installer/avis.iss0000644000175000017500000000633411147554176022730 0ustar dpocockdpocock; Avis Inno Setup installer script [Setup] AppName=Avis AppVerName=Avis 1.2.2 AppPublisher=Matthew Phillips AppPublisherURL=http://avis.sourceforge.net AppVersion=1.2.2 DefaultDirName={pf}\Avis DefaultGroupName=Avis SourceDir=.. OutputDir=..\..\build LicenseFile=license.rtf [Files] ; Make a backup of wrapper config Source: "{app}\config\wrapper.conf"; DestDir: "{app}\config\wrapper.conf.orig"; Flags: external skipifsourcedoesntexist ; Router Source: "..\..\lib\avis-router.jar"; DestDir: "{app}\lib" Source: "..\..\etc\avisd.config"; DestDir: "{app}\config"; Flags: onlyifdoesntexist Source: "..\..\doc\LICENSE.txt"; DestDir: "{app}" ; Client utilities Source: "..\..\..\client\lib\avis-client.jar"; DestDir: "{app}\lib" Source: "..\..\..\client\lib\avis-tools.jar"; DestDir: "{app}\lib" Source: "..\..\..\client\bin\ec.cmd"; DestDir: "{app}\bin" Source: "..\..\..\client\bin\ep.cmd"; DestDir: "{app}\bin" ; Service wrapper Source: "service\lib\wrapper.jar"; DestDir: "{app}\lib" Source: "service\lib\wrapper.dll"; DestDir: "{app}\lib" Source: "service\bin\wrapper.exe"; DestDir: "{app}\bin" Source: "service\bin\avis.cmd"; DestDir: "{app}\bin" Source: "service\bin\install_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\bin\uninstall_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\bin\start_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\bin\stop_avis_service.cmd"; DestDir: "{app}\bin" Source: "service\config\wrapper.conf"; DestDir: "{app}\config"; Flags: onlyifdoesntexist Source: "service\logs\empty.log"; DestDir: "{app}\logs"; DestName: "avis.log"; Flags: onlyifdoesntexist ; JRE Files ; Source: "@jre_installer@"; DestDir: "{app}"; DestName: "jre.exe"; Flags: deleteafterinstall [Tasks] Name: installservice; Description: "Install as a system service" Name: programicon; Description: "Create icons in the Programs menu" [Icons] Name: "{group}\Run Avis In Console"; Filename: "{app}\bin\avis.cmd"; WorkingDir: "{app}\bin"; Tasks: programicon Name: "{group}\Start Avis Service"; Filename: "{app}\bin\start_avis_service.cmd"; WorkingDir: "{app}\bin"; Tasks: programicon Name: "{group}\Stop Avis Service"; Filename: "{app}\bin\stop_avis_service.cmd"; WorkingDir: "{app}\bin"; Tasks: programicon Name: "{group}\Avis Config File"; Filename: "{app}\config\avisd.config"; WorkingDir: "{app}\config"; Tasks: programicon [Run] Filename: "{app}\bin\uninstall_avis_service.cmd"; Description: "Install as a system service"; StatusMsg: "Uninstalling existing Avis service..."; Flags: runhidden; Tasks: installservice Filename: "{app}\bin\install_avis_service.cmd"; Description: "Install as a system service"; StatusMsg: "Installing Avis as a service..."; Flags: runhidden; Tasks: installservice Filename: "{app}\bin\start_avis_service.cmd"; Description: "Start the Avis service"; StatusMsg: "Starting the Avis service..."; Flags: runhidden; Tasks: installservice ; Filename: "{app}\jre.exe"; Parameters: "/s /v""/qn ADDLOCAL=jrecore IEXPLORER=1 MOZILLA=1"" "; Description: "Install Java runtime"; StatusMsg: "Installing Java..."; Check: JavaCheck [UninstallRun] Filename: "{app}\bin\uninstall_avis_service.cmd"; Flags: runhidden [UninstallDelete] Type: filesandordirs; Name: "{app}\logs" avis-1.2.2/server/packaging/windows/service/0000755000175000017500000000000010750123552020670 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/service/bin/0000755000175000017500000000000012200662653021442 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/service/bin/stop_avis_service.cmd0000644000175000017500000000227510750123554025664 0ustar dpocockdpocock@echo off setlocal rem Copyright (c) 1999, 2006 Tanuki Software Inc. rem rem Java Service Wrapper general NT service install script rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem rem Find the application home. rem rem %~dp0 is location of current script under NT set _REALPATH=%~dp0 rem Decide on the wrapper binary. set _WRAPPER_BASE=wrapper set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe if exist "%_WRAPPER_EXE%" goto conf echo Unable to locate a Wrapper executable using any of the following names: echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe echo %_REALPATH%%_WRAPPER_BASE%.exe pause goto :eof rem rem Find the wrapper.conf rem :conf set _WRAPPER_CONF="%~f1" if not %_WRAPPER_CONF%=="" goto startup set _WRAPPER_CONF="%_REALPATH%..\config\wrapper.conf" rem rem Stop the service. rem :startup "%_WRAPPER_EXE%" -p %_WRAPPER_CONF% avis-1.2.2/server/packaging/windows/service/bin/avis.cmd0000644000175000017500000000262110750123554023072 0ustar dpocockdpocock@echo off setlocal rem Copyright (c) 1999, 2006 Tanuki Software Inc. rem rem Java Service Wrapper general startup script rem rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem rem Find the application home. rem rem %~dp0 is location of current script under NT set _REALPATH=%~dp0 rem Decide on the wrapper binary. set _WRAPPER_BASE=wrapper set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe if exist "%_WRAPPER_EXE%" goto conf echo Unable to locate a Wrapper executable using any of the following names: echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe echo %_REALPATH%%_WRAPPER_BASE%.exe pause goto :eof rem rem Find the wrapper.conf rem :conf set _WRAPPER_CONF="%~f1" if not %_WRAPPER_CONF%=="" goto startup set _WRAPPER_CONF="%_REALPATH%..\config\wrapper.conf" rem rem Start the Wrapper rem :startup "%_WRAPPER_EXE%" -c %_WRAPPER_CONF% if not errorlevel 1 goto :eof pause avis-1.2.2/server/packaging/windows/service/bin/start_avis_service.cmd0000644000175000017500000000227610750123554026035 0ustar dpocockdpocock@echo off setlocal rem Copyright (c) 1999, 2006 Tanuki Software Inc. rem rem Java Service Wrapper general NT service install script rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem rem Find the application home. rem rem %~dp0 is location of current script under NT set _REALPATH=%~dp0 rem Decide on the wrapper binary. set _WRAPPER_BASE=wrapper set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe if exist "%_WRAPPER_EXE%" goto conf echo Unable to locate a Wrapper executable using any of the following names: echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe echo %_REALPATH%%_WRAPPER_BASE%.exe pause goto :eof rem rem Find the wrapper.conf rem :conf set _WRAPPER_CONF="%~f1" if not %_WRAPPER_CONF%=="" goto startup set _WRAPPER_CONF="%_REALPATH%..\config\wrapper.conf" rem rem Start the service. rem :startup "%_WRAPPER_EXE%" -t %_WRAPPER_CONF% avis-1.2.2/server/packaging/windows/service/bin/install_avis_service.cmd0000644000175000017500000000232110750123554026335 0ustar dpocockdpocock@echo off setlocal rem Copyright (c) 1999, 2006 Tanuki Software Inc. rem rem Java Service Wrapper general NT service install script rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem rem Find the application home. rem rem %~dp0 is location of current script under NT set _REALPATH=%~dp0 rem Decide on the wrapper binary. set _WRAPPER_BASE=wrapper set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe if exist "%_WRAPPER_EXE%" goto conf echo Unable to locate a Wrapper executable using any of the following names: echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe echo %_REALPATH%%_WRAPPER_BASE%.exe pause goto :eof rem rem Find the wrapper.conf rem :conf set _WRAPPER_CONF="%~f1" if not %_WRAPPER_CONF%=="" goto startup set _WRAPPER_CONF="%_REALPATH%..\config\wrapper.conf" rem rem Install the Wrapper as an NT service. rem :startup "%_WRAPPER_EXE%" -i %_WRAPPER_CONF% avis-1.2.2/server/packaging/windows/service/bin/uninstall_avis_service.cmd0000644000175000017500000000232510750123554026704 0ustar dpocockdpocock@echo off setlocal rem Copyright (c) 1999, 2006 Tanuki Software Inc. rem rem Java Service Wrapper general NT service uninstall script rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem rem Find the application home. rem rem %~dp0 is location of current script under NT set _REALPATH=%~dp0 rem Decide on the wrapper binary. set _WRAPPER_BASE=wrapper set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe if exist "%_WRAPPER_EXE%" goto conf set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe if exist "%_WRAPPER_EXE%" goto conf echo Unable to locate a Wrapper executable using any of the following names: echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe echo %_REALPATH%%_WRAPPER_BASE%.exe pause goto :eof rem rem Find the wrapper.conf rem :conf set _WRAPPER_CONF="%~f1" if not %_WRAPPER_CONF%=="" goto startup set _WRAPPER_CONF="%_REALPATH%..\config\wrapper.conf" rem rem Uninstall the Wrapper as an NT service. rem :startup "%_WRAPPER_EXE%" -r %_WRAPPER_CONF% avis-1.2.2/server/packaging/windows/service/logs/0000755000175000017500000000000010750123550021632 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/service/logs/empty.log0000644000175000017500000000000010750123550023461 0ustar dpocockdpocockavis-1.2.2/server/packaging/windows/service/lib/0000755000175000017500000000000012200662656021443 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/service/config/0000755000175000017500000000000010750123550022133 5ustar dpocockdpocockavis-1.2.2/server/packaging/windows/service/config/wrapper.conf0000644000175000017500000000751710750123550024474 0ustar dpocockdpocock#******************************************************************** # Avis Windows Service Wrapper Properties #******************************************************************** # Java Application (Empty => use registry) wrapper.java.command= # wrapper.java.command=java # wrapper.java.command=%JAVA_HOME%/bin/java # Java Main class. This class must implement the WrapperListener interface # or guarantee that the WrapperManager class is initialized. Helper # classes are provided to do this for you. See the Integration section # of the documentation for details. wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp # Java Classpath (include wrapper.jar) Add class path elements as # needed starting from 1 wrapper.java.classpath.1=../lib/wrapper.jar wrapper.java.classpath.2=../lib/avis-router.jar # Java Library Path (location of Wrapper.DLL or libwrapper.so) wrapper.java.library.path.1=../lib # Java Additional Parameters wrapper.java.additional.1=-Xverify:none # uncomment to use faster server VM (only with JDK, not available in # JRE) # wrapper.java.additional.2=-server # Initial Java Heap Size (in MB) wrapper.java.initmemory=12 # Maximum Java Heap Size (in MB) wrapper.java.maxmemory=96 # Application parameters. Add parameters as needed starting from 1 wrapper.app.parameter.1=org.avis.router.Main wrapper.app.parameter.2=-c wrapper.app.parameter.3=../config/avisd.config # wrapper.app.parameter.4=-vv #******************************************************************** # Wrapper Logging Properties #******************************************************************** # Format of output for the console. (See docs for formats) wrapper.console.format=PM # Log Level for console output. (See docs for log levels) wrapper.console.loglevel=INFO # Log file to use for wrapper output logging. wrapper.logfile=../logs/avis.log # Format of output for the log file. (See docs for formats) wrapper.logfile.format=LPTM # Log Level for log file output. (See docs for log levels) wrapper.logfile.loglevel=INFO # Maximum size that the log file will be allowed to grow to before # the log is rolled. Size is specified in bytes. The default value # of 0, disables log rolling. May abbreviate with the 'k' (kb) or # 'm' (mb) suffix. For example: 10m = 10 megabytes. wrapper.logfile.maxsize=4M # Maximum number of rolled log files which will be allowed before old # files are deleted. The default value of 0 implies no limit. wrapper.logfile.maxfiles=10 # Log Level for sys/event log output. (See docs for log levels) wrapper.syslog.loglevel=NONE #******************************************************************** # Wrapper Windows Properties #******************************************************************** # Title to use when running as a console wrapper.console.title=Avis event router -- Press Ctrl+C to exit #******************************************************************** # Wrapper Windows NT/2000/XP Service Properties #******************************************************************** # WARNING - Do not modify any of these properties when an application # using this configuration file has been installed as a service. # Please uninstall the service before modifying this section. The # service can then be reinstalled. # Name of the service wrapper.ntservice.name=Avis # Display name of the service wrapper.ntservice.displayname=Avis # Description of the service wrapper.ntservice.description=The Avis event router service # Service dependencies. Add dependencies as needed starting from 1 # wrapper.ntservice.dependency.1= # Mode in which the service is installed. AUTO_START or DEMAND_START wrapper.ntservice.starttype=AUTO_START # Allow the service to interact with the desktop. wrapper.ntservice.interactive=false avis-1.2.2/server/packaging/fedora/0000755000175000017500000000000010750123550016774 5ustar dpocockdpocockavis-1.2.2/server/packaging/fedora/avis.spec0000644000175000017500000000456510764745724020646 0ustar dpocockdpocockName: avis Summary: Event Router Vendor: Matthew Phillips Packager: Avis Project Distribution: Sourceforge Group: System/Servers License: GPL Version: %{_avis_version} Release: %{_avis_release} URL: http://avis.sourceforge.net/ # build information Prefix: %{_prefix} BuildRoot: %{_topdir} AutoReq: no AutoReqProv: no BuildArchitectures: noarch # Requires: java >= 0:1.5 # Requires: jdk >= 0:1.5 %description Avis is a multicast event bus. It provides a fast, publish/subscribe event routing service compatible with the commercial Elvin implementation developed by Mantara Software. Elvin routers can be federated together to form wide-area event notification networks. Clients can exchange events with other clients anywhere on the bus, subscribing to messages using pattern-matching expressions that select messages based on their content. %install # create installation hierarchy mkdir -p -m 755 \ $RPM_BUILD_ROOT%{_prefix}/bin \ $RPM_BUILD_ROOT%{_prefix}/libexec/avis \ $RPM_BUILD_ROOT%{_prefix}/var/avis \ $RPM_BUILD_ROOT/etc/avis \ $RPM_BUILD_ROOT/etc/init.d # install client and server libs install -c -m 644 \ %{_avis_server}/lib/avis-router.jar \ %{_avis_client}/lib/avis-client.jar \ %{_avis_client}/lib/avis-tools.jar \ $RPM_BUILD_ROOT%{_prefix}/libexec/avis/ # install default server configuration install -c -m 644 \ %{_avis_server}/etc/avisd.config \ $RPM_BUILD_ROOT/etc/avis/ # install ec/ep install -c -m 755 \ %{_avis_client}/bin/ec %{_avis_client}/bin/ep \ $RPM_BUILD_ROOT%{_prefix}/bin/ # install avisd install -Dp -m 0755 \ %{_avis_server}/bin/avisd \ $RPM_BUILD_ROOT%{_prefix}/sbin/avisd # init script sed -e "s|__PREFIX__|%{_prefix}|g" \ < %{_avis_server}/packaging/fedora/rc_init_script.in > %{_tmppath}/avisd install -Dp -m 0755 \ %{_tmppath}/avisd $RPM_BUILD_ROOT/etc/init.d/avisd %files %defattr(-,root,root) %{_prefix}/sbin/avisd %{_prefix}/bin/ec %{_prefix}/bin/ep %{_prefix}/libexec/avis/avis-router.jar %{_prefix}/libexec/avis/avis-tools.jar %{_prefix}/libexec/avis/avis-client.jar /etc/init.d/avisd %config /etc/avis/avisd.config avis-1.2.2/server/packaging/fedora/rc_init_script.in0000644000175000017500000000310210750123550022333 0ustar dpocockdpocock#!/bin/sh # # chkconfig: 2345 86 14 # description: Starts and stops the Avis event router daemon. # # pidfile: /var/run/avisd.pid # config: /etc/avis/avisd.config # Source function library. if [ -f /etc/init.d/functions ] ; then . /etc/init.d/functions elif [ -f /etc/rc.d/init.d/functions ] ; then . /etc/rc.d/init.d/functions else exit 0 fi # Avoid using root's TMPDIR unset TMPDIR AVISD=__PREFIX__/sbin/avisd AVISD_CONFIG=/etc/avis/avisd.config AVISD_LOGFILE=/var/log/avisd.log AVISD_PIDFILE=/var/run/avisd.pid AVISD_OPTIONS="-daemon -c $AVISD_CONFIG -logfile $AVISD_LOGFILE -pidfile $AVISD_PIDFILE" # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && echo "No network" && exit 0 # Check that avisd.config exists. [ -f $AVISD_CONFIG ] || ( echo "No config" && exit 0 ) RETVAL=0 start() { KIND="Avis" echo -n $"Starting $KIND service: " daemon $AVISD $AVISD_OPTIONS RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/avisd || \ RETVAL=1 return $RETVAL } stop() { KIND="Avis" echo -n $"Shutting down $KIND service: " killproc avisd RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/avisd echo "" return $RETVAL } restart() { stop start } # Check that we can write to it... so non-root users stop here [ -w $AVISD_CONFIG ] || exit 0 case "$1" in start) start ;; stop) stop ;; restart) restart ;; condrestart) [ -f /var/lock/subsys/avisd ] && restart || : ;; *) echo $"Usage: $0 {start|stop|restart|condrestart}" exit 1 esac exit $? avis-1.2.2/server/packaging/unix/0000755000175000017500000000000010764745060016532 5ustar dpocockdpocockavis-1.2.2/server/packaging/unix/install.sh0000644000175000017500000000202010750123550020513 0ustar dpocockdpocock#!/bin/sh # Avis install script for Unix systems. # # Run with -h option for usage root=$(dirname $0)/root prefix=/usr/local usage () { local NL=$'\x0a' local help="\ Usage: $0 [-h|--help] [--prefix dir]$NL\ -h|--help : This text$NL\ --prefix dir : Set install dir prefix (default is \"/usr/local\")$NL" echo "$help" >&2 } OPTS=`getopt -o h --long prefix:,help -n '$0' -- "$@"` if [ $? != 0 ] ; then exit 1 ; fi eval set -- "$OPTS" while [ $# -gt 0 ]; do case "$1" in --prefix) prefix=$2 ; shift 2 ;; -h|--help) usage ; exit 0 ;; --) shift ; break ;; *) echo "!error" ; shift 1 ;; esac done install -dv -m 0755 -o root -g root $prefix/bin $prefix/libexec/avis install -Dpv -m 0755 -o root -g root $root/sbin/avisd $prefix/sbin/avisd install -pv -m 0755 -o root -g root $root/bin/* $prefix/bin install -pv -m 0644 -o root -g root $root/libexec/avis/*.jar \ $prefix/libexec/avis install -Dpv -m 0644 -o root -g root $root/etc/avis/avisd.config \ $prefix/etc/avis/avisd.config avis-1.2.2/server/packaging/unix/README.txt0000644000175000017500000000350710764745060020235 0ustar dpocockdpocockThis is a generic Avis install package for Unix systems not directly supported with native installation packages. Avis requires a Java 1.5.0 (or later) runtime. See http://java.sun.com/javase/downloads. Installation ---------------------------------------------------------------------- You can install the files directly to /usr/local using the installer script: > sudo ./install.sh --prefix=/usr/local Or, using GNU stow (http://www.gnu.org/software/stow): # install Avis into stow area > AVIS_HOME=/usr/local/stow/avis-1.2.0 > sudo mkdir $AVIS_HOME > sudo ./install.sh --prefix=$AVIS_HOME # install Avis using stow > (cd $AVIS_HOME/.. && sudo stow $(basename $AVIS_HOME)) This way is recommended, since it allows safe, easy uninstall of Avis with: > (cd $AVIS_HOME/.. && sudo stow --delete $(basename $AVIS_HOME)) Running The Avis Service ---------------------------------------------------------------------- Avis will run without any configuration required, but if you do want to change any of the router parameters a config file template is installed in: /usr/local/etc/avis/avisd.config. You can leave the config here, or copy it wherever you would prefer to keep the config file. Run Avis with: > /usr/local/sbin/avisd Or, if you have a customised configuration: > /usr/local/sbin/avisd -c /usr/local/etc/avis/avisd.config Manual Uninstallation ---------------------------------------------------------------------- An uninstall target is not provided in the script since the author doesn't want to risk damaging your system. If you don't use stow to manage installation/uninstallation, the files installed can be found by prepending the install prefix (default is /usr/local) to each line of the output of the the following command in the directory you untarred the installation package: > (cd root && find) avis-1.2.2/server/packaging/osx/0000755000175000017500000000000010750123550016345 5ustar dpocockdpocockavis-1.2.2/server/packaging/osx/Description.plist0000644000175000017500000000060110750123550021702 0ustar dpocockdpocock IFPkgDescriptionDescription Avis event router. See http://avis.sourceforge.net for more information. IFPkgDescriptionTitle Avis avis-1.2.2/server/packaging/osx/license.rtf0000644000175000017500000000153010750123550020503 0ustar dpocockdpocock{\rtf1\ansi\ansicpg1252\cocoartf949 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \paperw11900\paperh16840\margl1440\margr1440\vieww9000\viewh8400\viewkind0 \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural \f0\fs24 \cf0 Avis is Copyright (C) 2008 Matthew Phillips .\ \ Permission to distribute unmodified copies of this Avis package is granted.\ \ The source code to Avis is available under the terms of the GNU General Public License (GPL) -- see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/copyleft/gpl.html"}}{\fldrslt http://www.gnu.org/copyleft/gpl.html}} for the full text of the license.\ \ Please see {\field{\*\fldinst{HYPERLINK "http://avis.sourceforge.net"}}{\fldrslt http://avis.sourceforge.net}} for more information.\ }avis-1.2.2/server/packaging/osx/welcome.txt0000644000175000017500000000044210750123550020541 0ustar dpocockdpocockWelcome to the Avis installer. This will install Avis as a system service on your computer. After installation, you can start Avis using the command: sudo launchctl load /Library/LaunchDaemons/org.avis.avisd.plist See http://avis.sourceforge.net/installation.html for more information.avis-1.2.2/server/packaging/osx/Info.plist.in0000644000175000017500000000243710750123550020730 0ustar dpocockdpocock CFBundleGetInfoString @release@, Copyright (C) 2008 Matthew Phillips CFBundleIdentifier org.avis.avisd CFBundleShortVersionString @version@ IFMajorVersion @major_version@ IFMinorVersion @minor_version@ IFPkgFlagAllowBackRev IFPkgFlagAuthorizationAction AdminAuthorization IFPkgFlagBackgroundAlignment topleft IFPkgFlagBackgroundScaling none IFPkgFlagDefaultLocation / IFPkgFlagFollowLinks IFPkgFlagInstallFat IFPkgFlagIsRequired IFPkgFlagOverwritePermissions IFPkgFlagRelocatable IFPkgFlagRestartAction NoRestart IFPkgFlagRootVolumeOnly IFPkgFlagUpdateInstalledLanguages IFPkgFormatVersion 0.10000000149011612 avis-1.2.2/server/packaging/osx/org.avis.avisd.plist0000644000175000017500000000112310750123550022254 0ustar dpocockdpocock Label org.avis.avisd Disabled OnDemand RunAtLoad ProgramArguments /usr/local/sbin/avisd -logfile /var/log/avisd.log -c /usr/local/etc/avis/avisd.config avis-1.2.2/server/etc/0000755000175000017500000000000010750123560014364 5ustar dpocockdpocockavis-1.2.2/server/etc/avisd.config0000644000175000017500000003101610750123560016662 0ustar dpocockdpocock# Avis.Config-Version=2 ## # Avis router configuration. # # This file contains configuration settings for the Avis event # router. Uncomment and modify settings as required. # # For numeric values, the suffixes "K" and "M" may be used to indicate # units of 1 kilobyte == 1024 == 2^10 bytes, and 1 megabyte == # 1,048,576 == or 2^20 bytes. e.g. 4K == 4096 bytes # # For boolean (true/false) values, "1", "true" or "yes" may be used # for true, "0", "false" or "no" may be used for false. ## ###################################################################### # Router options ###################################################################### ## # The default port to run Avis service on. This may be overridden by # an explicit port setting in the "Listen" URI's below. # Port=2917 ## # Specifies the network addresses for the Avis router to listen on for # client connections. This option contains one or more # whitespace-separated Elvin URI's specifying a network address and a # protocol. # # Using the wildcard address "0.0.0.0" binds to all IPv4 and IPv6 # addresses. Using "!" as a prefix in the host name selects all # addresses on a network interface rather than associated with a host # name or IP (e.g. "elvin://!eth0:1234"). # # Examples: # Listen=elvin://hostname:1234 # Listen=elvin:/tcp,none,xdr/!eth0:1234 # Listen=elvin:/tcp,none,xdr/hostname:1234 \ # elvin:/secure/171.4.87.72:4567 # # Default: Listen=elvin://0.0.0.0 # Listen=elvin://0.0.0.0 ###################################################################### # Secure client connections (SSL/TLS) ###################################################################### ## # To set up secure connections using SSL/TLS, define one or more # secure URI's in the Listen setting, for example # elvin:/secure/0.0.0.0:29170. You must create your own private key: # see the TLS.Keystore option for more info. ## ## # A space-separated list of host names and/or IP addresses that must # be positively authenticated before they can connect. You can use # Unix-style wildcards ("*" and "?") in the host names, or simply "*" # to require authentication for all hosts. Since the only supported # authentication mechanism is TLS, clients in this list must connect # and successfully authenticate using TLS before being able to # establish a link. # # See also: Federation.Require-Authenticated # Default: nothing # Require-Authenticated=*.somewhere.org host.elsewhere.com # Require-Authenticated=* ## # The filename or URI for a keystore containing keys and certificates # used to secure a connection, and trusted certificates used to # authenticate clients. If not an absolute path, this path is resolved # relative to this configuration file. # # Default: No keystore. You need to generate a private key inside a # keystore before you use TLS using the keytool command that comes # with the JDK: see the instructions at # http://avis.sourceforge.net/tls.html for details. # # keytool -genkey -alias my-router-key -keysize 512 \ # -validity 3650 -keyalg RSA \ # -dname "CN=My Name, \ # OU=my.organisation.unit, \ # O=My Organisation, L=My Location, \ # S=My State, C=My Country Code" \ # -keypass mypassword -storepass mypassword \ # -keystore avis-router.keystore # # TLS.Keystore=avis-router.keystore ## # Pass phrase for verifying/securing the router's keystore. # # Default: no passphrase # TLS.Keystore-Passphrase=my-passphrase ###################################################################### # Federation ###################################################################### ## # A federation class is a named type of federation link, with # associated incoming (Federation.Subscribe) and outgoing # (Federation.Provide) notification filters: if either of these is # unspecified, they default to FALSE, i.e. allow nothing. You may wish # to set up different federation classes for different partners to # control what goes in and out of the router. # # The options that apply to federation classes use the notation: # #