├── META-INF └── MANIFEST.MF ├── src ├── main │ └── java │ │ ├── META-INF │ │ └── MANIFEST.MF │ │ └── net │ │ └── sharksystem │ │ ├── asap │ │ ├── protocol │ │ │ ├── ASAP_OfferPDU_1_0.java │ │ │ ├── ASAP_PDU_Management.java │ │ │ ├── ThreadFinishedListener.java │ │ │ ├── ASAPOnlineMessageSource.java │ │ │ ├── ASAP_Interest_PDU_1_0.java │ │ │ ├── ASAPExecTimeExceededException.java │ │ │ ├── ASAPConnectionListener.java │ │ │ ├── ASAPConnection.java │ │ │ ├── ASAPEncounterList.java │ │ │ ├── ASAP_AssimilationPDU_1_0.java │ │ │ ├── ASAPProtocolEngine.java │ │ │ └── ASAP_PDU_1_0.java │ │ ├── cmdline │ │ │ ├── TCPStreamCreatedListener.java │ │ │ ├── TCPASAPPeerFS.java │ │ │ └── ExampleASAPChunkReceivedListener.java │ │ ├── crypto │ │ │ ├── ASAPPoint2PointCryptoSettings.java │ │ │ ├── SharkCryptoException.java │ │ │ ├── ASAPPoint2PointCryptoSettingsImpl.java │ │ │ ├── ASAPKeyStore.java │ │ │ └── ASAPCryptoParameterStorage.java │ │ ├── ASAPChannelContentChangedListener.java │ │ ├── engine │ │ │ ├── ASAPInternalOnlinePeersChangedListener.java │ │ │ ├── CryptoControl.java │ │ │ ├── ASAPMemento.java │ │ │ ├── ASAPStorageImpl.java │ │ │ ├── MessagesContainer.java │ │ │ ├── EngineSetting.java │ │ │ ├── ASAPEngineFSSetting.java │ │ │ ├── ASAPCommunicationSetting.java │ │ │ ├── ASAPUndecryptableMessageHandler.java │ │ │ ├── MessageIter.java │ │ │ ├── ASAPOnlineMessageSender.java │ │ │ ├── ASAPAbstractOnlineMessageSender.java │ │ │ ├── ASAPChunkAssimilatedListener.java │ │ │ ├── ASAPEnginePermissionSettings.java │ │ │ ├── ASAPProtocolEngine.java │ │ │ ├── ASAPSingleProcessOnlineMessageSender.java │ │ │ ├── ASAPInternalChunk.java │ │ │ ├── ASAPInMemoTransientMessages.java │ │ │ ├── ASAPChannelImpl.java │ │ │ └── DefaultSecurityAdministrator.java │ │ ├── listenermanager │ │ │ ├── GenericNotifier.java │ │ │ ├── management │ │ │ │ ├── ASAPManagementStorage.java │ │ │ │ ├── ASAPManagementCreateASAPStorageMessage.java │ │ │ │ ├── ASAPManagementStorageImpl.java │ │ │ │ └── ASAPManagementMessage.java │ │ │ ├── ASAPEnvironmentChangesListenerManager.java │ │ │ ├── GenericListenerImplementation.java │ │ │ ├── ASAPChannelContentChangedListenerManager.java │ │ │ └── ASAPMessageReceivedListenerManager.java │ │ ├── ASAPMessageReceivedListener.java │ │ ├── ASAPEncounterHelper.java │ │ ├── ASAPEnvironmentChangesListener.java │ │ ├── ASAPSecurityException.java │ │ ├── ASAPException.java │ │ ├── ASAPEnvironmentChangesListenerManagement.java │ │ ├── ASAPChannelContentChangedListenerManagement.java │ │ ├── ASAPMessageReceivedListenerManagement.java │ │ ├── ASAPMessageCompare.java │ │ ├── ASAPEncounterConnectionType.java │ │ ├── ASAPPeerService.java │ │ ├── utils │ │ │ ├── PeerIDHelper.java │ │ │ ├── ASAPMessages2StringCollectionWrapper.java │ │ │ ├── ASAPPeerHandleConnectionThread.java │ │ │ ├── DateTimeHelper.java │ │ │ ├── ASAPLogHelper.java │ │ │ ├── ASAPChunkReceivedTester.java │ │ │ └── Batchprocessor.java │ │ ├── ASAPHop.java │ │ ├── ASAPChunkStorage.java │ │ ├── ASAPUtils.java │ │ ├── ASAPChannel.java │ │ ├── apps │ │ │ ├── TCPServerSocketAcceptor.java │ │ │ └── testsupport │ │ │ │ ├── ASAPTestPeerFS.java │ │ │ │ └── TestASAPConnectionHandler.java │ │ ├── ASAPMessageSender.java │ │ ├── ASAPHopImpl.java │ │ ├── ASAPConnectionHandler.java │ │ ├── ASAPMessages.java │ │ ├── ASAPEncounterManagerAdmin.java │ │ ├── ASAPPeer.java │ │ ├── ASAPListenerManagingPeer.java │ │ ├── ASAPChunk.java │ │ ├── ASAP.java │ │ └── ASAPStorage.java │ │ ├── utils │ │ ├── AlarmClockListener.java │ │ ├── tcp │ │ │ └── StreamPairCreatedListener.java │ │ ├── streams │ │ │ ├── WrappedStreamPairListener.java │ │ │ ├── StreamPairListener.java │ │ │ ├── StreamPair.java │ │ │ ├── StreamPairListenerManager.java │ │ │ ├── StreamPairLink.java │ │ │ ├── IdleStreamPairCloser.java │ │ │ ├── StreamPairImpl.java │ │ │ ├── StreamLink.java │ │ │ └── StreamPairWrapper.java │ │ ├── testsupport │ │ │ ├── TestConstants.java │ │ │ ├── ASAPMessageReceivedStorage.java │ │ │ └── TestHelper.java │ │ ├── AlarmClock.java │ │ ├── Commandline.java │ │ ├── Log.java │ │ └── SerializationHelper.java │ │ ├── SharkException.java │ │ ├── SharkNotSupportedException.java │ │ ├── fs │ │ ├── ExtraData.java │ │ └── FSUtils.java │ │ └── testhelper │ │ └── ASAPTesthelper.java └── test │ └── java │ ├── net │ └── sharksystem │ │ ├── TodoTests.java │ │ ├── asap │ │ ├── helper │ │ │ └── HelperTester.java │ │ ├── crypto │ │ │ ├── PersistInMemoKeystore.java │ │ │ └── CryptoUsage.java │ │ ├── mockAndTemplates │ │ │ ├── ASAPMessageReceivedListenerExample.java │ │ │ └── TestUtils.java │ │ ├── engine │ │ │ ├── ChunkPrinter.java │ │ │ ├── BasisCryptoTests.java │ │ │ ├── BasisMethodsTests.java │ │ │ ├── ChunkCacheTests.java │ │ │ └── WhiteBoxTests.java │ │ ├── appTests │ │ │ └── Reenactment.java │ │ ├── peer │ │ │ └── Point2Point2Test2.java │ │ └── serialization │ │ │ └── SerializationTests.java │ │ ├── streams │ │ ├── TestConstants.java │ │ └── DataExchangeTester.java │ │ ├── testsupport │ │ └── StoreReceivedMessages.java │ │ ├── CommandlineTests.java │ │ ├── CountsReceivedMessagesListener.java │ │ └── V1TestSuite.java │ └── bugreports │ └── BugReports.java ├── manifest.mf ├── .gitignore ├── README.md ├── pom.xml └── nbbuild.xml /META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: net.sharksystem.asap.cmdline.CmdLineUI 3 | 4 | -------------------------------------------------------------------------------- /src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: net.sharksystem.asap.cmdline.CmdLineUI 3 | 4 | -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | Main-Class: net.sharksystem.asap.cmdline.CmdLineUI 4 | 5 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAP_OfferPDU_1_0.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | public interface ASAP_OfferPDU_1_0 extends ASAP_PDU_1_0 { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/AlarmClockListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils; 2 | 3 | public interface AlarmClockListener { 4 | void alarmClockRinging(int yourKey); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAP_PDU_Management.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | interface ASAP_PDU_Management { 4 | void setVerified(boolean b); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ThreadFinishedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | public interface ThreadFinishedListener { 4 | void finished(Thread t); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/cmdline/TCPStreamCreatedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.cmdline; 2 | 3 | public interface TCPStreamCreatedListener { 4 | void streamCreated(TCPStream channel); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/crypto/ASAPPoint2PointCryptoSettings.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | public interface ASAPPoint2PointCryptoSettings { 4 | boolean mustEncrypt(); 5 | boolean mustSign(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPChannelContentChangedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public interface ASAPChannelContentChangedListener { 4 | void asapChannelContentChanged(CharSequence format, CharSequence uri, int era); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPInternalOnlinePeersChangedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | public interface ASAPInternalOnlinePeersChangedListener { 4 | void notifyOnlinePeersChanged(ASAPInternalPeer engine); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/CryptoControl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.protocol.ASAP_PDU_1_0; 4 | 5 | public interface CryptoControl { 6 | boolean allowed2Process(ASAP_PDU_1_0 pdu); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/GenericNotifier.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager; 2 | 3 | public interface GenericNotifier { 4 | /** 5 | * run specific notification 6 | */ 7 | void doNotify(L listener); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/tcp/StreamPairCreatedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.tcp; 2 | 3 | import net.sharksystem.utils.streams.StreamPair; 4 | 5 | public interface StreamPairCreatedListener { 6 | void streamPairCreated(StreamPair streamPair); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/WrappedStreamPairListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | public interface WrappedStreamPairListener extends StreamPairListener { 4 | /** data read or written 5 | * @param key*/ 6 | void notifyAction(String key); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/StreamPairListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | public interface StreamPairListener { 4 | /** stream was closed 5 | * @param closedStreamPair 6 | * @param key*/ 7 | void notifyClosed(StreamPair closedStreamPair, String key); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPMemento.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * The memento for the engine. 7 | * 8 | * @author thsc 9 | */ 10 | interface ASAPMemento { 11 | public void save(ASAPEngine engine) throws IOException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAPOnlineMessageSource.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public interface ASAPOnlineMessageSource { 7 | void sendStoredMessages(ASAPConnection asapConnection, OutputStream os) throws IOException; 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/TodoTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem; 2 | 3 | 4 | import org.junit.runner.RunWith; 5 | import org.junit.runners.Suite; 6 | import net.sharksystem.asap.engine.Point2PointTests; 7 | 8 | @RunWith(Suite.class) 9 | @Suite.SuiteClasses({ 10 | Point2PointTests.class 11 | }) 12 | public class TodoTests { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPStorageImpl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPChunkStorage; 4 | import net.sharksystem.asap.ASAPException; 5 | import net.sharksystem.asap.protocol.ASAP_1_0; 6 | 7 | import java.io.IOException; 8 | 9 | public abstract class ASAPStorageImpl implements ASAPInternalStorage { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAP_Interest_PDU_1_0.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | import java.util.Map; 4 | 5 | public interface ASAP_Interest_PDU_1_0 extends ASAP_PDU_1_0 { 6 | boolean eraFromSet(); 7 | boolean eraToSet(); 8 | int getEraFrom(); 9 | int getEraTo(); 10 | Map getEncounterMap(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPMessageReceivedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | public interface ASAPMessageReceivedListener { 7 | void asapMessagesReceived(ASAPMessages messages, String senderE2E, // E2E part 8 | List asapHops /* Point-to-point part */ ) throws IOException; 9 | } -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/cmdline/TCPASAPPeerFS.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.cmdline; 2 | 3 | /** 4 | * Run an ASAP peer that can communicate with TCP. It can offer an open tcp server socket. 5 | * It can also be used as sub unit of a hub, see ASAPHub project. 6 | * It works as an ordinary peer otherwise. It is meant to be used as a repeater. 7 | */ 8 | public class TCPASAPPeerFS { 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /build/ 3 | /dist/ 4 | /workingdirectory/ 5 | build.xml 6 | .idea 7 | target/ 8 | pom.xml.tag 9 | pom.xml.releaseBackup 10 | pom.xml.versionsBackup 11 | pom.xml.next 12 | release.properties 13 | dependency-reduced-pom.xml 14 | buildNumber.properties 15 | .mvn/timing.properties 16 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 17 | .mvn/wrapper/maven-wrapper.jar 18 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAPExecTimeExceededException.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | 5 | public class ASAPExecTimeExceededException extends ASAPException { 6 | public ASAPExecTimeExceededException() { 7 | super(); 8 | } 9 | 10 | public ASAPExecTimeExceededException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/MessagesContainer.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.List; 8 | 9 | public interface MessagesContainer { 10 | void addMessage(InputStream is, long length) throws IOException; 11 | void setASAPHopList(List asapHopList) throws IOException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAPConnectionListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | public interface ASAPConnectionListener { 4 | /** 5 | * Called when first message was read from remote peer. 6 | * The session started. 7 | */ 8 | void asapConnectionStarted(String remotePeerName, ASAPConnection connection); 9 | 10 | void asapConnectionTerminated(Exception terminatingException, ASAPConnection connection); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/SharkException.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem; 2 | 3 | public class SharkException extends Exception { 4 | public SharkException() { super(); } 5 | public SharkException(String message) { 6 | super(message); 7 | } 8 | public SharkException(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | public SharkException(Throwable cause) { 12 | super(cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPEncounterHelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.Socket; 6 | 7 | public class ASAPEncounterHelper { 8 | public static final String getRemoteAddress(Socket connectedSocket) throws IOException { 9 | InetAddress inetAddress = connectedSocket.getInetAddress(); 10 | int port = connectedSocket.getPort(); 11 | return inetAddress.getHostAddress() + ":" + port; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/helper/HelperTester.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.helper; 2 | 3 | import net.sharksystem.asap.utils.ASAPSerialization; 4 | import net.sharksystem.utils.SerializationHelper; 5 | import org.junit.Test; 6 | 7 | public class HelperTester { 8 | @Test 9 | public void short2bytes() { 10 | long valueL = System.currentTimeMillis(); 11 | 12 | byte[] result = SerializationHelper.long2byteArray(valueL); 13 | 14 | ASAPSerialization.printByteArray(result); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/streams/TestConstants.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.streams; 2 | 3 | public interface TestConstants { 4 | String ROOT_DIRECTORY = "playground/"; 5 | String ALICE_ID = "ALICE"; 6 | String ALICE_NAME = "Alice"; 7 | String BOB_ID = "BOB"; 8 | String BOB_NAME = "Bob"; 9 | String CLARA_ID = "44"; 10 | String CLARA_NAME = "Clara"; 11 | String DAVID_ID = "45"; 12 | String DAVID_NAME = "David"; 13 | int DEFAULT_PORT = 7777; 14 | 15 | int maxTimeInSeconds = 1; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPEnvironmentChangesListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.util.Set; 4 | 5 | public interface ASAPEnvironmentChangesListener { 6 | /** 7 | * ASAP peers establish connections on their own and usually if possible. This 8 | * message is called if one or more connections could be established or got lost. 9 | * @param peerList current list of peer we have a connection to (can be empty) 10 | */ 11 | void onlinePeersChanged(Set peerList); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPSecurityException.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public class ASAPSecurityException extends ASAPException { 4 | public ASAPSecurityException() { 5 | super(); 6 | } 7 | public ASAPSecurityException(Throwable cause) { 8 | super(cause); 9 | } 10 | public ASAPSecurityException(String message) { 11 | super(message); 12 | } 13 | public ASAPSecurityException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/EngineSetting.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | public class EngineSetting { 4 | public final CharSequence folder; 5 | public ASAPChunkAssimilatedListener listener; 6 | public ASAPEngine engine; 7 | 8 | EngineSetting(CharSequence folder, ASAPChunkAssimilatedListener listener) { 9 | this.folder = folder; 10 | this.listener = listener; 11 | } 12 | 13 | void setASAPEngine(ASAPEngine engine) { 14 | this.engine = engine; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/crypto/SharkCryptoException.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | 5 | public class SharkCryptoException extends ASAPException { 6 | public SharkCryptoException() { super(); } 7 | 8 | public SharkCryptoException(String message) { super(message); } 9 | 10 | public SharkCryptoException(String message, Throwable cause) { super(message, cause); } 11 | 12 | public SharkCryptoException(Throwable cause) { super(cause); } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPEngineFSSetting.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | public class ASAPEngineFSSetting { 4 | final CharSequence folder; 5 | final CharSequence format; 6 | final ASAPChunkAssimilatedListener listener; 7 | 8 | public ASAPEngineFSSetting(CharSequence format, CharSequence folder, 9 | ASAPChunkAssimilatedListener listener) { 10 | this.format = format; 11 | this.folder = folder; 12 | this.listener = listener; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/SharkNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem; 2 | 3 | public class SharkNotSupportedException extends RuntimeException { 4 | public SharkNotSupportedException() { 5 | super(); 6 | } 7 | public SharkNotSupportedException(String message) { 8 | super(message); 9 | } 10 | public SharkNotSupportedException(String message, Throwable cause) { 11 | super(message, cause); 12 | } 13 | public SharkNotSupportedException(Throwable cause) { 14 | super(cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPException.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.SharkException; 4 | 5 | /** 6 | * 7 | * @author thsc 8 | */ 9 | public class ASAPException extends SharkException { 10 | public ASAPException() { super(); } 11 | public ASAPException(String message) { 12 | super(message); 13 | } 14 | public ASAPException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | public ASAPException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/bugreports/BugReports.java: -------------------------------------------------------------------------------- 1 | package bugreports; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * Any bug reports is very welcome and appreciated. 7 | */ 8 | public class BugReports { 9 | @Test 10 | public void perfectSoftware() { 11 | System.out.println("There is no such thing as perfect software. It is only less buggy. Thanks for any help."); 12 | } 13 | 14 | /** 15 | * Add your report here - thank you 16 | */ 17 | @Test 18 | public void yourBugReport() { 19 | System.out.println("Thanks for helping!!"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPCommunicationSetting.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.protocol.ASAP_AssimilationPDU_1_0; 4 | 5 | interface ASAPCommunicationSetting { 6 | boolean allowedToCreateChannel(ASAP_AssimilationPDU_1_0 asapAssimilationPDU); 7 | 8 | /** 9 | * @param on if true - message must be encrypted 10 | */ 11 | void setSendEncryptedMessages(boolean on); 12 | 13 | /** 14 | * @param on if true - message must be signed 15 | */ 16 | void setSendSignedMessages(boolean on); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/management/ASAPManagementStorage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager.management; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | 5 | import java.io.IOException; 6 | import java.util.Collection; 7 | 8 | public interface ASAPManagementStorage { 9 | CharSequence ASAP_CREATE_CHANNEL = "asap://createChannel"; 10 | 11 | void notifyChannelCreated(CharSequence appName, CharSequence channelUri, 12 | CharSequence uri, Collection storageRecipients) throws ASAPException, IOException; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPEnvironmentChangesListenerManagement.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public interface ASAPEnvironmentChangesListenerManagement { 4 | /** 5 | * Add listener for changes in ASAP environment 6 | * @param changesListener listener which is to be added 7 | */ 8 | void addASAPEnvironmentChangesListener(ASAPEnvironmentChangesListener changesListener); 9 | 10 | /** 11 | * Remove changes listener 12 | * @param changesListener listener which is to be removed 13 | */ 14 | void removeASAPEnvironmentChangesListener(ASAPEnvironmentChangesListener changesListener); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/management/ASAPManagementCreateASAPStorageMessage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager.management; 2 | 3 | import java.util.Set; 4 | 5 | public interface ASAPManagementCreateASAPStorageMessage { 6 | /** 7 | * @return list of recipients of this storage/channel 8 | */ 9 | Set getRecipients(); 10 | 11 | /** 12 | * @return channel uri 13 | */ 14 | CharSequence getChannelUri(); 15 | 16 | /** 17 | * 18 | * @return storage/channel owner 19 | */ 20 | CharSequence getOwner(); 21 | 22 | CharSequence getAppName(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/crypto/ASAPPoint2PointCryptoSettingsImpl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | public class ASAPPoint2PointCryptoSettingsImpl implements ASAPPoint2PointCryptoSettings { 4 | private final boolean encrypt; 5 | private final boolean sign; 6 | 7 | public ASAPPoint2PointCryptoSettingsImpl(boolean encrypt, boolean sign) { 8 | this.encrypt = encrypt; 9 | this.sign = sign; 10 | } 11 | @Override 12 | public boolean mustEncrypt() { 13 | return this.encrypt; 14 | } 15 | 16 | @Override 17 | public boolean mustSign() { 18 | return this.sign; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASAPJava 2 | Asynchronous Semantic Ad-hoc Protocol (ASAP) engine for Java 3 | 4 | # Maven Guide 5 | ## Compile and run tests 6 | ``` 7 | mvn clean package 8 | ``` 9 | This compiles all files, runs tests and, if they succeed, compiles the jar. The compiled jar (ASAPJava-{Version}.jar) will be in your target/ folder. 10 | 11 | ## Compile without tests 12 | This is needed in case you want to build the jar while your tests fail (which currently, they do fail). 13 | ``` 14 | mvn clean package -DskipTests 15 | ``` 16 | 17 | # Wiki 18 | Visit [the wiki](https://github.com/SharedKnowledge/ASAPJava/wiki) for all other information on what ASAP is and how to use it. 19 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPChannelContentChangedListenerManagement.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public interface ASAPChannelContentChangedListenerManagement { 4 | /** 5 | * Add listener for changes in ASAP environment 6 | * @param listener listener which is to be added 7 | */ 8 | void addASAPChannelContentChangedListener(CharSequence format, ASAPChannelContentChangedListener listener); 9 | 10 | /** 11 | * Remove changes listener 12 | * @param listener listener which is to be removed 13 | */ 14 | void removeASAPChannelContentChangedListener(CharSequence format, ASAPChannelContentChangedListener listener); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPMessageReceivedListenerManagement.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public interface ASAPMessageReceivedListenerManagement { 4 | /** 5 | * Add a listener that is called when a message came in 6 | * @param format app name == supported format 7 | * @param listener listener object 8 | */ 9 | void addASAPMessageReceivedListener(CharSequence format, ASAPMessageReceivedListener listener); 10 | void removeASAPMessageReceivedListener(CharSequence format, ASAPMessageReceivedListener listener); 11 | 12 | /** 13 | * @ return number of listeners 14 | * @return 15 | */ 16 | int getNumberListener(); 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/testsupport/StoreReceivedMessages.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.testsupport; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | import net.sharksystem.asap.ASAPMessageReceivedListener; 5 | import net.sharksystem.asap.ASAPMessages; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class StoreReceivedMessages implements ASAPMessageReceivedListener { 12 | public List messageList = new ArrayList<>(); 13 | @Override 14 | public void asapMessagesReceived(ASAPMessages messages, String senderE2E, List asapHops) throws IOException { 15 | this.messageList.add(messages); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPMessageCompare.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public interface ASAPMessageCompare { 4 | /** 5 | * Returns if message A is earlier than message B. This interface must be implemented 6 | * by an applications and is parameter to retrieve a messages iterator object. If set, this 7 | * comparision would define the order on which those messages are delivered. 8 | *

9 | * Note: It is always assumed that message sent / received in an earlier era are always earlier. 10 | * 11 | * @param messageA 12 | * @param messageB 13 | * @return 14 | */ 15 | boolean earlier(byte[] messageA, byte[] messageB); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPUndecryptableMessageHandler.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.crypto.ASAPCryptoAlgorithms; 4 | 5 | public interface ASAPUndecryptableMessageHandler { 6 | String FORMAT_UNDECRYPTABLE_MESSAGES = "asap/undecryptable"; 7 | String URI_UNDECRYPTABLE_MESSAGES = "asap://undecryptable"; 8 | /** 9 | * Peer can (and should) receive encrypted messages without being receiver. A peer is not able 10 | * to encrypt that message but could store and forward. That is what ASAP is about. 11 | */ 12 | void handleUndecryptableMessage(ASAPCryptoAlgorithms.EncryptedMessagePackage encryptedMessage, CharSequence receiver); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/StreamPair.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | public interface StreamPair { 8 | /** InputStream to read data from the other side */ 9 | InputStream getInputStream() throws IOException; 10 | /** OutputStream to send data to the other side */ 11 | OutputStream getOutputStream() throws IOException; 12 | /** close connection to peer */ 13 | void close(); 14 | 15 | void addListener(StreamPairListener listener); 16 | 17 | CharSequence getSessionID(); 18 | 19 | CharSequence getEndpointID(); 20 | 21 | void setEndpointID(CharSequence peerID); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPEncounterConnectionType.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public enum ASAPEncounterConnectionType { 4 | UNKNOWN ((byte) 0), 5 | AD_HOC_LAYER_2_NETWORK ((byte) 1), 6 | ASAP_HUB ((byte) 2), 7 | INTERNET ((byte) 3), 8 | ONION_NETWORK ((byte) 4); 9 | 10 | private final byte type; 11 | 12 | ASAPEncounterConnectionType(byte type) { 13 | this.type = type; 14 | } 15 | 16 | public String toString() { 17 | switch(this.type) { 18 | case 0: return "unknown"; 19 | case 1: return "ad-hoc"; 20 | case 2: return "hub"; 21 | case 3: return "internet"; 22 | case 4: return "onion"; 23 | default: return " listenerList = new ArrayList<>(); 8 | 9 | public void addListener(StreamPairListener listener) { 10 | this.listenerList.add(listener); 11 | } 12 | 13 | protected void notifyAllListenerClosed(StreamPair closedStreamPair, String key) { 14 | for(StreamPairListener listener : this.listenerList) { 15 | (new Thread(new Runnable() { 16 | @Override 17 | public void run() { 18 | listener.notifyClosed(closedStreamPair, key); 19 | } 20 | })).start(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/CommandlineTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem; 2 | 3 | import net.sharksystem.utils.Commandline; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.HashMap; 8 | 9 | public class CommandlineTests { 10 | @Test 11 | public void test1() { 12 | String key0 = "-help"; 13 | String key1 = "-o"; 14 | String value1 = "xyz"; 15 | String[] args = new String[] {key0, key1, value1}; 16 | HashMap argsMap = Commandline.parametersToMap(args, false, "helpmessage"); 17 | 18 | Assert.assertEquals(2, argsMap.size()); 19 | 20 | // 0 21 | Assert.assertNull(argsMap.get(key0)); 22 | // 1 23 | String valueInMap = argsMap.get(key1); 24 | Assert.assertTrue(valueInMap.equalsIgnoreCase(value1)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPPeerService.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.asap.engine.ASAPChunkAssimilatedListener; 4 | 5 | public interface ASAPPeerService extends ASAPPeer, ASAPConnectionHandler { 6 | // long DEFAULT_MAX_PROCESSING_TIME = 30000; // 30 seconds 7 | long DEFAULT_MAX_PROCESSING_TIME = Long.MAX_VALUE; // eternity - debugging setting 8 | 9 | /** 10 | * Overwrite internal listener. This method is used e.g. in Androiud on service side. The asap peer is informed 11 | * about newly received chunks. That news is broadcasted to application side which performs the actual processing. 12 | * 13 | * @param listener this listener is called instead - no further chunk processing is made by this object. 14 | */ 15 | void overwriteChuckReceivedListener(ASAPChunkAssimilatedListener listener); 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/crypto/PersistInMemoKeystore.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.ASAPSecurityException; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | 9 | import static net.sharksystem.utils.testsupport.TestConstants.ALICE_ID; 10 | 11 | public class PersistInMemoKeystore { 12 | /** 13 | * It is not really a good idea to implement a serialization like this. It is extremely 14 | * vulnerable... But is makes testing a bit easier. 15 | */ 16 | @Test 17 | public void writeAndRestore() throws ASAPException, IOException { 18 | InMemoASAPKeyStore imKeystore = new InMemoASAPKeyStore("TestPeer"); 19 | imKeystore.generateKeyPair(); 20 | 21 | byte[] memento = imKeystore.getMemento(); 22 | imKeystore.restoreMemento(memento); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/PeerIDHelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import net.sharksystem.asap.ASAP; 4 | 5 | public class PeerIDHelper { 6 | public static boolean sameID(CharSequence idA, CharSequence idB) { 7 | if(idA.length() != idB.length()) return false; 8 | 9 | // same length - I take A 10 | for(int i = 0; i < idA.length(); i++) { 11 | if(idA.charAt(i) != idB.charAt(i)) return false; 12 | } 13 | 14 | // no difference 15 | return true; 16 | } 17 | 18 | public static boolean sameFormat(CharSequence formatA, CharSequence formatB) { 19 | return PeerIDHelper.sameID(formatA, formatB); 20 | } 21 | 22 | public static boolean sameUri(CharSequence uriA, CharSequence uriB) { 23 | return PeerIDHelper.sameID(uriA, uriB); 24 | } 25 | 26 | public static String createUniqueID() { 27 | return ASAP.createUniqueID(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/MessageIter.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import java.util.NoSuchElementException; 7 | 8 | public class MessageIter implements Iterator { 9 | private final List byteMessages; 10 | private int nextIndex; 11 | private String nextString; 12 | 13 | 14 | public MessageIter(List byteMessages) throws FileNotFoundException { 15 | this.byteMessages = byteMessages; 16 | this.nextIndex = 0; 17 | } 18 | 19 | @Override 20 | public boolean hasNext() { 21 | return this.byteMessages.size() > nextIndex; 22 | } 23 | 24 | @Override 25 | public String next() { 26 | if (!this.hasNext()) { 27 | throw new NoSuchElementException("no more messages"); 28 | } 29 | 30 | return new String(this.byteMessages.get(nextIndex++)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/testsupport/TestConstants.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.testsupport; 2 | 3 | public interface TestConstants { 4 | String ROOT_DIRECTORY = "testResultsRootFolder/"; 5 | String ALICE_ID = "Alice_42"; 6 | String ALICE_NAME = "Alice"; 7 | String BOB_ID = "Bob_43"; 8 | String BOB_NAME = "Bob"; 9 | String CLARA_ID = "Clara_44"; 10 | String CLARA_NAME = "Clara"; 11 | String DAVID_ID = "David_45"; 12 | String DAVID_NAME = "David"; 13 | 14 | String TEST_APP_FORMAT = "application/sn2"; 15 | String URI = "shark://testUri"; 16 | byte[] MESSAGE_1 = "1st message".getBytes(); 17 | byte[] MESSAGE_2 = "2nd message".getBytes(); 18 | 19 | byte[] MESSAGE_ALICE_TO_BOB_1 = "Alice -> Bob (#1)".getBytes(); 20 | byte[] MESSAGE_BOB_TO_ALICE_1 = "Bob -> Alice (#1)".getBytes(); 21 | byte[] MESSAGE_ALICE_TO_BOB_2 = "Alice -> Bob (#1)".getBytes(); 22 | byte[] MESSAGE_BOB_TO_ALICE_2 = "Bob -> Alice (#1)".getBytes(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPOnlineMessageSender.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | 5 | import java.io.IOException; 6 | import java.util.Set; 7 | 8 | public interface ASAPOnlineMessageSender { 9 | void sendASAPAssimilateMessage(CharSequence format, CharSequence urlTarget, Set recipients, 10 | byte[] messageAsBytes, int era) throws IOException, ASAPException; 11 | 12 | void sendASAPAssimilateMessage(CharSequence format, CharSequence urlTarget, CharSequence recipient, 13 | byte[] messageAsBytes, int era) throws IOException, ASAPException; 14 | 15 | void sendASAPAssimilateMessage(CharSequence format, CharSequence urlTarget, byte[] messageAsBytes, int era) 16 | throws IOException, ASAPException; 17 | 18 | void sendASAPAssimilateMessage(CharSequence format, CharSequence urlTarget, byte[] messageAsBytes) 19 | throws IOException, ASAPException; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAPEncounterList.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | 5 | import java.io.IOException; 6 | import java.util.Set; 7 | 8 | public interface ASAPEncounterList { 9 | /** 10 | * Provides a set of peer id. There is record of a previous encounter with those peers. 11 | * @return 12 | */ 13 | Set getEncounteredPeers(); 14 | 15 | /** 16 | * Provide era of last encounter with a peer. The local era, the counting of this peer is returned. 17 | * @param peerID 18 | * @return 19 | * @throws ASAPException There is not such peerID 20 | */ 21 | int getLocalMostRecentEra(CharSequence peerID) throws ASAPException; 22 | 23 | /** 24 | * Provide the most era from a most recent chunk received by this peer. 25 | * @param peerID 26 | * @return 27 | * @throws ASAPException There is not such peerID 28 | */ 29 | int getTheirMostRecentEra(CharSequence peerID) throws ASAPException, IOException; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/ASAPMessages2StringCollectionWrapper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import net.sharksystem.asap.ASAPMessages; 4 | 5 | import java.io.IOException; 6 | import java.util.Collection; 7 | import java.util.Iterator; 8 | 9 | /** 10 | * Assumed ASAPMessages only contain a string - instantiate an object of this class 11 | * with ASAPMessages and get a String iterator 12 | */ 13 | public class ASAPMessages2StringCollectionWrapper implements Iterator { 14 | private final ASAPMessages asapMessages; 15 | private final Iterator byteContentInterator; 16 | 17 | public ASAPMessages2StringCollectionWrapper(ASAPMessages asapMessages) throws IOException { 18 | this.asapMessages = asapMessages; 19 | this.byteContentInterator = this.asapMessages.getMessages(); 20 | } 21 | 22 | @Override 23 | public boolean hasNext() { 24 | return this.byteContentInterator.hasNext(); 25 | } 26 | 27 | @Override 28 | public String next() { 29 | return new String(this.byteContentInterator.next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/crypto/ASAPKeyStore.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.ASAPSecurityException; 5 | import net.sharksystem.fs.ExtraData; 6 | 7 | import java.io.IOException; 8 | import java.security.PublicKey; 9 | 10 | /** 11 | * Basic requirement to encrypt / decrypt and sign/verify messages. 12 | */ 13 | public interface ASAPKeyStore extends ASAPCryptoParameterStorage { 14 | /** 15 | * @param subjectID 16 | * @return public key of recipient - to encrypt 17 | * @throws ASAPSecurityException if key cannot be found 18 | */ 19 | PublicKey getPublicKey(CharSequence subjectID) throws ASAPSecurityException; 20 | 21 | /** 22 | * 23 | * @param peerID 24 | * @return true if peerID is owners' id. 25 | */ 26 | boolean isOwner(CharSequence peerID); 27 | 28 | CharSequence getOwner(); 29 | 30 | /** 31 | * A new key pair is created 32 | */ 33 | void generateKeyPair() throws ASAPSecurityException; 34 | 35 | void setMementoTarget(ExtraData extraData); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/ASAPPeerHandleConnectionThread.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | import net.sharksystem.asap.ASAPConnectionHandler; 8 | import net.sharksystem.asap.ASAPException; 9 | 10 | /** 11 | * 12 | * @author thsc 13 | */ 14 | public class ASAPPeerHandleConnectionThread extends Thread { 15 | private final ASAPConnectionHandler engine; 16 | private final InputStream is; 17 | private final OutputStream os; 18 | 19 | public ASAPPeerHandleConnectionThread(ASAPConnectionHandler engine, InputStream is, OutputStream os) { 20 | this.engine = engine; 21 | this.is = is; 22 | this.os = os; 23 | } 24 | 25 | @Override 26 | public void run() { 27 | try { 28 | this.engine.handleConnection(this.is, this.os); 29 | } catch (IOException | ASAPException e) { 30 | System.err.println(this.getClass().getSimpleName() + ": " + e.getClass().getSimpleName() 31 | + "caught: " + e.getLocalizedMessage()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/testsupport/ASAPMessageReceivedStorage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.testsupport; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | import net.sharksystem.asap.ASAPMessageReceivedListener; 5 | import net.sharksystem.asap.ASAPMessages; 6 | import net.sharksystem.utils.Log; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | 13 | public class ASAPMessageReceivedStorage implements ASAPMessageReceivedListener { 14 | public List messageStorage = new ArrayList<>(); 15 | @Override 16 | public void asapMessagesReceived(ASAPMessages messages, String senderE2E, List asapHops) throws IOException { 17 | Log.writeLog(this, "messages received"); 18 | if(messages != null) { 19 | Iterator messagesIter = messages.getMessages(); 20 | while(messagesIter.hasNext()) { 21 | this.messageStorage.add(messagesIter.next()); 22 | } 23 | } 24 | } 25 | 26 | public int getNumberReceivedMessages() { 27 | return this.messageStorage.size(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/CountsReceivedMessagesListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | import net.sharksystem.asap.ASAPMessageReceivedListener; 5 | import net.sharksystem.asap.ASAPMessages; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | public class CountsReceivedMessagesListener implements ASAPMessageReceivedListener { 11 | private final String peerName; 12 | public int numberOfMessages = 0; 13 | 14 | public CountsReceivedMessagesListener(String peerName) { 15 | this.peerName = peerName; 16 | } 17 | 18 | CountsReceivedMessagesListener() { 19 | this(null); 20 | } 21 | 22 | @Override 23 | public void asapMessagesReceived(ASAPMessages messages, 24 | String senderE2E, // E2E part 25 | List asapHopsList) throws IOException { 26 | CharSequence format = messages.getFormat(); 27 | CharSequence uri = messages.getURI(); 28 | if (peerName != null) { 29 | System.out.print(peerName); 30 | } 31 | 32 | this.numberOfMessages++; 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/DateTimeHelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | public class DateTimeHelper { 8 | public static final long TIME_NOT_SET = -1; 9 | 10 | private static String guardNull(long longtime) { 11 | if(longtime == TIME_NOT_SET) return "not set (yet)"; 12 | else return null; 13 | } 14 | 15 | public static String long2DateString(long longtime) { 16 | String ret = guardNull(longtime); 17 | if(ret != null) return ret; 18 | 19 | Date date = new Date(longtime); 20 | return DateFormat.getInstance().format(date); 21 | } 22 | 23 | public static String long2ExactTimeString(long longtime) { 24 | String ret = guardNull(longtime); 25 | if(ret != null) return ret; 26 | 27 | Date date = new Date(longtime); 28 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 29 | 30 | return df.format(date); 31 | } 32 | 33 | public static String nowAsStringMostSpecific() { 34 | return long2ExactTimeString(System.currentTimeMillis()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPAbstractOnlineMessageSender.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | 5 | import java.io.IOException; 6 | import java.util.*; 7 | 8 | public abstract class ASAPAbstractOnlineMessageSender implements ASAPOnlineMessageSender { 9 | private ASAPInternalStorage source = null; 10 | 11 | public void attachToSource(ASAPInternalStorage source) { 12 | source.attachASAPMessageAddListener(this); 13 | } 14 | 15 | public void detachFromStorage() { 16 | if(this.source != null) { 17 | this.source.detachASAPMessageAddListener(); 18 | } 19 | } 20 | 21 | public void sendASAPAssimilateMessage(CharSequence format, CharSequence uri, CharSequence recipient, 22 | byte[] messageAsBytes, int era) throws IOException, ASAPException { 23 | if(recipient == null) { 24 | this.sendASAPAssimilateMessage(format, uri, messageAsBytes, era); 25 | } else { 26 | Set recipients = new HashSet<>(); 27 | recipients.add(recipient); 28 | this.sendASAPAssimilateMessage(format, uri, recipients, messageAsBytes, era); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/fs/ExtraData.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.fs; 2 | 3 | import net.sharksystem.SharkException; 4 | 5 | import java.io.IOException; 6 | import java.util.Set; 7 | 8 | public interface ExtraData { 9 | // remember a parameter with a key 10 | void putExtra(CharSequence key, byte[] value) throws IOException, SharkException; 11 | 12 | void putExtra(CharSequence key, Integer value) throws IOException, SharkException; 13 | 14 | void putExtra(CharSequence key, CharSequence value) throws IOException, SharkException; 15 | 16 | void putExtra(CharSequence key, Set value) throws IOException, SharkException; 17 | 18 | /** get a parameter by a key 19 | * 20 | * @param key 21 | * @return 22 | * @throws IOException 23 | * @throws SharkException if no such key exists 24 | */ 25 | byte[] getExtra(CharSequence key) throws IOException, SharkException; 26 | 27 | int getExtraInteger(CharSequence key) throws IOException, SharkException; 28 | 29 | Set getExtraCharSequenceSetParameter(CharSequence key) throws IOException, SharkException; 30 | 31 | String getExtraString(CharSequence key) throws IOException, SharkException; 32 | // remove all data 33 | void removeAll() throws IOException, SharkException; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/management/ASAPManagementStorageImpl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager.management; 2 | 3 | import net.sharksystem.asap.engine.ASAPEngine; 4 | import net.sharksystem.asap.ASAPException; 5 | 6 | import java.io.IOException; 7 | import java.util.Collection; 8 | 9 | public class ASAPManagementStorageImpl implements ASAPManagementStorage { 10 | private final ASAPEngine asapEngine; 11 | 12 | public ASAPManagementStorageImpl(ASAPEngine asapEngine) { 13 | this.asapEngine = asapEngine; 14 | } 15 | 16 | @Override 17 | public void notifyChannelCreated(CharSequence appName, CharSequence channelUri, CharSequence uri, 18 | Collection recipients) throws ASAPException, IOException { 19 | 20 | byte[] createClosedASAPChannelMessage = ASAPManagementMessage.getCreateClosedASAPChannelMessage( 21 | this.asapEngine.getOwner(), 22 | appName, 23 | uri, 24 | recipients); 25 | 26 | // put into a channel 27 | CharSequence newUri = ASAPManagementMessageHandler.createUniqueUri(); 28 | this.asapEngine.add(newUri, createClosedASAPChannelMessage); 29 | this.asapEngine.setRecipients(newUri, recipients); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/V1TestSuite.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem; 2 | 3 | import howto.ConnectPeers; 4 | import net.sharksystem.asap.crypto.CryptoUsage; 5 | import net.sharksystem.asap.engine.*; 6 | import net.sharksystem.asap.helper.HelperTester; 7 | import net.sharksystem.asap.peer.Point2Point2Test2; 8 | import net.sharksystem.asap.peer.TransientMessages; 9 | import net.sharksystem.asap.protocol.PDUTests; 10 | import net.sharksystem.asap.serialization.SerializationTests; 11 | import net.sharksystem.asap.storage.StorageTests; 12 | import org.junit.runner.RunWith; 13 | import org.junit.runners.Suite; 14 | 15 | @RunWith(Suite.class) 16 | @Suite.SuiteClasses({ 17 | SerializationTests.class, 18 | BasisMethodsTests.class, 19 | UsageExamples.class, 20 | CreateNewChannelFromOutsideTest.class, 21 | PDUTests.class, 22 | CryptoTests.class, 23 | StorageTests.class, 24 | LongerMessages.class, 25 | CryptoUsage.class, 26 | HelperTester.class, 27 | MultihopTests.class, 28 | CommandlineTests.class, 29 | TransientMessages.class, 30 | Point2Point2Test2.class, 31 | ConnectPeers.class, // 32 | //MultipleEncounterTests.class 33 | //E2EStreamPairLinkTestVersion2.class TODO 34 | 35 | }) 36 | public class V1TestSuite { 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/AlarmClock.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils; 2 | 3 | public class AlarmClock extends Thread { 4 | public static final int DEFAULT_KEY = 42; 5 | private final long duration; 6 | private final int key; 7 | private final AlarmClockListener listener; 8 | private Thread thread; 9 | private boolean killed = false; 10 | 11 | public AlarmClock(long duration, int key, AlarmClockListener listener) { 12 | this.duration = duration; 13 | this.key = key; 14 | this.listener = listener; 15 | } 16 | 17 | public AlarmClock(long duration, AlarmClockListener listener) { 18 | this(duration, DEFAULT_KEY, listener); 19 | } 20 | 21 | public void kill() { 22 | this.killed = true; 23 | } 24 | 25 | public void run() { 26 | try { 27 | // killed before started ? 28 | if(!this.killed) { 29 | Thread.sleep(duration); 30 | } 31 | 32 | // killed during sleep? 33 | if(!this.killed) { 34 | this.listener.alarmClockRinging(this.key); 35 | } 36 | } catch (InterruptedException e) { 37 | // interrupted - nothing to do 38 | } 39 | } 40 | 41 | public String toString() { 42 | return "AlarmClock : " + this.key; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPHop.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | /** 4 | * Describes the exchange if an ASAP message from one peer to the other 5 | */ 6 | public interface ASAPHop { 7 | /** 8 | * Sender is always a point-to-point sender. There is no get receiver method, though. Hops are kept in a chain. 9 | * Receiver of the last entry is local peer itself. For all hops in between: Sender of next hop is receiver of 10 | * the previous hop. 11 | * @return 12 | */ 13 | CharSequence sender(); 14 | 15 | /** 16 | * An ASAP message exchange is based on a point-to-point connection. There are different options: Ad-hoc networks, 17 | * Internet, onion networks etc. This method describes the connection type of this hop. 18 | * @return 19 | */ 20 | ASAPEncounterConnectionType getConnectionType(); 21 | 22 | /** 23 | * A sender could have signed the point-to-point message transfer. This message returns true if the receiver was 24 | * able to verify the signature. A false could be indicator for a forged identity or that the receiver has not got 25 | * sender's public key. 26 | * @return 27 | */ 28 | boolean verified(); 29 | 30 | /** 31 | * This point-to-point connection was encrypted. 32 | * @return 33 | */ 34 | boolean encrypted(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPChunkStorage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.asap.engine.ASAPInternalChunk; 4 | 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | /** 9 | * There is a chunk storage for each format. It offers methods 10 | * to get (and create) and remove (drop) chunks. 11 | * 12 | * Be careful. In most cases, there will be an ASAP protocol engine that writes data 13 | * in chunks and reads and transmits messages to encountered peers. 14 | * 15 | * @author thsc 16 | */ 17 | public interface ASAPChunkStorage { 18 | String getFormat(); 19 | 20 | ASAPInternalChunk getChunk(CharSequence uri, int era) throws IOException; 21 | 22 | boolean existsChunk(CharSequence uri, int era) throws IOException; 23 | 24 | List getChunks(int era) throws IOException; 25 | 26 | void dropChunks(int era) throws IOException; 27 | 28 | /** 29 | * 30 | * @param uri chunk storage uri 31 | * @param toEra newest era 32 | * @return a chunk cache which hides details of era 33 | * @throws IOException 34 | */ 35 | ASAPMessages getASAPMessages(CharSequence uri, int toEra) throws IOException; 36 | 37 | ASAPMessages getASAPMessages(CharSequence uri, int fromEra, int toEra) throws IOException; 38 | 39 | ASAPMessages getASAPMessages(String uri) throws ASAPException, IOException; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/ASAPEnvironmentChangesListenerManager.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager; 2 | 3 | import net.sharksystem.asap.ASAPEnvironmentChangesListener; 4 | import net.sharksystem.asap.ASAPEnvironmentChangesListenerManagement; 5 | import net.sharksystem.utils.Log; 6 | 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | public class ASAPEnvironmentChangesListenerManager 11 | extends GenericListenerImplementation 12 | implements ASAPEnvironmentChangesListenerManagement { 13 | 14 | @Override 15 | public void addASAPEnvironmentChangesListener(ASAPEnvironmentChangesListener changesListener) { 16 | this.addListener(changesListener); 17 | } 18 | 19 | @Override 20 | public void removeASAPEnvironmentChangesListener(ASAPEnvironmentChangesListener changesListener) { 21 | this.removeListener(changesListener); 22 | } 23 | 24 | public void notifyListeners(Set peerList) { 25 | if(peerList == null) peerList = new HashSet<>(); 26 | 27 | if(this.listenerList == null || this.listenerList.isEmpty()) return; 28 | 29 | // make a copy of that list 30 | for(ASAPEnvironmentChangesListener listener : this.listenerList) { 31 | if(listener != null) listener.onlinePeersChanged(new HashSet<>(peerList)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/testhelper/ASAPTesthelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.testhelper; 2 | 3 | import net.sharksystem.utils.Log; 4 | import net.sharksystem.utils.testsupport.TestHelper; 5 | 6 | public class ASAPTesthelper { 7 | public static final String ALICE_ID = "Alice_ID"; 8 | public static final String ALICE_NAME = "Alice"; 9 | public static final String BOB_ID = "Bob_ID"; 10 | public static final String BOB_NAME = "Bob"; 11 | public static final String CLARA_ID = "Clara_ID"; 12 | public static final String CLARA_NAME = "Clara"; 13 | public static final String DAVID_ID = "David_ID"; 14 | public static final String DAVID_NAME = "David"; 15 | 16 | public static final String ROOT_DIRECTORY_TESTS = "playground/"; 17 | 18 | public static int testNumber = 0; 19 | 20 | /** 21 | * Create a fresh testNumber 22 | */ 23 | public static void incrementTestNumber() { 24 | testNumber++; 25 | } 26 | 27 | private static int portnumber = 7000; 28 | /** 29 | * Create a fresh TCP port. 30 | * @return 31 | */ 32 | public static int getPortNumber() { 33 | portnumber++; 34 | return portnumber; 35 | } 36 | 37 | public static final String getUniqueFolderName(String prefix) { 38 | Log.writeLog(TestHelper.class, "test number == " + TestHelper.testNumber); 39 | return prefix + "_" + ASAPTesthelper.testNumber; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPUtils.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.utils.Log; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | 8 | public class ASAPUtils { 9 | /** 10 | * 11 | * @param searchSpace list of possible eras 12 | * @param fromEra lowest era 13 | * @param toEra highest era 14 | * @return list of era which are within from and to and also in search space 15 | */ 16 | public static Collection getErasInRange(Collection searchSpace, 17 | int fromEra, int toEra) { 18 | 19 | Collection eras = new ArrayList<>(); 20 | 21 | // the only trick is to be aware of the cyclic nature of era numbers 22 | boolean wrapped = fromEra > toEra; // it reached the era end and started new 23 | 24 | for(Integer era : searchSpace) { 25 | if(!wrapped) { 26 | //INIT ---- from-> +++++++++++++ <-to ----- MAX (+ fits) 27 | if(era >= fromEra && era <= toEra) eras.add(era); 28 | } else { 29 | // INIT+++++++++<-to ------ from->++++++MAX 30 | if(era <= toEra && era >= ASAP.INITIAL_ERA 31 | || era >= fromEra && era <= ASAP.MAX_ERA 32 | ) eras.add(era); 33 | } 34 | } 35 | 36 | return eras; 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/Commandline.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils; 2 | 3 | import java.util.HashMap; 4 | 5 | public class Commandline { 6 | public static HashMap parametersToMap(String args[], boolean valueRequired, String helpMessage) { 7 | if(valueRequired && args.length % 2 != 0) { 8 | System.err.println("malformed parameter list: each parameter needs a value. "); 9 | System.err.println(helpMessage); 10 | return null; 11 | } 12 | 13 | HashMap argumentMap = new HashMap<>(); 14 | 15 | int i = 0; 16 | while(i < args.length) { 17 | // key is followed by value. Key starts with - 18 | if(!args[i].startsWith("-")) { 19 | /* found parameter that does not start with '-' 20 | maybe shell parameters. Leave it alone. We are done here 21 | */ 22 | return argumentMap; 23 | } 24 | 25 | // value can be empty 26 | if(args.length > i+1 && !args[i+1].startsWith("-")) { 27 | // it is a value 28 | argumentMap.put(args[i], args[i+1]); 29 | i += 2; 30 | } else { 31 | // no value - next parameter 32 | argumentMap.put(args[i], null); 33 | i += 1; 34 | } 35 | } 36 | 37 | return argumentMap; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/mockAndTemplates/ASAPMessageReceivedListenerExample.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.mockAndTemplates; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | import net.sharksystem.asap.ASAPMessages; 5 | import net.sharksystem.asap.ASAPMessageReceivedListener; 6 | 7 | import java.io.IOException; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | public class ASAPMessageReceivedListenerExample implements ASAPMessageReceivedListener { 12 | private final String peerName; 13 | 14 | ASAPMessageReceivedListenerExample(String peerName) { 15 | this.peerName = peerName; 16 | } 17 | 18 | public ASAPMessageReceivedListenerExample() { 19 | this(null); 20 | } 21 | 22 | @Override 23 | public void asapMessagesReceived(ASAPMessages messages, 24 | String senderE2E, // E2E part 25 | List asapHop) throws IOException { 26 | 27 | CharSequence format = messages.getFormat(); 28 | CharSequence uri = messages.getURI(); 29 | if (peerName != null) { 30 | System.out.print(peerName); 31 | } 32 | 33 | System.out.println("asap message received (" + format + " | " + uri + "). size == " + messages.size()); 34 | Iterator yourPDUIter = messages.getMessages(); 35 | while (yourPDUIter.hasNext()) { 36 | TestUtils.deserializeExample(yourPDUIter.next()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/GenericListenerImplementation.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager; 2 | 3 | import net.sharksystem.utils.Log; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class GenericListenerImplementation { 9 | protected List listenerList = new ArrayList(); 10 | 11 | protected void addListener(L listener) { 12 | if(!this.listenerList.contains(listener)) { 13 | this.listenerList.add(listener); 14 | } 15 | } 16 | 17 | protected void removeListener(L listener) { 18 | this.listenerList.remove(listener); 19 | } 20 | 21 | public void removeAllListeners() { 22 | this.listenerList = new ArrayList(); 23 | } 24 | 25 | public void notifyAll(GenericNotifier notifier, boolean useThreads) { 26 | for(L listener : this.listenerList) { 27 | if(useThreads) { 28 | new Thread (new Runnable() { 29 | @Override 30 | public void run() { notifier.doNotify(listener); } 31 | }).start(); 32 | } else { // no threads 33 | notifier.doNotify(listener); 34 | } 35 | } 36 | } 37 | 38 | protected void log(String msg) { 39 | StringBuilder sb = new StringBuilder(); 40 | sb.append(Log.startLog(this)); 41 | sb.append(": "); 42 | sb.append(msg); 43 | 44 | Log.writeLog(this, sb.toString()); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/StreamPairLink.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | import net.sharksystem.utils.Log; 4 | 5 | import java.io.IOException; 6 | 7 | public class StreamPairLink implements StreamPairListener { 8 | private StreamLink streamLinkA2B; 9 | private StreamLink streamLinkB2A; 10 | 11 | public StreamPairLink(StreamPair pairA, CharSequence idA, StreamPair pairB, CharSequence idB) throws IOException { 12 | this(pairA, idA, pairB, idB, true); 13 | } 14 | 15 | public StreamPairLink( 16 | StreamPair pairA, CharSequence idA, 17 | StreamPair pairB, CharSequence idB, 18 | boolean autostart) throws IOException { 19 | String tagA2B = idB + " ==> " + idA; 20 | this.streamLinkA2B = new StreamLink(pairA.getInputStream(), pairB.getOutputStream(), true, tagA2B); 21 | String tagB2A = idA + " ==> " + idB; 22 | this.streamLinkB2A = new StreamLink(pairB.getInputStream(), pairA.getOutputStream(), true, tagB2A); 23 | 24 | // listen to close 25 | pairA.addListener(this); 26 | pairB.addListener(this); 27 | 28 | if(autostart) this.start(); 29 | } 30 | 31 | @Override 32 | public void notifyClosed(StreamPair closedStreamPair, String key) { 33 | Log.writeLog(this, "stream pair closed: " + key); 34 | this.streamLinkA2B.close(); 35 | this.streamLinkB2A.close(); 36 | } 37 | 38 | public void start() { 39 | this.streamLinkA2B.start(); 40 | this.streamLinkB2A.start(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPChunkAssimilatedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | import net.sharksystem.asap.ASAPMessages; 5 | 6 | import java.io.IOException; 7 | import java.util.List; 8 | 9 | /** 10 | * 11 | * @author thsc 12 | */ 13 | public interface ASAPChunkAssimilatedListener { 14 | /** 15 | * Announce incoming ASAP messages. Note the important difference of both senders. ASAP is a routing protocol. 16 | * An ASAP message has got an original sender (the end-to-end (E2E) sender). Other peers can route this message. 17 | * The final (senderPoint2Point) is not necessarily the E2E sender but the peer on the other side of the channel. 18 | *

19 | * E2E sender and direct sender are always the same when disengaging routing. 20 | * It is recommended to allow routing, though. 21 | * 22 | * @param format application format 23 | * @param senderE2E original sender peer - the E2E sender - not necessarily the sender on the other side of 24 | * the channel. 25 | * @param uri message uri 26 | * @param era era of the original sender (the end-to-end sender). not 27 | * @param asapHop describes the point-to-point message exchhange in more details 28 | * @see ASAPHop 29 | * 30 | */ 31 | void chunkStored(String format, String senderE2E, String uri, int era, // E2E part 32 | List asapHop) throws IOException; 33 | 34 | void transientMessagesReceived(ASAPMessages transientMessages, ASAPHop asapHop) throws IOException; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPChannel.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Set; 6 | 7 | public interface ASAPChannel { 8 | CharSequence getOwner() throws IOException; 9 | CharSequence getUri() throws IOException; 10 | Set getRecipients() throws IOException; 11 | HashMap getExtraData() throws IOException; 12 | void putExtraData(String key, String value) throws IOException; 13 | void removeExtraData(String key) throws IOException; 14 | 15 | /** 16 | * Get all message in this channel, including received messages. Same as getMessages(true); 17 | * @return 18 | * @throws IOException 19 | */ 20 | ASAPMessages getMessages() throws IOException; 21 | 22 | /** 23 | * Get messages in this channel only (most probably sent) from this peer or also received messages 24 | * @param sentMessagesOnly 25 | * @return 26 | * @throws IOException 27 | */ 28 | ASAPMessages getMessages(boolean sentMessagesOnly) throws IOException, ASAPException; 29 | 30 | /** 31 | * Get all message (including received messages). Compare Object helps to bring messages in order. 32 | * 33 | * @param compare 34 | * @return 35 | * @throws IOException 36 | */ 37 | ASAPMessages getMessages(ASAPMessageCompare compare) throws IOException, ASAPException; 38 | 39 | /** 40 | * Add a message to this channel - in other words: broadcast a message into this channel. 41 | * @param message 42 | * @throws IOException 43 | */ 44 | void addMessage(byte[] message) throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/mockAndTemplates/TestUtils.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.mockAndTemplates; 2 | 3 | import java.io.*; 4 | 5 | public class TestUtils { 6 | static final CharSequence ALICE = "Alice"; 7 | static final CharSequence BOB = "Bob"; 8 | static final CharSequence YOUR_APP_NAME = "yourAppName"; 9 | static final CharSequence YOUR_URI = "yourSchema://example"; 10 | 11 | /** 12 | * a serialization example 13 | * @param exampleLong 14 | * @param exampleString 15 | * @param exampleBoolean 16 | * @return 17 | */ 18 | public static byte[] serializeExample(long exampleLong, String exampleString, boolean exampleBoolean) throws IOException { 19 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 20 | DataOutputStream daos = new DataOutputStream(baos); 21 | 22 | // serialize 23 | daos.writeLong(exampleLong); 24 | daos.writeUTF(exampleString); 25 | daos.writeBoolean(exampleBoolean); 26 | 27 | return baos.toByteArray(); 28 | } 29 | 30 | /** 31 | * a deserialization example 32 | */ 33 | public static void deserializeExample(byte[] serializedData) throws IOException { 34 | ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); 35 | DataInputStream dais = new DataInputStream(bais); 36 | 37 | // deserialize 38 | long exampleLong = dais.readLong(); 39 | String exampleString = dais.readUTF(); 40 | boolean exampleBoolean = dais.readBoolean(); 41 | 42 | // call a methode in your app 43 | 44 | // here: just print 45 | System.out.println("received: " + exampleLong + " | " + exampleString + " | " + exampleBoolean); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/fs/FSUtils.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.fs; 2 | 3 | import net.sharksystem.utils.Log; 4 | 5 | import java.io.File; 6 | 7 | public class FSUtils { 8 | //////////////////////////////////////////////////////////////////////////////////////// 9 | // helper // 10 | //////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | public static void removeFolder(String eraPathName) { 13 | File dir = new File(eraPathName); 14 | 15 | String[] dirEntries = dir.list(); 16 | 17 | if(dirEntries != null) { 18 | for(String fileName : dirEntries) { 19 | File fileInDir = new File(eraPathName + "/" + fileName); 20 | if(fileInDir.isDirectory()) { 21 | FSUtils.removeFolder(fileInDir.getAbsolutePath()); 22 | } else { 23 | try { 24 | if(!fileInDir.delete()) { 25 | Log.writeLog(FSUtils.class,"ASAPEngineFS: cannot delete file (try deleteOnExit):" 26 | + fileInDir); 27 | } 28 | } catch (RuntimeException e) { 29 | Log.writeLog(FSUtils.class, "cannot file:" + e.getLocalizedMessage()); 30 | // try next 31 | } 32 | } 33 | } 34 | } 35 | 36 | dir.delete(); 37 | //dir.deleteOnExit(); 38 | try { 39 | Thread.sleep(1); // give file system a moment 40 | } catch (InterruptedException e) { 41 | // nobody wants to know 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/crypto/ASAPCryptoParameterStorage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | import net.sharksystem.asap.ASAPSecurityException; 4 | 5 | import javax.crypto.SecretKey; 6 | import java.security.PrivateKey; 7 | import java.security.PublicKey; 8 | 9 | /** 10 | * Defines basic methods we need to implement signing and verification and to get information how to encrypt 11 | */ 12 | public interface ASAPCryptoParameterStorage { 13 | String DEFAULT_ASYMMETRIC_ENCRYPTION_ALGORITHM = "RSA/ECB/PKCS1Padding"; 14 | String DEFAULT_SYMMETRIC_KEY_TYPE = "AES"; 15 | String DEFAULT_SYMMETRIC_ENCRYPTION_ALGORITHM = "AES/ECB/PKCS5Padding"; 16 | // public static int DEFAULT_AES_KEY_SIZE = 256; 17 | int DEFAULT_SYMMETRIC_KEY_SIZE = 128; // TODO we can do better 18 | String DEFAULT_ASYMMETRIC_SIGNATURE_ALGORITHM = "SHA256withRSA"; 19 | 20 | /** 21 | * 22 | * @return private key of local device - for signing 23 | * @throws ASAPSecurityException 24 | */ 25 | PrivateKey getPrivateKey() throws ASAPSecurityException; 26 | 27 | // debugging 28 | // PrivateKey getPrivateKey(CharSequence subjectID) throws ASAPSecurityException; 29 | 30 | /** 31 | * @return public key of local device - for signing 32 | * @throws ASAPSecurityException 33 | */ 34 | PublicKey getPublicKey() throws ASAPSecurityException; 35 | 36 | long getKeysCreationTime() throws ASAPSecurityException; 37 | 38 | String getAsymmetricEncryptionAlgorithm(); 39 | 40 | String getAsymmetricSigningAlgorithm(); 41 | 42 | SecretKey generateSymmetricKey() throws ASAPSecurityException; 43 | 44 | String getSymmetricEncryptionAlgorithm(); 45 | 46 | String getSymmetricKeyType(); 47 | 48 | int getSymmetricKeyLen(); 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/engine/ChunkPrinter.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.Map; 7 | 8 | import net.sharksystem.asap.ASAPException; 9 | import net.sharksystem.asap.engine.ASAPInternalChunkFS; 10 | import org.junit.Test; 11 | 12 | /** 13 | * 14 | * @author thsc 15 | */ 16 | public class ChunkPrinter { 17 | 18 | @Test 19 | public void printChunk() throws IOException, ASAPException { 20 | Map m = new HashMap<>(); 21 | m.put(null, "A"); 22 | 23 | m.remove(null); 24 | 25 | String chunkTrunkName = "bob/0/content%3A%2F%2FaliceAndBob.talk"; 26 | 27 | System.out.println("going to print content of chunk " + chunkTrunkName); 28 | 29 | try { 30 | ASAPInternalChunkFS chunk = new ASAPInternalChunkFS(null, chunkTrunkName); 31 | 32 | System.out.println("url: " + chunk.getUri()); 33 | 34 | Iterator messages = chunk.getMessagesAsCharSequence(); 35 | if(messages == null) { 36 | System.out.println("no message iterator"); 37 | return; 38 | } 39 | 40 | int i = 0; 41 | while(messages.hasNext()) { 42 | CharSequence s = messages.next(); 43 | StringBuilder b = new StringBuilder(); 44 | b.append(i); 45 | b.append(": "); 46 | b.append(s); 47 | System.out.println(b.toString()); 48 | } 49 | } 50 | catch(Throwable t) { 51 | System.out.println("failure"); 52 | t.printStackTrace(System.out); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/apps/TCPServerSocketAcceptor.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.apps; 2 | 3 | import net.sharksystem.asap.ASAPEncounterManager; 4 | import net.sharksystem.asap.ASAPEncounterConnectionType; 5 | import net.sharksystem.utils.Log; 6 | import net.sharksystem.utils.streams.StreamPair; 7 | import net.sharksystem.utils.tcp.SocketFactory; 8 | import net.sharksystem.utils.tcp.StreamPairCreatedListener; 9 | 10 | import java.io.IOException; 11 | 12 | public class TCPServerSocketAcceptor implements StreamPairCreatedListener { 13 | private final ASAPEncounterManager encounterManager; 14 | private final SocketFactory socketFactory; 15 | 16 | public TCPServerSocketAcceptor(int portNumber, ASAPEncounterManager encounterManager, boolean remainOpen) 17 | throws IOException { 18 | this.encounterManager = encounterManager; 19 | this.socketFactory = new SocketFactory(portNumber, this, remainOpen); 20 | 21 | Log.writeLog(this, "start socket factory - no race condition assumed"); 22 | new Thread(socketFactory).start(); 23 | } 24 | 25 | public TCPServerSocketAcceptor(int portNumber, ASAPEncounterManager encounterManager) throws IOException { 26 | this(portNumber, encounterManager, false); 27 | } 28 | 29 | public void close() throws IOException { 30 | this.socketFactory.close(); 31 | } 32 | 33 | @Override 34 | public void streamPairCreated(StreamPair streamPair) { 35 | Log.writeLog(this, "new stream pair created"); 36 | try { 37 | this.encounterManager.handleEncounter(streamPair, ASAPEncounterConnectionType.INTERNET); 38 | } catch (IOException e) { 39 | Log.writeLogErr(this, "exception when asking for new connection handling: " 40 | + e.getLocalizedMessage()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPMessageSender.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.io.IOException; 4 | 5 | public interface ASAPMessageSender { 6 | /** 7 | * Send a message 8 | * @param appName 9 | * @param uri 10 | * @param message 11 | * @throws ASAPException 12 | */ 13 | void sendASAPMessage(CharSequence appName, CharSequence uri, 14 | byte[] message) throws ASAPException; 15 | 16 | /** 17 | * When calling this methode, this asap message is sent over any existing connection. 18 | * It is not stored on sender or receiver side. Message listeners are called as usual. Nothing happens (no 19 | * exception is thrown) if there is not a single peer encounter running. 20 | * @param appName 21 | * @param uri 22 | * @param message 23 | * @throws ASAPException 24 | * @throws IOException 25 | */ 26 | void sendTransientASAPMessage(CharSequence appName, CharSequence uri, byte[] message) 27 | throws ASAPException, IOException; 28 | 29 | /** 30 | * When calling this methode, this asap message is sent over any existing connection. 31 | * It is not stored on sender or receiver side. Message listeners are called as usual. Nothing happens (no 32 | * exception is thrown) if there is not a single peer encounter running. 33 | * 34 | * @param nextHopPeerID Peer will try to send a message only to this peer. An ASAPException is thrown if there 35 | * is no running encounter with that peer in place. 36 | * @param appName 37 | * @param uri 38 | * @param message 39 | * @throws ASAPException 40 | * @throws IOException 41 | */ 42 | void sendTransientASAPMessage(CharSequence nextHopPeerID, CharSequence appName, CharSequence uri, byte[] message) 43 | throws ASAPException, IOException; 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/engine/BasisCryptoTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPSecurityException; 4 | import net.sharksystem.asap.crypto.ASAPCryptoAlgorithms; 5 | import net.sharksystem.asap.crypto.InMemoASAPKeyStore; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.security.spec.InvalidKeySpecException; 14 | 15 | public class BasisCryptoTests { 16 | public static final String ALICE_ID = "Alice"; 17 | public static final String BOB_ID = "Bob"; 18 | 19 | @Test 20 | public void publicKeyExportImport() throws ASAPSecurityException, IOException, InvalidKeySpecException, NoSuchAlgorithmException { 21 | InMemoASAPKeyStore aliceStorage = new InMemoASAPKeyStore(ALICE_ID); 22 | // a message 23 | String msg = "Hi Bob"; 24 | 25 | // convert to bytes 26 | byte[] msgBytes = msg.getBytes(); 27 | 28 | // sign a message - with Alice' private key 29 | byte[] signatureBytes = ASAPCryptoAlgorithms.sign(msgBytes, aliceStorage); 30 | 31 | // Alice could now send message with signature to bob - we are in a test, nothing to do here. 32 | 33 | // Bob need to know Alice' public key to verify - simulate transfer 34 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 35 | aliceStorage.writePublicKey(baos); 36 | 37 | InMemoASAPKeyStore bobStorage = new InMemoASAPKeyStore(BOB_ID); 38 | // store Alice' public key with Bob 39 | bobStorage.readPublicKey(ALICE_ID, new ByteArrayInputStream(baos.toByteArray())); 40 | 41 | Assert.assertTrue(ASAPCryptoAlgorithms.verify(msgBytes, signatureBytes, ALICE_ID, bobStorage)); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/ASAPLogHelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import net.sharksystem.utils.Log; 4 | import net.sharksystem.utils.Utils; 5 | import net.sharksystem.asap.ASAPChunkStorage; 6 | import net.sharksystem.asap.engine.ASAPEngine; 7 | import net.sharksystem.asap.engine.ASAPEngineFS; 8 | import net.sharksystem.asap.ASAPException; 9 | import net.sharksystem.asap.ASAPMessages; 10 | 11 | import java.io.*; 12 | 13 | public class ASAPLogHelper { 14 | public static final String SERIALIZATION_DELIMITER = "|||"; 15 | 16 | public static ASAPMessages getMessagesByChunkReceivedInfos(String format, String sender, String uri, 17 | String folderName, int era) { 18 | try { 19 | String rootIncomingStorage = folderName + "/" + Utils.url2FileName(format); 20 | Log.writeLog(ASAPLogHelper.class, "try getting storage in folder " + rootIncomingStorage); 21 | ASAPEngine existingASAPEngineFS = 22 | ASAPEngineFS.getExistingASAPEngineFS(rootIncomingStorage); 23 | Log.writeLog(ASAPLogHelper.class, "got existing asap engine"); 24 | 25 | ASAPChunkStorage chunkStorage = existingASAPEngineFS.getReceivedChunksStorage(sender); 26 | Log.writeLog(ASAPLogHelper.class, "got incoming channel of " + sender); 27 | 28 | ASAPMessages asapMessages = chunkStorage.getASAPMessages(uri, era, era); 29 | Log.writeLog(ASAPLogHelper.class, "got messages uri: " + uri + " / era: " + era); 30 | 31 | return asapMessages; 32 | } catch (IOException | ASAPException e) { 33 | Log.writeLog(ASAPLogHelper.class, "could not access message after be informed about new chunk arrival" 34 | + e.getLocalizedMessage()); 35 | } 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/testsupport/TestHelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.testsupport; 2 | 3 | import net.sharksystem.utils.Utils; 4 | import net.sharksystem.fs.FSUtils; 5 | 6 | public class TestHelper { 7 | public static int testNumber = 0; 8 | public static int portNumber = 4444; 9 | 10 | public static int getPortNumber() { 11 | portNumber++; 12 | //System.out.println(">>>>>>>>>>>>>>>> portnumber: " + portNumber); 13 | return portNumber; 14 | } 15 | 16 | public static String getFullTempFolderName(String fullRootFolderName, boolean increment) { 17 | // String retVal = fullRootFolderName + "_" + testNumber; 18 | String retVal = TestConstants.ROOT_DIRECTORY + "/" + fullRootFolderName + "/test_" + testNumber; 19 | if(increment) testNumber++; 20 | return retVal; 21 | } 22 | 23 | public static String getFullRootFolderName(String peerID, Class testClass) { 24 | return testClass.getSimpleName() 25 | + "/" 26 | + peerID; 27 | } 28 | 29 | public static void incrementTestNumber() { 30 | testNumber++; 31 | } 32 | 33 | public static String produceTestAppName(Class testClass) { 34 | return "application/x-" + testClass.getSimpleName(); 35 | } 36 | 37 | public static int testMessageCounter = 0; 38 | 39 | /** 40 | * Produce a test message - just some bytes - Note: This methods return a different message 41 | * after each call. 42 | * @return 43 | */ 44 | public static byte[] produceTestMessage() { 45 | String s = "testMessage" + testMessageCounter++; 46 | return s.getBytes(); 47 | } 48 | 49 | public static boolean sameMessages(byte[] msgA, byte[] msgB) { 50 | return Utils.compareArrays(msgA, msgB); 51 | } 52 | 53 | public static void removeFolder(String foldername) { 54 | FSUtils.removeFolder(foldername); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPHopImpl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | public class ASAPHopImpl implements ASAPHop { 4 | private final CharSequence sender; 5 | private final boolean verified; 6 | private final boolean encrypted; 7 | private final ASAPEncounterConnectionType connectionType; 8 | 9 | public ASAPHopImpl(CharSequence sender, boolean verified, boolean encrypted, ASAPEncounterConnectionType connectionType) { 10 | this.sender = sender; 11 | this.verified = verified; 12 | this.encrypted = encrypted; 13 | this.connectionType = connectionType; 14 | } 15 | 16 | @Override 17 | public CharSequence sender() { 18 | return this.sender; 19 | } 20 | 21 | @Override 22 | public ASAPEncounterConnectionType getConnectionType() { 23 | return this.connectionType; 24 | } 25 | 26 | @Override 27 | public boolean verified() { 28 | return this.verified; 29 | } 30 | 31 | @Override 32 | public boolean encrypted() { 33 | return this.encrypted; 34 | } 35 | 36 | public String toString() { 37 | StringBuilder sb = new StringBuilder(); 38 | sb.append("sender: "); 39 | sb.append(this.sender); 40 | sb.append(" | "); 41 | sb.append("verified: "); 42 | sb.append(this.verified); 43 | sb.append(" | "); 44 | sb.append("encrypted: "); 45 | sb.append(this.encrypted); 46 | sb.append(" | "); 47 | sb.append("connectionType: "); 48 | switch (this.connectionType) { 49 | case ONION_NETWORK: sb.append("onionNetwork"); break; 50 | case ASAP_HUB: sb.append("ASAP Hub"); break; 51 | case AD_HOC_LAYER_2_NETWORK: sb.append("Ad-hoc protocol"); break; 52 | case INTERNET: sb.append("Internet"); break; 53 | default: sb.append("unknown"); break; 54 | } 55 | 56 | return sb.toString(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAP_AssimilationPDU_1_0.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.util.List; 9 | 10 | public interface ASAP_AssimilationPDU_1_0 extends ASAP_PDU_1_0 { 11 | String getRecipientPeer(); 12 | 13 | byte[] getData() throws IOException; 14 | 15 | /** 16 | * @return full length of chunk data, regardless of any internal message 17 | * structure whatsoever. 18 | */ 19 | long getLength(); 20 | 21 | /** 22 | * it is assumed that the stream of bytes contains a number of 23 | * opaque, application specific messages. This list contains the offsets where 24 | * each message starts. The obvious first offset 0 is not part of this 25 | * list. Thus, an empty list means: There is only one application specific 26 | * message in this data block. 27 | * 28 | * Applications don't have to use this feature, e.g. if it tempers with 29 | * encryption / security issues. 30 | * 31 | * @return list of numbers. Each number states the position of the first byte 32 | * of each message. There is no number 0: First message starts - of course - at the beginning. 33 | */ 34 | List getMessageOffsets(); 35 | 36 | List getASAPHopList(); 37 | 38 | /** 39 | * Streams data into a storage. That method should be used instead of getData() when possible. 40 | * Data can directly be passed from network to its final destination without allocation memory 41 | * storage. 42 | * @param os 43 | * @throws IOException 44 | */ 45 | void streamData(OutputStream os) throws IOException; 46 | 47 | /** 48 | * return row input stream from protocol - handle with care! 49 | * @return 50 | */ 51 | public InputStream getInputStream() throws IOException; 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPEnginePermissionSettings.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import java.io.IOException; 4 | 5 | interface ASAPEnginePermissionSettings { 6 | /** 7 | * Engine can remember peers they encountered. It is assumed that those peers are kept with the local peer (not only 8 | * in this engine) 9 | * @param on 10 | * @throws IOException 11 | */ 12 | void setRememberEncounteredPeers(boolean on) throws IOException; 13 | 14 | /** 15 | * Engine would ignore all unencrypted messages if set true. 16 | * @param on 17 | * @throws IOException 18 | */ 19 | void setReceivedMessagesMustBeEncrypted(boolean on) throws IOException; 20 | 21 | /** 22 | * Engine would ignore all unsigned messages if set true. 23 | * @param on 24 | * @throws IOException 25 | */ 26 | void setReceivedMessagesMustBeSigned(boolean on) throws IOException; 27 | 28 | /** 29 | * Define with what peers an engine is allowed to communicate 30 | * @param safetyLevel 31 | * @throws IOException 32 | */ 33 | void setSetAllowedRemotePeers(AllowedRemotePeers safetyLevel); 34 | 35 | public enum AllowedRemotePeers { 36 | ANY_PEER /** no restrictions - allowed to communicate with any peer */, 37 | PEERS_MET /** allowed to communicate with already encountered peers */, 38 | PEERS_VERIFIED /** allowed to communicate with peers who can be verified with a certificate */; 39 | } 40 | 41 | /** 42 | * Engine is allowed to reveal its supported format or not 43 | * @param peerName can be null for anonymous peer 44 | * @return 45 | */ 46 | boolean setRevealEngineFormat(String peerName); 47 | 48 | /** 49 | * Engine is allowed to send open (messages with no recipient specified) messages to a peer 50 | * @param peerName can be null for anonymous peer 51 | * @return 52 | */ 53 | boolean setSendOpenMessages(String peerName); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPProtocolEngine.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPEncounterConnectionType; 4 | import net.sharksystem.asap.ASAPException; 5 | import net.sharksystem.asap.protocol.ASAP_1_0; 6 | import net.sharksystem.asap.protocol.ASAP_AssimilationPDU_1_0; 7 | import net.sharksystem.asap.protocol.ASAP_Interest_PDU_1_0; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | 13 | /** 14 | * @author thsc 15 | */ 16 | public interface ASAPProtocolEngine { 17 | void handleASAPInterest(ASAP_Interest_PDU_1_0 asapInterest, ASAP_1_0 protocol, 18 | String encounteredPeer, OutputStream os, ASAPEncounterConnectionType connectionType) 19 | throws ASAPException, IOException; 20 | 21 | void handleASAPAssimilate(ASAP_AssimilationPDU_1_0 asapAssimilationPDU, ASAP_1_0 protocolModem, 22 | String encounteredPeer, InputStream is, OutputStream os, 23 | ASAPEncounterConnectionType connectionType, 24 | ASAPChunkAssimilatedListener listener) 25 | throws ASAPException, IOException; 26 | 27 | /** 28 | * Chunks are (tried to be) delivered to their recipients during each encounter 29 | * with another peer. After successful delivery, recipient is withdrawn from recipient 30 | * list to prevent multiple delivery. 31 | * 32 | * If this flag is set, chunk are removed permanently if their are delivered 33 | * to all their recipients. There are kept in local storage otherwise. 34 | * @param drop 35 | */ 36 | void setBehaviourDropDeliveredChunks(boolean drop) throws IOException; 37 | 38 | /** 39 | * engine can deliver local message but also received messages - default false - send no received messages 40 | * @param on 41 | */ 42 | void setBehaviourAllowRouting(boolean on) throws IOException; 43 | 44 | boolean routingAllowed(); 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/appTests/Reenactment.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.appTests; 2 | 3 | import net.sharksystem.asap.apps.testsupport.ASAPTestPeerFS; 4 | import net.sharksystem.asap.ASAPException; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | 11 | public class Reenactment { 12 | public static final String ALICE_ROOT_FOLDER = "reenact/Alice"; 13 | public static final String ALICE_APP_FOLDER = ALICE_ROOT_FOLDER + "/appFolder"; 14 | public static final String BOB_ROOT_FOLDER = "reenact/Bob"; 15 | public static final String BOB_APP_FOLDER = BOB_ROOT_FOLDER + "/appFolder"; 16 | public static final String ALICE = "Alice"; 17 | public static final String BOB = "Bob"; 18 | 19 | 20 | @Test 21 | public void reenact2021_01_05() throws IOException, ASAPException, InterruptedException { 22 | String format = "application/x-BluetoothSchach"; 23 | String uri1 = "bluetoothschach://"; 24 | String uri2 = "bluetoothschach://3979"; 25 | String oneSign = "X"; 26 | String message = "message B -> A"; 27 | 28 | Collection formats = new ArrayList<>(); 29 | formats.add(format); 30 | 31 | ///////////////// ALICE ////////////////////////////////////////////////////////////// 32 | // setup mocked peer / asap application and activity in android 33 | ASAPTestPeerFS aliceSimplePeer = new ASAPTestPeerFS(ALICE, formats); 34 | ASAPTestPeerFS bobSimplePeer = new ASAPTestPeerFS(BOB, formats); 35 | 36 | aliceSimplePeer.sendASAPMessage(format, uri1, oneSign.getBytes()); 37 | 38 | bobSimplePeer.sendASAPMessage(format, uri2, message.getBytes()); 39 | 40 | aliceSimplePeer.startEncounter(7777, bobSimplePeer); 41 | // give your app a moment to process 42 | Thread.sleep(1000); 43 | // stop encounter 44 | bobSimplePeer.stopEncounter(aliceSimplePeer); 45 | // give your app a moment to process 46 | Thread.sleep(1000); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPSingleProcessOnlineMessageSender.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.protocol.ASAPConnection; 5 | import net.sharksystem.asap.protocol.ASAPOnlineMessageSource; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.util.Set; 10 | 11 | public class ASAPSingleProcessOnlineMessageSender 12 | extends ASAPAbstractOnlineMessageSender implements ASAPOnlineMessageSource { 13 | 14 | private final ASAPOnlineMessageSenderEngineSide asapOnlineMessageSenderEngineSide; 15 | 16 | public ASAPSingleProcessOnlineMessageSender(ASAPInternalPeer multiEngine, ASAPInternalStorage source) { 17 | this.attachToSource(source); 18 | this.asapOnlineMessageSenderEngineSide = new ASAPOnlineMessageSenderEngineSide(multiEngine); 19 | } 20 | 21 | @Override 22 | public void sendASAPAssimilateMessage(CharSequence format, CharSequence uri, Set recipients, 23 | byte[] messageAsBytes, int era) throws IOException, ASAPException { 24 | 25 | this.asapOnlineMessageSenderEngineSide.sendASAPAssimilateMessage( 26 | format, uri, recipients, messageAsBytes, era); 27 | } 28 | 29 | public void sendASAPAssimilateMessage(CharSequence format, CharSequence uri, byte[] messageAsBytes) 30 | throws IOException, ASAPException { 31 | this.sendASAPAssimilateMessage(format, uri, messageAsBytes, ASAPEngineFS.DEFAULT_INIT_ERA); 32 | } 33 | 34 | @Override 35 | public void sendASAPAssimilateMessage(CharSequence format, CharSequence uri, byte[] messageAsBytes, int era) 36 | throws IOException, ASAPException { 37 | 38 | this.asapOnlineMessageSenderEngineSide.sendASAPAssimilateMessage(format, uri, messageAsBytes, era); 39 | } 40 | 41 | @Override 42 | public void sendStoredMessages(ASAPConnection asapConnection, OutputStream os) throws IOException { 43 | this.asapOnlineMessageSenderEngineSide.sendStoredMessages(asapConnection, os); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/streams/DataExchangeTester.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.streams; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.utils.ASAPSerialization; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | 10 | class DataExchangeTester implements Runnable { 11 | private final InputStream is; 12 | private final OutputStream os; 13 | private final String id; 14 | private int rounds; 15 | public boolean synced = false; 16 | 17 | DataExchangeTester(InputStream is, OutputStream os, int rounds, String id) { 18 | this.is = is; 19 | this.os = os; 20 | this.rounds = rounds; 21 | this.id = id; 22 | } 23 | 24 | @Override 25 | public void run() { 26 | // exchange some example data 27 | int value = 0; 28 | try { 29 | int maxValue = this.rounds - 1; 30 | System.out.println("Data Exchange Tester - count up to " + maxValue + " | " + id); 31 | while (this.rounds-- > 0) { 32 | System.out.println("write data: " + value + " | " + id); 33 | ASAPSerialization.writeIntegerParameter(value, this.os); 34 | int retVal = ASAPSerialization.readIntegerParameter(this.is); 35 | System.out.println("read data: " + retVal + " | " + id); 36 | 37 | if (value != retVal) { 38 | System.out.println("data exchange testers are out of sync: " + id); 39 | break; 40 | } 41 | value++; 42 | } 43 | System.out.println("synced: " + id); 44 | this.synced = true; 45 | // block 46 | System.out.println("read again - blocking (?): " + id); 47 | int retVal = this.is.read(); 48 | System.out.println("back from read after synced: " + retVal + " | " + id); 49 | } catch (IOException | ASAPException e) { 50 | System.out.println("exception data exchange tester - most probably good: " + id + " | " 51 | + e.getLocalizedMessage()); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAPProtocolEngine.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | import net.sharksystem.asap.engine.ASAPUndecryptableMessageHandler; 4 | import net.sharksystem.asap.crypto.ASAPKeyStore; 5 | import net.sharksystem.utils.Log; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | 11 | public abstract class ASAPProtocolEngine { 12 | protected final ASAP_1_0 protocol; 13 | protected final InputStream is; 14 | protected final OutputStream os; 15 | protected final ASAPUndecryptableMessageHandler undecryptableMessageHandler; 16 | protected final ASAPKeyStore ASAPKeyStore; 17 | 18 | public ASAPProtocolEngine(InputStream is, OutputStream os, ASAP_1_0 protocol, 19 | ASAPUndecryptableMessageHandler undecryptableMessageHandler, 20 | ASAPKeyStore ASAPKeyStore) { 21 | /* 22 | this.is = new ISWrapper(is); 23 | this.os = new OSWrapper(os); 24 | */ 25 | this.is = is; 26 | this.os = os; 27 | this.protocol = protocol; 28 | this.undecryptableMessageHandler = undecryptableMessageHandler; 29 | this.ASAPKeyStore = ASAPKeyStore; 30 | 31 | Log.writeLog(this, "constructor", "is: " 32 | + is.getClass().getSimpleName() + " | os: " + os.getClass().getSimpleName()); 33 | } 34 | 35 | private class ISWrapper extends InputStream { 36 | private final InputStream is; 37 | 38 | ISWrapper(InputStream is) { 39 | this.is = is; 40 | } 41 | @Override 42 | public int read() throws IOException { 43 | return is.read(); 44 | } 45 | public void close() { 46 | Log.writeLog(this, "wrapper: close called"); 47 | } 48 | } 49 | 50 | private class OSWrapper extends OutputStream { 51 | private final OutputStream os; 52 | 53 | OSWrapper(OutputStream is) { 54 | this.os = is; 55 | } 56 | 57 | @Override 58 | public void write(int b) throws IOException { 59 | this.os.write(b); 60 | } 61 | 62 | public void close() { 63 | Log.writeLog(this, "wrapper: close called"); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPConnectionHandler.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.asap.protocol.ASAPConnection; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.util.Set; 9 | 10 | public interface ASAPConnectionHandler { 11 | /** 12 | * Ask the entity to run an ASAP session based on this point-to-point connection. 13 | * @param is stream to read from 14 | * @param os stream to write into 15 | * @param encrypt encrypt this point-to-point communication 16 | * @param sign sign this point-to-point communication 17 | * @param appsWhiteList if not null - only message from this app (synonym: with this format) 18 | * within that list are handled. All other messages are ignored 19 | * @param appsBlackList if not null - message from this app (synonym: with this format) 20 | * are not handled. Overwrites white list entries. 21 | * @return 22 | * @throws IOException 23 | * @throws ASAPException 24 | */ 25 | ASAPConnection handleConnection( 26 | InputStream is, OutputStream os, boolean encrypt, boolean sign, 27 | Set appsWhiteList, Set appsBlackList 28 | ) throws IOException, ASAPException; 29 | 30 | ASAPConnection handleConnection( 31 | InputStream is, OutputStream os, boolean encrypt, boolean sign, ASAPEncounterConnectionType connectionType, 32 | Set appsWhiteList, Set appsBlackList 33 | ) throws IOException, ASAPException; 34 | 35 | /** 36 | * 37 | * Ask the entity to run an ASAP session based on this point-to-point connection. Message are neither 38 | * encrypted nor signed. All formats are accepted in principle. 39 | * @param is stream to read from 40 | * @param os stream to write into 41 | * @return 42 | * @throws IOException 43 | * @throws ASAPException 44 | */ 45 | ASAPConnection handleConnection(InputStream is, OutputStream os) throws IOException, ASAPException; 46 | 47 | ASAPConnection handleConnection(InputStream inputStream, OutputStream outputStream, 48 | ASAPEncounterConnectionType connectionType) throws IOException, ASAPException; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/ASAPChunkReceivedTester.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import net.sharksystem.asap.ASAP; 4 | import net.sharksystem.asap.ASAPHop; 5 | import net.sharksystem.asap.ASAPMessages; 6 | import net.sharksystem.asap.engine.ASAPChunkAssimilatedListener; 7 | import net.sharksystem.utils.Log; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * 15 | * @author thsc 16 | */ 17 | public class ASAPChunkReceivedTester implements ASAPChunkAssimilatedListener { 18 | private CharSequence senderE2E = null; 19 | private CharSequence uri = null; 20 | private int era; 21 | private CharSequence format; 22 | 23 | @Override 24 | public void chunkStored(String format, String senderE2E, String uri, int era, 25 | List asapHop) throws IOException { 26 | 27 | this.chunkAssimilated(format, senderE2E, uri, era, asapHop); 28 | } 29 | 30 | @Override 31 | public void transientMessagesReceived(ASAPMessages transientMessages, ASAPHop asapHop) throws IOException { 32 | List asapHops = new ArrayList<>(); 33 | asapHops.add(asapHop); 34 | this.chunkAssimilated(transientMessages.getFormat(), asapHop.sender(), 35 | transientMessages.getURI(), ASAP.TRANSIENT_ERA, asapHops); 36 | } 37 | 38 | private void chunkAssimilated(CharSequence format, CharSequence senderE2E, CharSequence uri, int era, List asapHop) { 39 | Log.writeLog(this, "chunk assimilated called: (format|sender|uri|era) " + 40 | format + 41 | " | " + 42 | senderE2E + 43 | " | " + 44 | uri + 45 | " | " + 46 | era); 47 | this.format = format; 48 | this.senderE2E = senderE2E; 49 | this.uri = uri; 50 | this.era = era; 51 | 52 | } 53 | 54 | public boolean chunkReceived() { 55 | return this.senderE2E != null; 56 | } 57 | 58 | public String getSenderE2E() { 59 | return this.senderE2E.toString(); 60 | } 61 | 62 | public String getFormat() { 63 | return this.format.toString(); 64 | } 65 | 66 | public String getUri() { 67 | return this.uri.toString(); 68 | } 69 | 70 | public int getEra() { 71 | return this.era; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/cmdline/ExampleASAPChunkReceivedListener.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.cmdline; 2 | 3 | import net.sharksystem.asap.ASAPHop; 4 | import net.sharksystem.asap.ASAPMessages; 5 | import net.sharksystem.asap.engine.ASAPChunkAssimilatedListener; 6 | import net.sharksystem.utils.Log; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | // ASAP API: callbacks ASAPChunkReceived // 14 | //////////////////////////////////////////////////////////////////////////////////////////////////////////// 15 | public class ExampleASAPChunkReceivedListener implements ASAPChunkAssimilatedListener { 16 | private final String rootFolder; 17 | private List receivedList = new ArrayList<>(); 18 | 19 | public ExampleASAPChunkReceivedListener(String rootFolder) { 20 | this.rootFolder = rootFolder; 21 | } 22 | 23 | @Override 24 | public void chunkStored(String format, String senderE2E, String uri, int era, 25 | List asapHop) throws IOException { 26 | 27 | this.receivedList.add(new ASAPChunkReceivedParameters(format, senderE2E, uri, era)); 28 | } 29 | 30 | @Override 31 | public void transientMessagesReceived(ASAPMessages transientMessages, ASAPHop asapHop) throws IOException { 32 | Log.writeLog(this, "transient message received - TODO?"); 33 | } 34 | 35 | public List getReceivedList() { return this.receivedList; } 36 | 37 | public class ASAPChunkReceivedParameters { 38 | private final String format; 39 | private final String sender; 40 | private final String uri; 41 | private final int era; 42 | 43 | private ASAPChunkReceivedParameters(String format, String sender, String uri, int era) { 44 | Log.writeLog(this, "ASAPChunkReceivedParameters: chunk received: " + format + " | " + sender + " | " + uri); 45 | this.format = format; 46 | this.sender = sender; 47 | this.uri = uri; 48 | this.era = era; 49 | } 50 | 51 | public String getFormat() { return this.format; } 52 | public String getSender() { return this.sender; } 53 | public String getUri() { return this.uri; } 54 | public int getEra() { return this.era; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/engine/BasisMethodsTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAP; 4 | import net.sharksystem.asap.ASAPException; 5 | import net.sharksystem.asap.utils.ASAPSerialization; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | 13 | public class BasisMethodsTests { 14 | 15 | @Test 16 | public void asapID() throws InterruptedException { 17 | for (int i = 0; i < 10; i++) { 18 | System.out.println(ASAP.createUniqueID()); 19 | Thread.sleep(2); 20 | } 21 | } 22 | 23 | @Test 24 | public void bitMasks() throws IOException, ASAPException { 25 | int flags = 0x0000BA98; 26 | System.out.print("1: "); 27 | ASAPSerialization.printBits(flags); 28 | System.out.print("\n"); 29 | 30 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 31 | byte flagBytes = ASAPSerialization.getByteFromInt(flags, 0); 32 | ASAPSerialization.writeByteParameter(flagBytes, os); // mand 33 | flagBytes = ASAPSerialization.getByteFromInt(flags, 1); 34 | ASAPSerialization.writeByteParameter(flagBytes, os); // mand 35 | 36 | ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); 37 | 38 | byte byteLeastSignificant = ASAPSerialization.readByte(is); 39 | byte byteNextByte = ASAPSerialization.readByte(is); 40 | 41 | int retFlags = byteNextByte; 42 | retFlags = retFlags << 8; 43 | System.out.print("2: "); 44 | ASAPSerialization.printBits(retFlags); 45 | System.out.print("\n"); 46 | 47 | // set all random bits to 0 48 | retFlags = retFlags & 0x0000FF00; 49 | // set all bits in higher byte to zero - not exactly necessary but makes debugging easier 50 | System.out.print("3: "); 51 | ASAPSerialization.printBits(retFlags); 52 | System.out.print("\n"); 53 | 54 | int leastSignificantInt = byteLeastSignificant; 55 | leastSignificantInt = leastSignificantInt & 0x000000FF; 56 | System.out.print("4: "); 57 | ASAPSerialization.printBits(leastSignificantInt); 58 | System.out.print("\n"); 59 | 60 | retFlags = retFlags | leastSignificantInt; 61 | System.out.print("5: "); 62 | ASAPSerialization.printBits(retFlags); 63 | System.out.print("\n"); 64 | 65 | Assert.assertEquals(flags, retFlags); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.sharksystem 8 | ASAPJava 9 | 0.7.0 10 | 11 | 12 | 8 13 | 8 14 | 5.8.1 15 | 1.8.1 16 | 0.7.0 17 | 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.13.2 24 | test 25 | 26 | 27 | 28 | 29 | org.junit.jupiter 30 | junit-jupiter-engine 31 | 5.8.1 32 | test 33 | 34 | 35 | 36 | 37 | commons-io 38 | commons-io 39 | 2.11.0 40 | 41 | 42 | 43 | 45 | 46 | com.sun.xml.bind 47 | jaxb-impl 48 | 3.0.2 49 | 50 | 51 | org.junit.platform 52 | junit-platform-suite-api 53 | 1.8.1 54 | test 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-surefire-plugin 65 | 2.22.1 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/protocol/ASAP_PDU_1_0.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.protocol; 2 | 3 | public interface ASAP_PDU_1_0 { 4 | /** 5 | * @return ASAP command. Version 1 know three alternatives: interest, offer, assimilate 6 | */ 7 | byte getCommand(); 8 | 9 | /** 10 | * @return ID of sender of this message - optional 11 | */ 12 | String getSender(); 13 | 14 | /** 15 | * @return ID of recipient of this message - optional 16 | */ 17 | String getRecipient(); 18 | 19 | /** 20 | * @return format A URI style is strongly recommended. 21 | * In most cases a format will match with an application that deals with one (or more 22 | * formats) 23 | * 24 | */ 25 | String getFormat(); 26 | 27 | /** 28 | * @return A URI representing content (semantics) of this message. 29 | * It is assumed that multiple messages with same uri are floating around the 30 | * ASAP network 31 | */ 32 | String getChannelUri(); 33 | 34 | /** 35 | * @return Sender can send era in which this message was created. Can be helpful 36 | * in multihop networks to reduce communication costs. 37 | */ 38 | int getEra(); 39 | 40 | /** 41 | * @return a flag that indicates whether the optional sender parameter was transmitted 42 | */ 43 | boolean senderSet(); 44 | 45 | /** 46 | * @return true if received message was encrypted and could obviously be encrypted 47 | */ 48 | boolean encrypted(); 49 | 50 | /** 51 | * @return true if received message was signed 52 | */ 53 | boolean signed(); 54 | 55 | /** 56 | * @return true if received message was signed and signature could be verified 57 | */ 58 | boolean verified(); 59 | 60 | /** 61 | * @return a flag that indicates whether the optional recipient parameter was transmitted 62 | */ 63 | boolean recipientSet(); 64 | /** 65 | * @return a flag that indicates whether the optional channel parameter was transmitted 66 | */ 67 | boolean channelSet(); 68 | /** 69 | * @return a flag that indicates whether the optional era parameter was transmitted 70 | */ 71 | boolean eraSet(); 72 | 73 | /** 74 | * Routing allowed - yes or no 75 | * @return 76 | */ 77 | public boolean routing(); 78 | 79 | /** 80 | * Sent an encounter list? 81 | * @return 82 | */ 83 | public boolean encounterList(); 84 | 85 | /** 86 | * Make sure that their are no more data on the real input stream. This pdu object will no longer be used. 87 | */ 88 | void takeDataFromStream(); 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/IdleStreamPairCloser.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | import net.sharksystem.utils.AlarmClock; 4 | import net.sharksystem.utils.AlarmClockListener; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | 10 | public class IdleStreamPairCloser implements AlarmClockListener, WrappedStreamPairListener { 11 | private final int timeout; 12 | private StreamPairWrapper streamPairWrapper; 13 | private AlarmClock alarmClock; 14 | 15 | private IdleStreamPairCloser(int timeout) { 16 | this.timeout = timeout; 17 | } 18 | 19 | public static IdleStreamPairCloser getIdleStreamsCloser(StreamPair streamPair, int timeout) throws IOException { 20 | // create object that also becomes listener 21 | IdleStreamPairCloser idleStreamPairCloser = new IdleStreamPairCloser(timeout); 22 | 23 | // create wrapper with listener 24 | StreamPairWrapper streamPairWrapper = new StreamPairWrapper( 25 | streamPair.getInputStream(), streamPair.getOutputStream(), idleStreamPairCloser, "X"); 26 | 27 | // now: put wrapper into listener 28 | idleStreamPairCloser.setStreamPairWrapper(streamPairWrapper); 29 | return idleStreamPairCloser; 30 | } 31 | 32 | public InputStream getInputStream() { 33 | return this.streamPairWrapper.getInputStream(); 34 | } 35 | 36 | public OutputStream getOutputStream() { 37 | return this.streamPairWrapper.getOutputStream(); 38 | } 39 | 40 | public StreamPair getStreamPair() { 41 | return this.streamPairWrapper; 42 | } 43 | 44 | void setStreamPairWrapper(StreamPairWrapper streamPairWrapper) { 45 | this.streamPairWrapper = streamPairWrapper; 46 | } 47 | 48 | public void start() { 49 | // give it more time in the first round - there will be a connection establishment process on its way... 50 | this.alarmClock = new AlarmClock(this.timeout * 2, this); 51 | } 52 | 53 | @Override 54 | public void alarmClockRinging(int i) { 55 | try { 56 | this.streamPairWrapper.getInputStream().close(); 57 | } catch (IOException e) { 58 | // ignore 59 | } 60 | } 61 | 62 | @Override 63 | public void notifyClosed(StreamPair streamPair, String s) { 64 | this.alarmClock.kill(); // nothing todo. 65 | this.alarmClock = null; 66 | } 67 | 68 | @Override 69 | public void notifyAction(String s) { 70 | if(alarmClock != null) { 71 | this.alarmClock.kill(); 72 | this.alarmClock = new AlarmClock(timeout, this); 73 | } // else not yet started 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/Log.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils; 2 | 3 | import java.io.PrintStream; 4 | 5 | public class Log { 6 | private static PrintStream outStream = System.out; 7 | private static PrintStream errStream = System.err; 8 | 9 | public static void setOutStream(PrintStream outStream) { 10 | Log.outStream = outStream; 11 | } 12 | 13 | public static void setErrStream(PrintStream errStream) { 14 | Log.errStream = errStream; 15 | } 16 | 17 | public static StringBuilder startLog(Object o, String parameter) { 18 | return startLog(o.getClass(), parameter); 19 | } 20 | 21 | public static StringBuilder startLog(Object o) { 22 | return startLog(o, null); 23 | } 24 | 25 | public static StringBuilder startLog(Class c) { 26 | return startLog(c, null); 27 | } 28 | 29 | public static StringBuilder startLog(Class c, String parameter) { 30 | StringBuilder sb = new StringBuilder(); 31 | sb.append(c.getSimpleName()); 32 | if(parameter != null && parameter.length() > 0) { 33 | sb.append("("); 34 | sb.append(parameter); 35 | sb.append(")"); 36 | } 37 | sb.append(": "); 38 | return sb; 39 | } 40 | 41 | public static void writeLog(Object o, String parameter, String message) { 42 | Log.outStream.println(Log.startLog(o, parameter) + message); 43 | } 44 | 45 | public static void writeLog(Object o, CharSequence parameter, String message) { 46 | Log.outStream.println(Log.startLog(o, String.valueOf(parameter)) + message); 47 | } 48 | 49 | public static void writeLog(Object o, String message) { 50 | writeLog(o, null, message); 51 | } 52 | 53 | public static void writeLog(Class c, String parameter, String message) { 54 | Log.outStream.println(Log.startLog(c, parameter) + message); 55 | } 56 | 57 | public static void writeLog(Class c, String message) { 58 | writeLog(c, null, message); 59 | } 60 | 61 | public static void writeLogErr(Object o, String parameter, String message) { 62 | Log.errStream.println(Log.startLog(o, parameter) + message); 63 | } 64 | 65 | public static void writeLogErr(Object o, CharSequence parameter, String message) { 66 | Log.errStream.println(Log.startLog(o, String.valueOf(parameter)) + message); 67 | } 68 | 69 | public static void writeLogErr(Object o, String message) { 70 | writeLogErr(o, null, message); 71 | } 72 | 73 | public static void writeLogErr(Class c, String parameter, String message) { 74 | Log.errStream.println(Log.startLog(c, parameter) + message); 75 | } 76 | 77 | public static void writeLogErr(Class c, String message) { 78 | writeLogErr(c, null, message); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPInternalChunk.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPChannel; 4 | import net.sharksystem.asap.ASAPChunk; 5 | import net.sharksystem.asap.ASAPHop; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.*; 10 | 11 | /** 12 | * An ASAP chunk is a set of message with the same format, same uri and same era. 13 | * 14 | * Reading from a chunk can be done anytime. 15 | * 16 | * Use change methods only if you really know what you do. Most likely, there will be 17 | * a running ASAP protocol in the background that writes data into chunks, namely chunks 18 | * of current era. The protocol engine reads also from older era to transmit messages to 19 | * encountered peers. There will be a documentation that explains strategies when to do what. 20 | * 21 | * Rule of thumb: Read is ok. Better do not change anything with this interface. 22 | * 23 | * @author thsc 24 | */ 25 | public interface ASAPInternalChunk extends ASAPChunk, MessagesContainer { 26 | /** 27 | * Convenient methode: It calls getMessages and transforms each message into a string 28 | * @return iterator of all messages in the chunk 29 | * @throws IOException 30 | */ 31 | Iterator getMessagesAsCharSequence() throws IOException; 32 | 33 | /** 34 | * 35 | * @return recipients of that chunk 36 | */ 37 | Set getRecipients(); 38 | 39 | /** 40 | * add recipients 41 | * @param recipient 42 | * @throws IOException 43 | */ 44 | void addRecipient(CharSequence recipient) throws IOException; 45 | 46 | /** 47 | * set a list of recipients. Former recipients are dikscarded 48 | * @param recipients 49 | * @throws IOException 50 | */ 51 | void setRecipients(Collection recipients) throws IOException; 52 | 53 | /** 54 | * recipient is removed 55 | * @param recipients 56 | * @throws IOException 57 | */ 58 | void removeRecipient(CharSequence recipients) throws IOException; 59 | 60 | long getLength(); 61 | 62 | List getOffsetList(); 63 | 64 | InputStream getMessageInputStream(); 65 | 66 | void putExtra(String key, String value) throws IOException; 67 | 68 | CharSequence removeExtra(String key) throws IOException; 69 | 70 | CharSequence getExtra(String key) throws IOException; 71 | 72 | /** 73 | * set up this chunk by a source 74 | * @param chunkSource 75 | */ 76 | void clone(ASAPInternalChunk chunkSource) throws IOException; 77 | 78 | HashMap getExtraData(); 79 | 80 | void deliveredTo(String peer) throws IOException; 81 | 82 | List getDeliveredTo(); 83 | 84 | void copyMetaData(ASAPChannel channel) throws IOException; 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPMessages.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.engine.ASAPInternalChunk; 5 | 6 | import java.io.IOException; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | 10 | /** 11 | * Chunks are identified by an URI and ordered by era numbers. 12 | * User applications can often define an URI but would hardly like 13 | * to deal with era management. 14 | * 15 | * This interface helps those applications. Classes implementing that 16 | * interface wrap an actual chunk storage and offer a view of an ordered 17 | * list of message. Especially era management is hidden with this interfaces. 18 | * 19 | * It is meant to be a cache. It is *not* assumed that this cache is 20 | * always in sync with the actual chunk storage. Syncing is up to application 21 | * which makes implementing that cache easier. 22 | * 23 | * 24 | * @author thsc 25 | */ 26 | public interface ASAPMessages { 27 | /** 28 | * @return number of messages 29 | */ 30 | int size() throws IOException; 31 | 32 | /** 33 | * @return channel uri 34 | */ 35 | CharSequence getURI(); 36 | 37 | /** 38 | * @return format which - in most cases - matches with an application name 39 | */ 40 | CharSequence getFormat(); 41 | 42 | /** 43 | * 44 | * first, false: newest message comes first 45 | * @return iterator of all messages in that chunk cache 46 | * @throws IOException 47 | * @deprecated this lib works supports byte[] - apps deal with (de)serialization. 48 | */ 49 | Iterator getMessagesAsCharSequence() throws IOException; 50 | 51 | /** 52 | * 53 | * first, false: newest message comes first 54 | * @return iterator of all messages in that chunk cache 55 | * @throws IOException 56 | */ 57 | Iterator getMessages() throws IOException; 58 | 59 | /** 60 | * Returns a message with a given position 61 | * @param position 62 | * @param chronologically in chronological order: true: oldest message comes 63 | * first, false: newest message comes first 64 | * @return message 65 | * @throws ASAPException message on that position does 66 | * not exist 67 | * @throws IOException couldn't read from storage 68 | */ 69 | CharSequence getMessageAsCharSequence(int position, boolean chronologically) 70 | throws ASAPException, IOException; 71 | 72 | byte[] getMessage(int position, boolean chronologically) 73 | throws ASAPException, IOException; 74 | 75 | /** 76 | * Return chunk in which message at position is to be found 77 | * @param position 78 | * @param chronologically 79 | * @return 80 | */ 81 | ASAPChunk getChunk(int position, boolean chronologically) throws IOException, ASAPException; 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/utils/Batchprocessor.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.utils; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.engine.ASAPInternalPeer; 5 | import net.sharksystem.asap.cmdline.CmdLineUI; 6 | import net.sharksystem.utils.Log; 7 | 8 | import java.io.ByteArrayInputStream; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.PrintStream; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class Batchprocessor implements Runnable { 15 | List cmdList = new ArrayList<>(); 16 | private CmdLineUI cmdLineUI; 17 | private PrintStream printStream; 18 | private ByteArrayInputStream inputStream; 19 | private Thread runningThread; 20 | 21 | public Batchprocessor() { 22 | this(true); 23 | } 24 | 25 | public Batchprocessor(boolean cleanup) { 26 | if(cleanup) { 27 | Log.writeLog(this, "clean asap peers folders"); 28 | this.cmdLineUI = new CmdLineUI(); 29 | } 30 | } 31 | 32 | public void setOutputstream(PrintStream ps) { 33 | this.cmdLineUI.setOutStreams(ps); 34 | } 35 | 36 | public void addCommand(String cmd) { 37 | this.cmdList.add(cmd); 38 | } 39 | 40 | public void execute() { 41 | this.prepareExecution(); 42 | this.doExecution(); 43 | } 44 | 45 | public void executeAsThread() { 46 | this.prepareExecution(); 47 | this.runningThread = new Thread(this); 48 | this.runningThread.start(); 49 | } 50 | 51 | public void join() throws InterruptedException { 52 | if(this.runningThread != null && this.runningThread.isAlive()) { 53 | this.runningThread.join(); 54 | } 55 | } 56 | 57 | private void prepareExecution() { 58 | // prepare output 59 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 60 | PrintStream ps = new PrintStream(baos); 61 | 62 | for(String cmd : this.cmdList) { 63 | ps.println(cmd); 64 | } 65 | 66 | // clean cmd list 67 | this.cmdList = new ArrayList<>(); 68 | 69 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 70 | 71 | this.printStream = System.out; 72 | this.inputStream = bais; 73 | } 74 | 75 | private void doExecution() { 76 | this.cmdLineUI.runCommandLoop(this.printStream, this.inputStream); 77 | 78 | // in any case - give it some time to tidy up 79 | try { 80 | Thread.sleep(1000); 81 | } catch (InterruptedException e) { 82 | // ignore 83 | } 84 | 85 | this.runningThread = null; 86 | } 87 | 88 | @Override 89 | public void run() { 90 | this.doExecution(); 91 | } 92 | 93 | public ASAPInternalPeer getASAPPeer(String peerName) throws ASAPException { 94 | return this.cmdLineUI.getASAPPeer(peerName); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPEncounterManagerAdmin.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.util.Date; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | /** 8 | * That's the admin interface of an encounter manager. It allows deny list management. 9 | * A deny list contains peerIDs. The encounter manager must not establish new connections to 10 | * those peers. There can be several reasons (security (peer is not considered trustworthy), 11 | * network topology (see considerations of a large scale ad-hoc network with limited 12 | * connections on each peer). 13 | * 14 | * Future enhancements: 15 | *
  • define a point to point encryption policy.
  • 16 | *
  • define what connections types are acceptable (Internet, Hub, Ad-hoc, Onion)
17 | */ 18 | public interface ASAPEncounterManagerAdmin { 19 | /** 20 | * @return set of ID to which an open connection exists right now. 21 | */ 22 | Set getConnectedPeerIDs(); 23 | 24 | /** 25 | * 26 | * @param peerID peerID 27 | * @return connection type of existing connection with given peer 28 | * @throws ASAPException if no connection to that peer exists 29 | */ 30 | ASAPEncounterConnectionType getConnectionType(CharSequence peerID) throws ASAPException; 31 | 32 | /** 33 | * Add a peerID to what no connection should be established. 34 | * Note: Adding a peer to the deny list does not necessarily terminate an 35 | * existing connection to that peer. 36 | * @param peerID 37 | */ 38 | void addToDenyList(CharSequence peerID); 39 | 40 | /** 41 | * Remove a peerID from deny list 42 | * @param peerID 43 | */ 44 | void removeFromDenyList(CharSequence peerID); 45 | 46 | /** 47 | * Get PeerID set to which no connection should be established 48 | * @return 49 | */ 50 | Set getDenyList(); 51 | 52 | /** 53 | * remove all entries from deny list 54 | */ 55 | void clearDenyList(); 56 | 57 | /** 58 | * Cancel a connection to a peer. This method call does not change the deny list. 59 | * @param peerID 60 | */ 61 | void closeEncounter(CharSequence peerID); 62 | 63 | 64 | /** 65 | * 66 | * @return information about last enocunter of a certain peer (described by its id) 67 | */ 68 | Map getEncounterTime(); 69 | 70 | /** 71 | * Peers exchange data during an encounter. In certain circumstances, ad-hoc networks show a feature that can be 72 | * described es flickering. Devices might have reached communication range. A little shift can lead to connection 73 | * lost. A little move backwards can bring them back in range. Connection establishment is a time-consuming task, 74 | * though. Each encounter manager waits a while before it re-connects to a peer. 75 | * 76 | * @return time in milliseconds (aka 'cool down periode') 77 | */ 78 | long getTimeBeforeReconnect(); 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/apps/testsupport/ASAPTestPeerFS.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.apps.testsupport; 2 | 3 | import net.sharksystem.asap.ASAPPeerFS; 4 | import net.sharksystem.asap.ASAPException; 5 | import net.sharksystem.asap.ASAPEncounterConnectionType; 6 | import net.sharksystem.testhelper.ASAPTesthelper; 7 | import net.sharksystem.utils.Log; 8 | 9 | import java.io.IOException; 10 | import java.net.ServerSocket; 11 | import java.net.Socket; 12 | import java.util.Collection; 13 | 14 | public class ASAPTestPeerFS extends ASAPPeerFS { 15 | private ServerSocket serverSocket = null; 16 | private Socket socket = null; 17 | 18 | public ASAPTestPeerFS(CharSequence peerName, Collection supportedFormats) throws IOException, ASAPException { 19 | this(peerName, "./testPeerFS/" + peerName, supportedFormats); 20 | } 21 | 22 | public ASAPTestPeerFS(CharSequence peerName, CharSequence rootFolder, Collection supportedFormats) 23 | throws IOException, ASAPException { 24 | super(peerName, rootFolder, supportedFormats); 25 | } 26 | 27 | public void startEncounter(int port, ASAPTestPeerFS otherPeer) throws IOException { 28 | this.serverSocket = new ServerSocket(port); 29 | 30 | new Thread(new Runnable() { 31 | @Override 32 | public void run() { 33 | try { 34 | ASAPTestPeerFS.this.socket = ASAPTestPeerFS.this.serverSocket.accept(); 35 | } catch (IOException e) { 36 | Log.writeLog(ASAPTestPeerFS.this,"fatal while waiting for client to connect: " 37 | + e.getLocalizedMessage()); 38 | } 39 | 40 | ASAPTestPeerFS.this.startSession(); 41 | } 42 | }).start(); 43 | 44 | // wait a moment 45 | try { 46 | Thread.sleep(100); 47 | } catch (InterruptedException e) { 48 | } 49 | 50 | otherPeer.connect(port); 51 | } 52 | 53 | private void connect(int port) throws IOException { 54 | this.socket = new Socket("localhost", port); 55 | this.startSession(); 56 | } 57 | 58 | public void stopEncounter(ASAPTestPeerFS otherPeer) throws IOException { 59 | this.socket.close(); 60 | } 61 | 62 | private void startSession() { 63 | new Thread(new Runnable() { 64 | @Override 65 | public void run() { 66 | try { 67 | ASAPTestPeerFS.this.handleConnection( 68 | ASAPTestPeerFS.this.socket.getInputStream(), 69 | ASAPTestPeerFS.this.socket.getOutputStream(), 70 | ASAPEncounterConnectionType.INTERNET); 71 | } catch (IOException | ASAPException e) { 72 | Log.writeLog(ASAPTestPeerFS.this,"fatal while connecting: " + e.getLocalizedMessage()); 73 | } 74 | } 75 | }).start(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/peer/Point2Point2Test2.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.peer; 2 | 3 | import net.sharksystem.utils.testsupport.TestConstants; 4 | import net.sharksystem.utils.testsupport.TestHelper; 5 | import net.sharksystem.asap.ASAPException; 6 | import net.sharksystem.asap.apps.testsupport.ASAPTestPeerFS; 7 | import net.sharksystem.testsupport.StoreReceivedMessages; 8 | import net.sharksystem.utils.Utils; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | import java.io.IOException; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.ArrayList; 15 | import java.util.Collection; 16 | 17 | public class Point2Point2Test2 { 18 | public static final String WORKING_SUB_DIRECTORY = TestConstants.ROOT_DIRECTORY + "point2point2/"; 19 | 20 | /** 21 | * An reenactment of a failed Android test - a subdirectory wasn't created or something.. 22 | * @throws IOException 23 | * @throws ASAPException 24 | * @throws InterruptedException 25 | * 26 | */ 27 | @Test 28 | public void point2point1() throws IOException, ASAPException, InterruptedException { 29 | TestHelper.removeFolder(WORKING_SUB_DIRECTORY); 30 | 31 | String appName = "shark/onlineExampleMessages"; 32 | String uri = "asapExample://uriExample"; 33 | byte[] message = "ASAP example message".getBytes(StandardCharsets.UTF_8); 34 | 35 | String aliceID = TestConstants.ALICE_ID; 36 | String bobID = TestConstants.BOB_ID; 37 | String aliceDirectory = WORKING_SUB_DIRECTORY + "/" + aliceID; 38 | String bobDirectory = WORKING_SUB_DIRECTORY + "/" + bobID; 39 | 40 | Collection formats = new ArrayList<>(); 41 | formats.add(appName); 42 | 43 | ///////////////// PEERS ////////////////////////////////////////////////////////////// 44 | // setup mocked peer / asap application and activity in android 45 | ASAPTestPeerFS aliceSimplePeer = new ASAPTestPeerFS(aliceID, aliceDirectory, formats); 46 | 47 | ASAPTestPeerFS bobSimplePeer = new ASAPTestPeerFS(bobID, bobDirectory, formats); 48 | StoreReceivedMessages bobListener = new StoreReceivedMessages(); 49 | bobSimplePeer.addASAPMessageReceivedListener(appName, bobListener); 50 | 51 | aliceSimplePeer.startEncounter(TestHelper.getPortNumber(), bobSimplePeer); 52 | // give your app a moment to process 53 | Thread.sleep(100); 54 | 55 | // send message 56 | System.out.println("\n>>>>>>>>>>>>>>>>>>> send message #1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); 57 | aliceSimplePeer.sendASAPMessage(appName, uri, message); 58 | Thread.sleep(100); 59 | //Thread.sleep(Long.MAX_VALUE); 60 | 61 | Assert.assertEquals(1, bobListener.messageList.size()); 62 | 63 | byte[] messageReceived = bobListener.messageList.get(0).getMessages().next(); 64 | Assert.assertTrue(Utils.compareArrays(messageReceived, message)); 65 | 66 | aliceSimplePeer.stopEncounter(bobSimplePeer); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/StreamPairImpl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | public class StreamPairImpl extends StreamPairListenerManager implements StreamPair { 8 | private final InputStream is; 9 | private final OutputStream os; 10 | private final CharSequence sessionID; 11 | private CharSequence endpointID; 12 | 13 | public static final String NO_ID = "no ID"; 14 | 15 | public static StreamPair getStreamPair(InputStream is, OutputStream os) { 16 | return new StreamPairImpl(is, os, NO_ID, NO_ID); 17 | } 18 | 19 | public static StreamPair getStreamPair(InputStream is, OutputStream os, 20 | CharSequence sessionID, CharSequence endpointAddress) { 21 | return new StreamPairImpl(is, os, sessionID, endpointAddress); 22 | } 23 | 24 | public static StreamPair getStreamPairWithSessionID(InputStream is, OutputStream os, CharSequence sessionID) { 25 | return new StreamPairImpl(is, os, sessionID, NO_ID); 26 | } 27 | 28 | public static StreamPair getStreamPairWithEndpointAddress(InputStream is, OutputStream os, 29 | CharSequence endpointAddress) { 30 | return new StreamPairImpl(is, os, NO_ID, endpointAddress); 31 | } 32 | 33 | /** 34 | * Create a pair of in- and output streams (most probably a kind of socket) with an ID. This ID should be 35 | * an endpoint address, e.g. a mac address, IP/TCP - address. It could also be an ASAP peer ID. 36 | * @param is 37 | * @param os 38 | * @param sessionID a session id 39 | */ 40 | private StreamPairImpl(InputStream is, OutputStream os, CharSequence sessionID, CharSequence endpointAddress) { 41 | this.sessionID = sessionID; 42 | this.endpointID = endpointAddress; 43 | this.is = is; 44 | this.os = os; 45 | } 46 | 47 | @Override 48 | public InputStream getInputStream() { return this.is; } 49 | 50 | @Override 51 | public OutputStream getOutputStream() { return this.os; } 52 | 53 | public CharSequence getSessionID() { 54 | return this.sessionID; 55 | } 56 | 57 | public CharSequence getEndpointID() { 58 | return this.endpointID; 59 | } 60 | 61 | public void setEndpointID(CharSequence endpointID) { 62 | this.endpointID = endpointID; 63 | } 64 | 65 | @Override 66 | public void close() { 67 | try { 68 | is.close(); 69 | } catch (IOException e) { 70 | // ignore 71 | } 72 | try { 73 | os.close(); 74 | } catch (IOException e) { 75 | // ignore 76 | } 77 | 78 | if(this.sessionID != null) this.notifyAllListenerClosed(this, this.sessionID.toString()); 79 | } 80 | 81 | public String toString() { 82 | return "StreamPair (session: " + this.sessionID + " | endpoint: " + this.endpointID + ")"; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/ASAPChannelContentChangedListenerManager.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager; 2 | 3 | import net.sharksystem.asap.*; 4 | 5 | import java.util.HashMap; 6 | 7 | public class ASAPChannelContentChangedListenerManager implements ASAPChannelContentChangedListenerManagement { 8 | private HashMap> listenerMap = 9 | new HashMap(); 10 | 11 | @Override 12 | public void addASAPChannelContentChangedListener(CharSequence format, ASAPChannelContentChangedListener listener) { 13 | GenericListenerImplementation listenerList = this.listenerMap.get(format); 14 | if(listenerList == null) { 15 | listenerList = new GenericListenerImplementation(); 16 | this.listenerMap.put(format, listenerList); 17 | } 18 | 19 | listenerList.addListener(listener); 20 | } 21 | 22 | @Override 23 | public void removeASAPChannelContentChangedListener(CharSequence format, ASAPChannelContentChangedListener listener) { 24 | GenericListenerImplementation listenerList = this.listenerMap.get(format); 25 | if(listenerList != null) { 26 | listenerList.removeListener(listener); 27 | } 28 | } 29 | 30 | public int getNumberListener() { 31 | if(this.listenerMap == null) return 0; 32 | return this.listenerMap.size(); 33 | } 34 | 35 | public void removeAllListeners() { 36 | // reset 37 | this.listenerMap = new HashMap(); 38 | } 39 | 40 | public void notifyChanged(CharSequence format, CharSequence uri, int era) { 41 | 42 | this.notifyChanged(format, uri, era, false); 43 | } 44 | 45 | public void notifyChanged(CharSequence format, CharSequence uri, int era, boolean useThreads) { 46 | 47 | GenericListenerImplementation listenerList = this.listenerMap.get(format); 48 | if(listenerList != null) { 49 | ASAPChannelContentChangedListenerManager.ASAPChannelContentChangedNotifier notifier 50 | = new ASAPChannelContentChangedListenerManager.ASAPChannelContentChangedNotifier(format, uri, era); 51 | 52 | listenerList.notifyAll(notifier, useThreads); 53 | } 54 | } 55 | 56 | public class ASAPChannelContentChangedNotifier implements GenericNotifier { 57 | private CharSequence format; 58 | private CharSequence uri; 59 | private int era; 60 | 61 | ASAPChannelContentChangedNotifier(CharSequence format, CharSequence uri, int era) { 62 | this.format = format; 63 | this.uri = uri; 64 | this.era = era; 65 | } 66 | 67 | public void doNotify(ASAPChannelContentChangedListener listener) { 68 | listener.asapChannelContentChanged(this.format, this.uri, this.era); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPPeer.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.SharkException; 4 | import net.sharksystem.asap.crypto.ASAPKeyStore; 5 | import net.sharksystem.fs.ExtraData; 6 | 7 | import java.io.IOException; 8 | 9 | public interface ASAPPeer extends 10 | ASAPMessageSender, 11 | ASAPEnvironmentChangesListenerManagement, 12 | ASAPMessageReceivedListenerManagement, 13 | ASAPChannelContentChangedListenerManagement 14 | { 15 | CharSequence UNKNOWN_USER = "anon"; 16 | boolean ONLINE_EXCHANGE_DEFAULT = true; 17 | 18 | CharSequence getPeerID(); 19 | 20 | ASAPStorage getASAPStorage(CharSequence format) throws IOException, ASAPException; 21 | 22 | /** 23 | * Provide an ASAP keystore to the peer. Now, point-to-point encryption is possible. 24 | * @param asapKeyStore 25 | */ 26 | void setASAPKeyStore(ASAPKeyStore asapKeyStore); 27 | 28 | /** 29 | * Get ASAP keystore. Throws an exception if not set 30 | * @return ASAP keystore 31 | * @throws ASAPException if no keystore present 32 | */ 33 | ASAPKeyStore getASAPKeyStore() throws ASAPSecurityException; 34 | 35 | /** 36 | * ASAP peer are both: application host and potential router. Routing can be switched on or off. 37 | * We tend to label this behaviour with different ages (stone. bronze, internet), see discussion there. 38 | *

39 | * If true: This engine will route received messages 40 | * @return 41 | */ 42 | boolean isASAPRoutingAllowed(CharSequence applicationFormat) throws IOException, ASAPException; 43 | 44 | void setASAPRoutingAllowed(CharSequence applicationFormat, boolean allowed) 45 | throws IOException, ASAPException; 46 | 47 | /** 48 | * Returns true if both peer represent the same peer - ID are compared. 49 | * @param otherPeer 50 | * @return 51 | */ 52 | boolean samePeer(ASAPPeer otherPeer); 53 | 54 | boolean samePeer(CharSequence otherPeerID); 55 | 56 | /** 57 | * Handle a connection 58 | * @param is 59 | * @param os 60 | * @param encrypt point-to-point encryption 61 | * @param sign point-to-point signing / verifying ? 62 | * @param connectionType 63 | * @return 64 | * @throws IOException 65 | * @throws ASAPException 66 | */ 67 | /* 68 | ASAPConnection handleConnection(InputStream is, OutputStream os, boolean encrypt, boolean sign, 69 | EncounterConnectionType connectionType) throws IOException, ASAPException; 70 | 71 | 72 | */ 73 | 74 | /** 75 | * Make a value persistent with key 76 | * @param key 77 | * @param value 78 | */ 79 | void putExtra(CharSequence key, byte[] value) throws IOException, ASAPException; 80 | 81 | /** 82 | * Return a value. Throws an exception if not set 83 | * @param key 84 | * @throws ASAPException key never used in putExtra 85 | */ 86 | byte[] getExtra(CharSequence key) throws ASAPException, IOException; 87 | 88 | ExtraData getExtraData() throws SharkException, IOException; 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/apps/testsupport/TestASAPConnectionHandler.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.apps.testsupport; 2 | 3 | import net.sharksystem.asap.ASAPConnectionHandler; 4 | import net.sharksystem.asap.ASAPException; 5 | import net.sharksystem.asap.ASAPEncounterConnectionType; 6 | import net.sharksystem.asap.protocol.ASAPConnection; 7 | import net.sharksystem.asap.protocol.ASAPConnectionListener; 8 | import net.sharksystem.asap.protocol.ASAPOnlineMessageSource; 9 | import net.sharksystem.utils.Log; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.util.Set; 15 | 16 | public class TestASAPConnectionHandler implements ASAPConnectionHandler { 17 | @Override 18 | public ASAPConnection handleConnection(InputStream is, OutputStream os, boolean encrypt, boolean sign, Set appsWhiteList, Set appsBlackList) throws IOException, ASAPException { 19 | return this.dummyHandleConnection(); 20 | } 21 | 22 | @Override 23 | public ASAPConnection handleConnection(InputStream is, OutputStream os, boolean encrypt, boolean sign, ASAPEncounterConnectionType connectionType, Set appsWhiteList, Set appsBlackList) throws IOException, ASAPException { 24 | return this.dummyHandleConnection(); 25 | } 26 | 27 | @Override 28 | public ASAPConnection handleConnection(InputStream is, OutputStream os) throws IOException, ASAPException { 29 | return this.dummyHandleConnection(); 30 | } 31 | 32 | @Override 33 | public ASAPConnection handleConnection(InputStream inputStream, OutputStream outputStream, ASAPEncounterConnectionType connectionType) throws IOException, ASAPException { 34 | return this.dummyHandleConnection(); 35 | } 36 | 37 | private ASAPConnection dummyHandleConnection() { 38 | Log.writeLog(this, "handleConnection"); 39 | return new ASAPConnection() { 40 | @Override 41 | public CharSequence getEncounteredPeer() { 42 | return "dummy"; 43 | } 44 | 45 | @Override 46 | public void addOnlineMessageSource(ASAPOnlineMessageSource source) { 47 | 48 | } 49 | 50 | @Override 51 | public void removeOnlineMessageSource(ASAPOnlineMessageSource source) { 52 | 53 | } 54 | 55 | @Override 56 | public void addASAPConnectionListener(ASAPConnectionListener asapConnectionListener) { 57 | 58 | } 59 | 60 | @Override 61 | public void removeASAPConnectionListener(ASAPConnectionListener asapConnectionListener) { 62 | 63 | } 64 | 65 | @Override 66 | public boolean isSigned() { 67 | return false; 68 | } 69 | 70 | @Override 71 | public ASAPEncounterConnectionType getASAPEncounterConnectionType() { 72 | return ASAPEncounterConnectionType.UNKNOWN; 73 | } 74 | 75 | @Override 76 | public void kill() { 77 | 78 | } 79 | }; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/crypto/CryptoUsage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.crypto; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.ASAPSecurityException; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.io.IOException; 10 | import java.security.NoSuchAlgorithmException; 11 | 12 | import static net.sharksystem.utils.testsupport.TestConstants.ALICE_ID; 13 | import static net.sharksystem.utils.testsupport.TestConstants.BOB_ID; 14 | 15 | public class CryptoUsage { 16 | private InMemoASAPKeyStore aliceKeyStorage; 17 | private InMemoASAPKeyStore bobKeyStorage; 18 | 19 | @Before 20 | public void setupASAPKeyStores() throws ASAPSecurityException { 21 | this.aliceKeyStorage = new InMemoASAPKeyStore(ALICE_ID); 22 | this.bobKeyStorage = new InMemoASAPKeyStore(BOB_ID); 23 | 24 | // simulate key exchange 25 | this.aliceKeyStorage.addKeyPair(BOB_ID, bobKeyStorage.getKeyPair()); 26 | this.bobKeyStorage.addKeyPair(ALICE_ID, aliceKeyStorage.getKeyPair()); 27 | } 28 | 29 | @Test 30 | public void printFingerprint() throws ASAPSecurityException, NoSuchAlgorithmException { 31 | System.out.println(ASAPCryptoAlgorithms.getFingerprint(aliceKeyStorage.getPublicKey())); 32 | } 33 | 34 | 35 | @Test 36 | public void signVerifyUsage() throws ASAPSecurityException { 37 | String messageString = "From Alice signed"; 38 | // produce bytes 39 | byte[] messageBytes = messageString.getBytes(); 40 | // alice signs a message 41 | byte[] signedMessage = ASAPCryptoAlgorithms.sign(messageBytes, aliceKeyStorage); 42 | 43 | // verify 44 | Assert.assertTrue(ASAPCryptoAlgorithms.verify(messageBytes, signedMessage, ALICE_ID, bobKeyStorage)); 45 | } 46 | 47 | @Test 48 | public void encryptedPackageUsageExample() throws IOException, ASAPException { 49 | String messageString = "From Alice, encrypted for Bob"; 50 | // produce bytes 51 | byte[] messageBytes = messageString.getBytes(); 52 | 53 | // produce encryption package: encrypt with new session key, encrypt session key with receivers public key 54 | byte[] encryptedMessagePackageBytes = ASAPCryptoAlgorithms.produceEncryptedMessagePackage( 55 | messageBytes, // message that is encrypted 56 | BOB_ID, // recipient id 57 | aliceKeyStorage // key store sender 58 | ); 59 | 60 | // package is sent e.g. with ASAP 61 | byte[] receivedEncryptedPackageBytes = encryptedMessagePackageBytes; 62 | 63 | // receiver creates package from byte[] - will fail if we are not recipient 64 | ASAPCryptoAlgorithms.EncryptedMessagePackage receivedEncryptedPackage = 65 | ASAPCryptoAlgorithms.parseEncryptedMessagePackage(receivedEncryptedPackageBytes); 66 | 67 | // decrypt message 68 | byte[] receivedMessageBytes = 69 | ASAPCryptoAlgorithms.decryptPackage(receivedEncryptedPackage, bobKeyStorage); 70 | 71 | // must be the same 72 | Assert.assertArrayEquals(messageBytes, receivedMessageBytes); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/engine/ChunkCacheTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.ASAPMessages; 5 | import net.sharksystem.fs.FSUtils; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.io.IOException; 10 | 11 | public class ChunkCacheTests { 12 | public static final String ALICE_FOLDER = "tests/alice"; 13 | public static final String ALICE = "alice"; 14 | public static final String BOB = "bob"; 15 | public static final String CLARA = "clara"; 16 | public static final String DUMMY_USER = "dummyUser"; 17 | public static final String TEST_URI = "TEST_URI"; 18 | public static final String TEST_FORMAT = "format"; 19 | public static final String TEST_APP = "asapApp"; 20 | private static final String MESSAGE_ONE = "message one"; 21 | private static final String MESSAGE_TWO = "message two"; 22 | private static final String MESSAGE_THREE = "message three"; 23 | private static final String MESSAGE_FOUR = "message four"; 24 | private static final String MESSAGE_FIVE = "message five"; 25 | 26 | @Test 27 | public void chunkTest0() throws IOException, ASAPException { 28 | FSUtils.removeFolder(ALICE_FOLDER); // clean previous version before 29 | ASAPEngine aliceStorage = ASAPEngineFS.getASAPStorage(ALICE, ALICE_FOLDER, TEST_APP); 30 | 31 | String[] message = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; 32 | 33 | aliceStorage.add(TEST_URI, message[0]); 34 | aliceStorage.add(TEST_URI, message[1]); 35 | aliceStorage.add(TEST_URI, message[2]); 36 | aliceStorage.add(TEST_URI, message[3]); 37 | aliceStorage.newEra(); 38 | aliceStorage.add(TEST_URI, message[4]); 39 | aliceStorage.add(TEST_URI, message[5]); 40 | aliceStorage.newEra(); 41 | aliceStorage.add(TEST_URI, message[6]); 42 | aliceStorage.add(TEST_URI, message[7]); 43 | aliceStorage.newEra(); 44 | aliceStorage.add(TEST_URI, message[8]); 45 | aliceStorage.add(TEST_URI, message[9]); 46 | aliceStorage.add(TEST_URI, message[0xA]); 47 | aliceStorage.newEra(); 48 | aliceStorage.add(TEST_URI, message[0xB]); 49 | aliceStorage.add(TEST_URI, message[0xC]); 50 | aliceStorage.add(TEST_URI, message[0xD]); 51 | aliceStorage.newEra(); 52 | aliceStorage.add(TEST_URI, message[0xE]); 53 | aliceStorage.add(TEST_URI, message[0xF]); 54 | 55 | // now get message chain 56 | ASAPMessages chunkChain = aliceStorage.getChunkChain(TEST_URI); 57 | for(int i = 0; i < 0x10; i++) { 58 | Assert.assertTrue(chunkChain.getMessageAsCharSequence(i, true).toString() 59 | .equalsIgnoreCase(message[i])); 60 | } 61 | 62 | // now get message chain - reverse order 63 | chunkChain = aliceStorage.getChunkChain(TEST_URI); 64 | for(int i = 0; i < 0x10; i++) { 65 | Assert.assertTrue(chunkChain.getMessageAsCharSequence(i, false).toString() 66 | .equalsIgnoreCase(message[0xF - i])); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPListenerManagingPeer.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.asap.listenermanager.ASAPChannelContentChangedListenerManager; 4 | import net.sharksystem.asap.listenermanager.ASAPEnvironmentChangesListenerManager; 5 | import net.sharksystem.asap.listenermanager.ASAPMessageReceivedListenerManager; 6 | 7 | public abstract class ASAPListenerManagingPeer implements ASAPPeer { 8 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 9 | // ASAPMessageReceivedListener // 10 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | protected ASAPMessageReceivedListenerManager asapMessageReceivedListenerManager = 12 | new ASAPMessageReceivedListenerManager(); 13 | 14 | public void addASAPMessageReceivedListener(CharSequence format, ASAPMessageReceivedListener listener) { 15 | this.asapMessageReceivedListenerManager.addASAPMessageReceivedListener(format, listener); 16 | } 17 | 18 | @Override 19 | public void removeASAPMessageReceivedListener(CharSequence format, ASAPMessageReceivedListener listener) { 20 | this.asapMessageReceivedListenerManager.removeASAPMessageReceivedListener(format, listener); 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 24 | // ASAPChannelContentChangedListener // 25 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 26 | protected ASAPChannelContentChangedListenerManager asapChannelContentChangedListenerManager = 27 | new ASAPChannelContentChangedListenerManager(); 28 | 29 | @Override 30 | public void addASAPChannelContentChangedListener(CharSequence format, ASAPChannelContentChangedListener listener) { 31 | this.asapChannelContentChangedListenerManager.addASAPChannelContentChangedListener(format, listener); 32 | } 33 | 34 | @Override 35 | public void removeASAPChannelContentChangedListener(CharSequence format, ASAPChannelContentChangedListener listener) { 36 | this.asapChannelContentChangedListenerManager.removeASAPChannelContentChangedListener(format, listener); 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 40 | // ASAPEnvironmentChangesListener // 41 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 42 | 43 | protected ASAPEnvironmentChangesListenerManager environmentChangesListenerManager = 44 | new ASAPEnvironmentChangesListenerManager(); 45 | 46 | @Override 47 | public void addASAPEnvironmentChangesListener(ASAPEnvironmentChangesListener changesListener) { 48 | this.environmentChangesListenerManager.addASAPEnvironmentChangesListener(changesListener); 49 | } 50 | 51 | @Override 52 | public void removeASAPEnvironmentChangesListener(ASAPEnvironmentChangesListener changesListener) { 53 | this.environmentChangesListenerManager.removeASAPEnvironmentChangesListener(changesListener); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/StreamLink.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | import net.sharksystem.asap.ASAP; 4 | import net.sharksystem.asap.utils.ASAPSerialization; 5 | import net.sharksystem.utils.Log; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | 11 | public class StreamLink extends Thread { 12 | private final InputStream sourceIS; 13 | private final OutputStream targetOS; 14 | private final boolean closeStreams; 15 | private String id = "anon"; 16 | 17 | public StreamLink(InputStream sourceIS, OutputStream targetOS, boolean closeStreams, String id) { 18 | this.sourceIS = sourceIS; 19 | this.targetOS = targetOS; 20 | this.closeStreams = closeStreams; 21 | this.id = id; 22 | } 23 | 24 | StreamLink(InputStream sourceIS, OutputStream targetOS, boolean closeStreams) { 25 | this(sourceIS, targetOS, closeStreams, "no id"); 26 | } 27 | 28 | public void close() { 29 | this.again = false; 30 | } 31 | 32 | private boolean again = true; 33 | 34 | public void run() { 35 | Log.writeLog(this, this.toString(), "start read/write loop"); 36 | try { 37 | int singleReadCounter = 0; 38 | int read = -1; 39 | do { 40 | int available = sourceIS.available(); 41 | //Log.writeLog(this, this.toString(), "available: " + available); 42 | if (available > 0) { 43 | singleReadCounter = 0; 44 | byte[] buffer = new byte[available]; 45 | sourceIS.read(buffer); 46 | targetOS.write(buffer); 47 | //Log.writeLog(this, this.toString(), ASAPSerialization.printByteArrayToString(buffer) 48 | // + " end buffer:\n"); 49 | } else { 50 | // block 51 | //Log.writeLog(this, this.toString(), "going to block in read(): "); 52 | read = sourceIS.read(); 53 | if(read != -1) { 54 | /* 55 | Log.writeLog(this, this.toString(), "read != -1 (" + ++singleReadCounter + ")"); 56 | Log.writeLog(this, this.toString(), ASAPSerialization.printByteToString((short) read) 57 | + " end byte"); 58 | */ 59 | targetOS.write(read); 60 | } else { 61 | //Log.writeLog(this, this.toString(), "read -1 - end"); 62 | again = false; 63 | } 64 | } 65 | } while (again); 66 | } catch (IOException e) { 67 | Log.writeLog(this, this.toString(), "ioException - most probably connection closed: " + id); 68 | } finally { 69 | if(this.closeStreams) { 70 | Log.writeLog(this, this.toString(), "try closing linked streams: " + id); 71 | try {this.targetOS.close();} 72 | catch (IOException ioException) { Log.writeLog(this, this.toString(), "failed close input stream: " + id); } 73 | try {this.sourceIS.close();} 74 | catch (IOException ioException) { Log.writeLog(this, this.toString(), "failed close output stream: " + id); } 75 | } 76 | 77 | Log.writeLog(this, this.toString(), "end linked streams connection: " + id); 78 | } 79 | } 80 | 81 | public String toString() { 82 | return this.id; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/management/ASAPManagementMessage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager.management; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.protocol.ASAP_1_0; 5 | import net.sharksystem.asap.protocol.ASAP_PDU_1_0; 6 | 7 | import java.io.*; 8 | import java.util.*; 9 | 10 | public class ASAPManagementMessage { 11 | public static byte[] getCreateClosedASAPChannelMessage( 12 | CharSequence owner, CharSequence appName, CharSequence channelUri, 13 | Collection recipients) throws ASAPException, IOException { 14 | 15 | if(recipients == null || recipients.size() < 1) { 16 | throw new ASAPException("recipients in storage/channelUri must not be null or empty: "); 17 | } 18 | 19 | // we have to put format and recipients into an assimilate message. 20 | 21 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 22 | DataOutputStream dos = new DataOutputStream(baos); 23 | 24 | // put owner 25 | dos.writeUTF(owner.toString()); 26 | 27 | // put appName uri 28 | dos.writeUTF(appName.toString()); 29 | 30 | // put channelUri uri 31 | dos.writeUTF(channelUri.toString()); 32 | 33 | // put recipients 34 | for(CharSequence recipient : recipients) { 35 | dos.writeUTF(recipient.toString()); 36 | } 37 | 38 | return baos.toByteArray(); 39 | } 40 | 41 | public static ASAPManagementCreateASAPStorageMessage parseASAPManagementMessage(byte[] message) 42 | throws IOException { 43 | 44 | return new CreateASAPStorageMessage(message); 45 | } 46 | 47 | private static boolean isASAPManagementMessage(ASAP_PDU_1_0 asapPDU) { 48 | return asapPDU.getFormat().equalsIgnoreCase(ASAP_1_0.ASAP_MANAGEMENT_FORMAT); 49 | } 50 | 51 | private static class CreateASAPStorageMessage implements ASAPManagementCreateASAPStorageMessage { 52 | private final Set recipients; 53 | private final CharSequence channelUri; 54 | private final CharSequence appName; 55 | private final CharSequence owner; 56 | 57 | CreateASAPStorageMessage(byte[] message) throws IOException { 58 | 59 | // convert to string 60 | DataInputStream dis = new DataInputStream(new ByteArrayInputStream(message)); 61 | this.owner = dis.readUTF(); 62 | this.appName = dis.readUTF(); 63 | this.channelUri = dis.readUTF(); 64 | 65 | // there must be at least one recipient 66 | this.recipients = new HashSet<>(); 67 | this.recipients.add(dis.readUTF()); 68 | 69 | // and maybe some more 70 | try { 71 | for(;;) { 72 | this.recipients.add(dis.readUTF()); 73 | } 74 | } 75 | catch(Throwable t) { 76 | // reach end - ok 77 | } 78 | } 79 | 80 | @Override 81 | public Set getRecipients() { 82 | return this.recipients; 83 | } 84 | 85 | @Override 86 | public CharSequence getChannelUri() { 87 | return this.channelUri; 88 | } 89 | 90 | @Override 91 | public CharSequence getOwner() { 92 | return this.owner; 93 | } 94 | 95 | @Override 96 | public CharSequence getAppName() { 97 | return this.appName; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPInMemoTransientMessages.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.*; 4 | import net.sharksystem.asap.protocol.ASAP_AssimilationPDU_1_0; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.ArrayList; 9 | import java.util.Iterator; 10 | import java.util.List; 11 | 12 | public class ASAPInMemoTransientMessages implements ASAPMessages, MessagesContainer { 13 | private final CharSequence format; 14 | private final CharSequence uri; 15 | private final CharSequence sender; 16 | private ASAPHop asapHop; 17 | private int size = -1; 18 | private List messageList = new ArrayList<>(); 19 | 20 | ASAPInMemoTransientMessages(ASAP_AssimilationPDU_1_0 pdu, ASAPHop asapHop) { 21 | this.format = pdu.getFormat(); 22 | this.uri = pdu.getChannelUri(); 23 | this.sender = pdu.getSender(); 24 | this.asapHop = asapHop; 25 | } 26 | 27 | public ASAPInMemoTransientMessages(CharSequence format, CharSequence uri, CharSequence sender, ASAPHop asapHop) { 28 | this.format = format; 29 | this.uri = uri; 30 | this.sender = sender; 31 | this.asapHop = asapHop; 32 | } 33 | 34 | @Override 35 | public int size() throws IOException { 36 | return this.messageList.size(); 37 | } 38 | 39 | @Override 40 | public CharSequence getURI() { 41 | return this.uri; 42 | } 43 | 44 | @Override 45 | public CharSequence getFormat() { 46 | return this.format; 47 | } 48 | 49 | public CharSequence getSender() { return this.sender; } 50 | 51 | @Override 52 | public void setASAPHopList(List asapHopList) throws IOException { 53 | if(this.asapHop == null) { 54 | this.asapHop = asapHopList.get(asapHopList.size()-1); 55 | } 56 | } 57 | 58 | @Override 59 | public Iterator getMessagesAsCharSequence() throws IOException { 60 | throw new IOException("not implemented yet"); 61 | } 62 | 63 | @Override 64 | public Iterator getMessages() throws IOException { 65 | return this.messageList.iterator(); 66 | } 67 | 68 | @Override 69 | public CharSequence getMessageAsCharSequence(int position, boolean chronologically) throws ASAPException, IOException { 70 | return new String(this.getMessage(position, chronologically)); 71 | } 72 | 73 | @Override 74 | public byte[] getMessage(int position, boolean chronologically) throws ASAPException, IOException { 75 | if(position > this.messageList.size() || position < 0) 76 | throw new ASAPException("no message on index (out of range): " + position); 77 | 78 | int index = chronologically ? position : this.messageList.size() - position; 79 | 80 | return this.messageList.get(index); 81 | } 82 | 83 | @Override 84 | public ASAPChunk getChunk(int position, boolean chronologically) throws IOException, ASAPException { 85 | throw new ASAPException("transient message are not stored"); 86 | } 87 | 88 | @Override 89 | public void addMessage(InputStream is, long length) throws IOException { 90 | if(length > Integer.MAX_VALUE) throw new IOException("length exceeds range of integer value"); 91 | 92 | byte[] message = new byte[(int)length]; 93 | is.read(message); 94 | 95 | this.addMessage(message); 96 | } 97 | 98 | public void addMessage(byte[] message) throws IOException { 99 | this.messageList.add(message); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/ASAPChannelImpl.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.*; 4 | 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | public class ASAPChannelImpl implements ASAPChannel { 12 | private static final String CHANNEL_OWNER = "ASAPChannelOwner"; 13 | private final ASAPEngine asapEngine; 14 | private final CharSequence uri; 15 | 16 | public ASAPChannelImpl(ASAPEngine asapEngine, CharSequence uri) { 17 | this.asapEngine = asapEngine; 18 | this.uri = uri; 19 | } 20 | 21 | @Override 22 | public CharSequence getOwner() throws IOException { 23 | return this.asapEngine.getExtra(this.getUri(), CHANNEL_OWNER); 24 | } 25 | 26 | @Override 27 | public CharSequence getUri() throws IOException { 28 | return this.uri; 29 | } 30 | 31 | @Override 32 | public Set getRecipients() throws IOException { 33 | return this.asapEngine.getRecipients(this.getUri()); 34 | } 35 | 36 | @Override 37 | public HashMap getExtraData() throws IOException { 38 | return this.asapEngine.getStorage().getChunk(uri, this.asapEngine.getEra()).getExtraData(); 39 | } 40 | 41 | @Override 42 | public void putExtraData(String key, String value) throws IOException { 43 | this.asapEngine.getStorage().getChunk(uri, this.asapEngine.getEra()).putExtra(key, value); 44 | } 45 | 46 | @Override 47 | public void removeExtraData(String key) throws IOException { 48 | this.asapEngine.getStorage().getChunk(uri, this.asapEngine.getEra()).removeExtra(key); 49 | } 50 | 51 | @Override 52 | public ASAPMessages getMessages() throws IOException { 53 | return this.asapEngine.getChunkStorage().getASAPMessages(this.getUri(), this.asapEngine.getEra()); 54 | } 55 | 56 | @Override 57 | public ASAPMessages getMessages(boolean peerOnly) throws IOException, ASAPException { 58 | if(peerOnly) return this.getMessages(); 59 | // else 60 | return this.getMessages(null); 61 | } 62 | 63 | @Override 64 | public ASAPMessages getMessages(ASAPMessageCompare compare) throws IOException, ASAPException { 65 | List messagesSource = new ArrayList<>(); 66 | 67 | // add message from local peer first 68 | messagesSource.add(this.getMessages()); 69 | 70 | // other sender? 71 | List sender = this.asapEngine.getSender(); 72 | for(CharSequence senderID : sender) { 73 | try { 74 | ASAPStorage existingIncomingStorage = this.asapEngine.getExistingIncomingStorage(senderID); 75 | int currentEra = existingIncomingStorage.getEra(); 76 | // want all messages 77 | int beforeCurrentEra = ASAP.previousEra(currentEra); 78 | // currentEra -> direct predecessor 79 | messagesSource.add(existingIncomingStorage.getChunkStorage().getASAPMessages( 80 | this.getUri(), currentEra, beforeCurrentEra)); 81 | } catch (ASAPException e) { 82 | // no such storage - ok ignore and go ahead 83 | } 84 | } 85 | 86 | return new ASAPMessagesMerger(messagesSource, compare); 87 | } 88 | 89 | @Override 90 | public void addMessage(byte[] message) throws IOException { 91 | this.asapEngine.add(this.uri, message); 92 | } 93 | 94 | public void setOwner(CharSequence owner) throws IOException { 95 | this.asapEngine.putExtra(this.getUri(), CHANNEL_OWNER, owner.toString()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/engine/DefaultSecurityAdministrator.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import net.sharksystem.asap.protocol.ASAP_AssimilationPDU_1_0; 4 | import net.sharksystem.asap.protocol.ASAP_PDU_1_0; 5 | import net.sharksystem.asap.crypto.ASAPPoint2PointCryptoSettings; 6 | import net.sharksystem.utils.Log; 7 | 8 | import java.io.IOException; 9 | 10 | public class DefaultSecurityAdministrator implements ASAPCommunicationSetting, 11 | ASAPEnginePermissionSettings, CryptoControl, ASAPPoint2PointCryptoSettings { 12 | 13 | private boolean receivedMessageMustBeEncrypted = false; 14 | private boolean receivedMessagesMustBeSigned = false; 15 | private boolean sendEncrypted = false; 16 | private boolean sendSigned; 17 | 18 | @Override 19 | public void setRememberEncounteredPeers(boolean on) throws IOException { 20 | 21 | } 22 | 23 | @Override 24 | public void setReceivedMessagesMustBeEncrypted(boolean on) throws IOException { 25 | this.receivedMessageMustBeEncrypted = on; 26 | } 27 | 28 | @Override 29 | public void setReceivedMessagesMustBeSigned(boolean on) throws IOException { 30 | this.receivedMessagesMustBeSigned = on; 31 | } 32 | 33 | @Override 34 | public void setSetAllowedRemotePeers(AllowedRemotePeers safetyLevel) { 35 | 36 | } 37 | 38 | @Override 39 | public boolean setRevealEngineFormat(String peerName) { 40 | return false; 41 | } 42 | 43 | @Override 44 | public boolean setSendOpenMessages(String peerName) { 45 | return false; 46 | } 47 | 48 | @Override 49 | public boolean allowedToCreateChannel(ASAP_AssimilationPDU_1_0 asapAssimilationPDU) { 50 | return true; // it is a dummy 51 | } 52 | 53 | @Override 54 | public void setSendEncryptedMessages(boolean on) { 55 | this.sendEncrypted = on; 56 | } 57 | 58 | @Override 59 | public void setSendSignedMessages(boolean on) { 60 | this.sendSigned = on; 61 | } 62 | 63 | @Override 64 | public boolean allowed2Process(ASAP_PDU_1_0 pdu) { 65 | if(this.receivedMessagesMustBeSigned && !pdu.signed()) { 66 | Log.writeLog(this, "checked: " + pdu); 67 | Log.writeLog(this, "not signed"); 68 | return false; 69 | } 70 | if(this.receivedMessageMustBeEncrypted && !pdu.encrypted()) { 71 | Log.writeLog(this, "checked: " + pdu); 72 | Log.writeLog(this, "not encrypted"); 73 | return false; 74 | } 75 | 76 | Log.writeLog(this, "ok"); 77 | return true; 78 | } 79 | 80 | private String getLogStart() { 81 | return this.getClass().getSimpleName() + ": "; 82 | } 83 | 84 | @Override 85 | public boolean mustEncrypt() { 86 | return this.sendEncrypted; 87 | } 88 | 89 | @Override 90 | public boolean mustSign() { 91 | return this.sendSigned; 92 | } 93 | 94 | /* 95 | private boolean encryptedMessagesOnly = false; 96 | private boolean signedMessagesOnly = false; 97 | private boolean sendEncrypted = false; 98 | private boolean sendSigned; 99 | 100 | */ 101 | public String toString() { 102 | StringBuilder sb = new StringBuilder(); 103 | sb.append(this.getLogStart()); 104 | sb.append("recEncrypted: " + this.receivedMessageMustBeEncrypted); 105 | sb.append(" | recSigned: " + this.receivedMessagesMustBeSigned); 106 | sb.append(" | sendEncrypted: " + this.sendEncrypted); 107 | sb.append(" | sendSigned: " + this.sendSigned); 108 | return sb.toString(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /nbbuild.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project ASP3Java. 12 | 13 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/listenermanager/ASAPMessageReceivedListenerManager.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.listenermanager; 2 | 3 | import net.sharksystem.asap.*; 4 | import net.sharksystem.utils.Log; 5 | 6 | import java.io.IOException; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | 10 | public class ASAPMessageReceivedListenerManager implements ASAPMessageReceivedListenerManagement { 11 | private HashMap> listenerMap = 12 | new HashMap(); 13 | 14 | @Override 15 | public void addASAPMessageReceivedListener(CharSequence format, ASAPMessageReceivedListener listener) { 16 | GenericListenerImplementation listenerList = this.listenerMap.get(format); 17 | if(listenerList == null) { 18 | listenerList = new GenericListenerImplementation(); 19 | this.listenerMap.put(format, listenerList); 20 | } 21 | 22 | listenerList.addListener(listener); 23 | } 24 | 25 | @Override 26 | public void removeASAPMessageReceivedListener(CharSequence format, ASAPMessageReceivedListener listener) { 27 | GenericListenerImplementation listenerList = this.listenerMap.get(format); 28 | if(listenerList != null) { 29 | listenerList.removeListener(listener); 30 | } 31 | } 32 | 33 | @Override 34 | public int getNumberListener() { 35 | if(this.listenerMap == null) return 0; 36 | return this.listenerMap.size(); 37 | } 38 | 39 | public void removeAllListeners() { 40 | // reset 41 | this.listenerMap = new HashMap(); 42 | } 43 | 44 | public void notifyReceived(CharSequence format, ASAPMessages asapMessage, 45 | String senderE2E, // E2E part 46 | List asapHopList) { 47 | 48 | this.notifyReceived(format, asapMessage, false, senderE2E, asapHopList); 49 | } 50 | 51 | public void notifyReceived(CharSequence format, ASAPMessages asapMessage, boolean useThreads, 52 | String senderE2E, List asapHopList) { 53 | 54 | GenericListenerImplementation listenerList = this.listenerMap.get(format); 55 | if(listenerList != null) { 56 | ASAPMessageReceivedNotifier asapMessageReceivedNotifier 57 | = new ASAPMessageReceivedNotifier(asapMessage, senderE2E, asapHopList); 58 | 59 | listenerList.notifyAll(asapMessageReceivedNotifier, useThreads); 60 | } 61 | } 62 | 63 | public class ASAPMessageReceivedNotifier implements GenericNotifier { 64 | private final ASAPMessages asapMessage; 65 | private final String senderE2E; 66 | private List asapHopList; 67 | 68 | ASAPMessageReceivedNotifier(ASAPMessages asapMessage, 69 | String senderE2E, 70 | List asapHopList) { 71 | 72 | this.asapMessage = asapMessage; 73 | this.senderE2E = senderE2E; 74 | this.asapHopList = asapHopList; 75 | } 76 | 77 | public void doNotify(ASAPMessageReceivedListener listener) { 78 | try { 79 | //Log.writeLog(this, "notify: " + listener.getClass().getSimpleName()); 80 | listener.asapMessagesReceived(this.asapMessage, this.senderE2E, this.asapHopList); 81 | } catch (IOException e) { 82 | System.err.println("error when notifying about received asap message: " 83 | + e.getLocalizedMessage()); 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPChunk.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import net.sharksystem.asap.engine.MessagesContainer; 4 | 5 | import java.io.IOException; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | /** 10 | * An ASAP chunk is a set of message with the same format, same uri and same era. 11 | * 12 | * Usually, there will be a running ASAP protocol engine that writes received messages into 13 | * chunks and delivers chunks to peers during an encounter 14 | * 15 | * @author thsc 16 | */ 17 | public interface ASAPChunk extends MessagesContainer { 18 | /** 19 | * 20 | * @return number of message in that chunk 21 | */ 22 | int getNumberMessage(); 23 | 24 | /** 25 | * URI of all messages within that chunk 26 | * @return 27 | * @throws IOException 28 | */ 29 | String getUri() throws IOException; 30 | 31 | /** 32 | * 33 | * @return iterator of all messages in the chunk 34 | * @throws IOException 35 | */ 36 | Iterator getMessages() throws IOException; 37 | 38 | /** 39 | * remove that chunk.. drop all object references after 40 | * calling this methods. Further calls on this object 41 | * have an undefined behaviour. 42 | */ 43 | public void drop(); 44 | 45 | /** 46 | * 47 | * @return era all messages in that chunk 48 | * @throws IOException 49 | */ 50 | public int getEra() throws IOException; 51 | 52 | /** 53 | * Handle this methode with great care. 54 | * It will most probable disappear of we find time to re-implement the PKI projects. Until then: 55 | *

56 | * A chunk behaves like an in- or outbox in an mail system. Sometimes a chunk is both. 57 | * An ASAP peer offers a send message which is the preferred and recommended way to send messages. 58 | *

59 | * A peer behaves pretty simple internally. A message that is ought to be sent is stored in 60 | * a chunk with current era. Received messages are stored in chunks as well. There is no need 61 | * to change those chunks from any application. 62 | *

63 | * Peers can also be asked to re-deliver received messages. In that case, an application could think 64 | * of managing such chunks to drop message (usually whole chunks) which are not meant to be 65 | * re-delivered. Actually, it is not a really good idea. But we did. And we haven't not yet found time 66 | * to take it out. And we need a running public key infrastructure. 67 | *

68 | * Furthermore, an ASAP peer keeps a list of its encounters. It can figure out the last era 69 | * in which another peer was met. If they have never met before the initial era would be taken in 70 | * its place. 71 | *

72 | * Now, default behaviour of a peer is to transmit all messages from this last-met-era 73 | * until current era. 74 | *

75 | * Sometimes, it seemed to be efficient to manipulate those chunks to slightly change the set 76 | * of delivered messages. I our decentralized PKI, we decided to implement the certificate 77 | * storage as decorator of a chunk storage. That's actually quite efficient but requires 78 | * a lot of explaining and hinders us to change the internal structure. 79 | *

80 | * Unfortunately, it forced us to make this feature public. And here we are. You can add a message 81 | * to a chunk. You should not do it until you are absolutely sure what you are doing. 82 | * It is something of a hack. Efficient yet but not good. 83 | * 84 | * @param messageAsBytes 85 | * @throws IOException 86 | * @see net.sharksystem.asap.ASAPPeer#sendASAPMessage(CharSequence, CharSequence, byte[]) 87 | */ 88 | void addMessage(byte[] messageAsBytes) throws IOException; 89 | 90 | List getASAPHopList(); 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/serialization/SerializationTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.serialization; 2 | 3 | import net.sharksystem.asap.ASAPHopImpl; 4 | import net.sharksystem.asap.ASAPException; 5 | import net.sharksystem.asap.ASAPHop; 6 | import net.sharksystem.asap.ASAPEncounterConnectionType; 7 | import net.sharksystem.asap.utils.ASAPSerialization; 8 | import net.sharksystem.asap.utils.PeerIDHelper; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.io.ByteArrayOutputStream; 14 | import java.io.IOException; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | public class SerializationTests { 20 | @Test 21 | public void test1() throws IOException, ASAPException { 22 | String messageIn = "I am Alice"; 23 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 24 | ASAPSerialization.writeCharSequenceParameter(messageIn, baos); 25 | 26 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 27 | String messageOut = ASAPSerialization.readCharSequenceParameter(bais); 28 | 29 | Assert.assertTrue(messageOut.equals(messageIn)); 30 | } 31 | 32 | @Test 33 | public void serializeASAPHopListLen1() throws IOException, ASAPException { 34 | List exampleList = new ArrayList<>(); 35 | 36 | exampleList.add(new ASAPHopImpl("Alice", true, true, ASAPEncounterConnectionType.ASAP_HUB)); 37 | 38 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 39 | ASAPSerialization.writeASAPHopList(exampleList, baos); 40 | 41 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 42 | List receivedList = ASAPSerialization.readASAPHopList(bais); 43 | 44 | Assert.assertEquals(exampleList.size(), receivedList.size()); 45 | for(int i = 0; i < exampleList.size(); i++) { 46 | ASAPHop origHop = exampleList.get(i); 47 | ASAPHop receivedHop = receivedList.get(i); 48 | 49 | Assert.assertTrue(PeerIDHelper.sameID(origHop.sender(), receivedHop.sender())); 50 | Assert.assertTrue(origHop.verified() == receivedHop.verified()); 51 | Assert.assertTrue(origHop.encrypted() == receivedHop.encrypted()); 52 | Assert.assertTrue(origHop.getConnectionType() == receivedHop.getConnectionType()); 53 | } 54 | } 55 | 56 | @Test 57 | public void serializeASAPHopListLen5() throws IOException, ASAPException { 58 | List exampleList = new ArrayList<>(); 59 | 60 | exampleList.add(new ASAPHopImpl("Alice", true, true, ASAPEncounterConnectionType.ASAP_HUB)); 61 | exampleList.add(new ASAPHopImpl("Bob", false, true, ASAPEncounterConnectionType.INTERNET)); 62 | exampleList.add(new ASAPHopImpl("Clara", false, false, ASAPEncounterConnectionType.AD_HOC_LAYER_2_NETWORK)); 63 | exampleList.add(new ASAPHopImpl("David", true, false, ASAPEncounterConnectionType.ONION_NETWORK)); 64 | exampleList.add(new ASAPHopImpl("Eveline", true, false, ASAPEncounterConnectionType.UNKNOWN)); 65 | 66 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 67 | ASAPSerialization.writeASAPHopList(exampleList, baos); 68 | 69 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 70 | List receivedList = ASAPSerialization.readASAPHopList(bais); 71 | 72 | Assert.assertEquals(exampleList.size(), receivedList.size()); 73 | for(int i = 0; i < exampleList.size(); i++) { 74 | ASAPHop origHop = exampleList.get(i); 75 | ASAPHop receivedHop = receivedList.get(i); 76 | 77 | Assert.assertTrue(PeerIDHelper.sameID(origHop.sender(), receivedHop.sender())); 78 | Assert.assertTrue(origHop.verified() == receivedHop.verified()); 79 | Assert.assertTrue(origHop.encrypted() == receivedHop.encrypted()); 80 | Assert.assertTrue(origHop.getConnectionType() == receivedHop.getConnectionType()); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/streams/StreamPairWrapper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils.streams; 2 | 3 | import net.sharksystem.utils.Log; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | 9 | public class StreamPairWrapper extends StreamPairListenerManager implements StreamPair { 10 | private final InputStreamWrapper is; 11 | private final OutputStreamWrapper os; 12 | private String endpointAddress; 13 | 14 | /** 15 | * @param is 16 | * @param os 17 | * @param listener 18 | * @param endpointAddress 19 | */ 20 | public StreamPairWrapper(InputStream is, OutputStream os, WrappedStreamPairListener listener, String endpointAddress) { 21 | this.is = new InputStreamWrapper(is); 22 | this.os = new OutputStreamWrapper(os); 23 | this.endpointAddress = endpointAddress; 24 | super.addListener(listener); 25 | 26 | } 27 | 28 | public StreamPairWrapper(InputStream is, OutputStream os) { 29 | this(is, os, null, "0"); 30 | } 31 | 32 | @Override 33 | public InputStream getInputStream() { 34 | return this.is; 35 | } 36 | 37 | @Override 38 | public OutputStream getOutputStream() { 39 | return this.os; 40 | } 41 | 42 | @Override 43 | public void close() { 44 | // do not close the streams but prevent any further communication 45 | Log.writeLog(this, "closed"); 46 | this.is.closed = true; 47 | this.os.closed = true; 48 | this.notifyAllListenerClosed(this, this.endpointAddress); 49 | /* 50 | if(!this.listenerList.isEmpty()) { 51 | for(WrappedStreamPairListener listener : this.listenerList) { 52 | listener.notifyClosed(this.id); 53 | } 54 | } 55 | */ 56 | } 57 | 58 | @Override 59 | public CharSequence getEndpointID() { 60 | return this.endpointAddress; 61 | } 62 | 63 | @Override 64 | public void setEndpointID(CharSequence peerID) { 65 | this.endpointAddress = peerID.toString(); 66 | } 67 | 68 | @Override 69 | public CharSequence getSessionID() { 70 | return StreamPairImpl.NO_ID; 71 | } 72 | 73 | private void notifyAction() { 74 | if(!this.listenerList.isEmpty()) { 75 | for(StreamPairListener listener : this.listenerList) { 76 | if(listener instanceof WrappedStreamPairListener) { 77 | WrappedStreamPairListener wrappedListener = (WrappedStreamPairListener) listener; 78 | wrappedListener.notifyAction(this.endpointAddress); 79 | } 80 | } 81 | } 82 | } 83 | 84 | public void addListener(WrappedStreamPairListener listener) { 85 | 86 | } 87 | 88 | private class InputStreamWrapper extends InputStream { 89 | private final InputStream is; 90 | private boolean closed = false; 91 | 92 | InputStreamWrapper(InputStream is) { 93 | this.is = is; 94 | } 95 | 96 | @Override 97 | public int read() throws IOException { 98 | if(this.closed) throw new IOException("wrapped stream closed"); 99 | int i = this.is.read(); 100 | if(this.closed) { 101 | Log.writeLog(this, "read sign after already closed " + i); 102 | throw new IOException("wrapped stream closed"); 103 | } 104 | // else 105 | StreamPairWrapper.this.notifyAction(); 106 | return i; 107 | } 108 | 109 | public void close() { 110 | StreamPairWrapper.this.close(); 111 | } 112 | } 113 | 114 | private class OutputStreamWrapper extends OutputStream { 115 | private final OutputStream os; 116 | private boolean closed = false; 117 | 118 | OutputStreamWrapper(OutputStream os) { 119 | this.os = os; 120 | } 121 | 122 | @Override 123 | public void write(int value) throws IOException { 124 | if(this.closed) throw new IOException("wrapped stream closed"); 125 | this.os.write(value); 126 | StreamPairWrapper.this.notifyAction(); 127 | } 128 | 129 | public void close() { 130 | StreamPairWrapper.this.close(); 131 | } 132 | } 133 | 134 | public String toString() { 135 | return this.endpointAddress; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/utils/SerializationHelper.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.utils; 2 | 3 | import net.sharksystem.asap.ASAPException; 4 | import net.sharksystem.asap.utils.ASAPSerialization; 5 | 6 | import java.io.*; 7 | import java.util.*; 8 | 9 | public class SerializationHelper { 10 | public static final String SERIALIZATION_DELIMITER = "|||"; 11 | 12 | public static String collection2String(Collection stringList) { 13 | StringBuilder sb = new StringBuilder(); 14 | 15 | boolean first = true; 16 | if(stringList != null && !stringList.isEmpty()) { 17 | for(CharSequence s : stringList) { 18 | if(s != null) { 19 | if(!first) { 20 | sb.append(SERIALIZATION_DELIMITER); 21 | } else { 22 | first = false; 23 | } 24 | sb.append(s); 25 | } 26 | } 27 | } 28 | 29 | return sb.toString(); 30 | } 31 | 32 | public static byte[] long2byteArray(long value) { 33 | int numberBytes = Long.SIZE / 8; // A byte has 8 bits :) Fascinating, Captain. 34 | byte[] result = new byte[numberBytes]; 35 | 36 | for(int index = numberBytes-1; index >= 0; index--) { 37 | long mask = 0xFF; 38 | // shift mask 39 | mask = mask << index; 40 | 41 | // mask all but indexed byte 42 | long resultLong = value & mask; 43 | 44 | // shift result to first byte 45 | resultLong = resultLong >> index; 46 | 47 | // take first byte only 48 | result[index] = (byte)resultLong; 49 | } 50 | 51 | return result; 52 | } 53 | 54 | public static byte[] characterSequence2bytes(CharSequence cs) throws IOException { 55 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 56 | ASAPSerialization.writeCharSequenceParameter(cs, baos); 57 | return baos.toByteArray(); 58 | } 59 | 60 | public static CharSequence bytes2characterSequence(byte[] bytes) throws IOException, ASAPException { 61 | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 62 | return ASAPSerialization.readCharSequenceParameter(bais); 63 | } 64 | 65 | public static List string2CharSequenceList(String s) { 66 | StringTokenizer t = new StringTokenizer(s, SERIALIZATION_DELIMITER); 67 | ArrayList list = new ArrayList(); 68 | 69 | while(t.hasMoreTokens()) { 70 | list.add(t.nextToken()); 71 | } 72 | 73 | return list; 74 | } 75 | 76 | public static Set string2CharSequenceSet(String s) { 77 | List charSequences = SerializationHelper.string2CharSequenceList(s); 78 | Set charSet = new HashSet(); 79 | for(CharSequence c : charSequences) { 80 | charSet.add(c); 81 | } 82 | 83 | return charSet; 84 | } 85 | 86 | public static ArrayList set2arraylist(Set aSet) { 87 | ArrayList aList = new ArrayList(); 88 | if(aSet == null) return aList; 89 | 90 | for(Object o : aSet) { 91 | aList.add(o); 92 | } 93 | 94 | return aList; 95 | } 96 | 97 | public static Set list2set(List aList) { 98 | Set aSet = new HashSet(); 99 | if(aList == null) return aSet; 100 | 101 | for(Object o : aList) { 102 | aSet.add(o); 103 | } 104 | 105 | return aSet; 106 | } 107 | 108 | public static byte[] str2bytes(String msg) throws IOException { 109 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 110 | DataOutputStream daos = new DataOutputStream(baos); 111 | daos.writeUTF(msg); 112 | 113 | return baos.toByteArray(); 114 | } 115 | 116 | public static String bytes2str(byte[] bytes) throws IOException { 117 | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 118 | DataInputStream daos = new DataInputStream(bais); 119 | return daos.readUTF(); 120 | } 121 | 122 | public static final boolean sameByteArray(byte[] a, byte[] b) { 123 | if(a == null && b == null) return true; 124 | if(a.length != b.length) return false; 125 | 126 | // not null, same length 127 | for(int i = 0; i < a.length; i++) { 128 | if(a[i] != b[i]) return false; 129 | } 130 | 131 | return true; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAP.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.util.Random; 4 | 5 | public class ASAP { 6 | public static final int TRANSIENT_ERA = Integer.MAX_VALUE; 7 | public static final int INITIAL_ERA = 0; 8 | public static final int MIN_ERA = INITIAL_ERA; 9 | public static final int MAX_ERA = Integer.MAX_VALUE-1; 10 | 11 | public static int nextEra(int workingEra) { 12 | if(workingEra == ASAP.MAX_ERA) { 13 | return ASAP.INITIAL_ERA; 14 | } 15 | return workingEra+1; 16 | } 17 | 18 | public static int previousEra(int workingEra) { 19 | if(workingEra == ASAP.INITIAL_ERA) { 20 | return ASAP.MAX_ERA; 21 | } 22 | return workingEra-1; 23 | } 24 | 25 | /** 26 | * produces a unique id - is made up by current time in millis added with some random digits. 27 | * @return 28 | */ 29 | public static String createUniqueID() { 30 | long now = System.currentTimeMillis(); 31 | long nowUnchanged = now; 32 | 33 | /* now is a 64 bit value. Actually, only 63 bits are relevant because it is positive value. 34 | Moreover, we are already in the 21. century - could reduce bit even more. We ignore that obvious fact 35 | at first. 36 | 37 | Calculations: 2^64 = 1,8.. * 10^19; we take a..z and A..Z and 0..9 -> 62 digits 38 | 62^11 = 5,2.. * 10^19 we need 11 digits 39 | */ 40 | 41 | int randomDigits = 3; 42 | int timeDigits = 11; 43 | int digits = timeDigits + randomDigits; 44 | int basis = 62; 45 | 46 | /* 47 | 0..9 --> 0..9 48 | 10..35 -> a..z 49 | 36..61 -> A..Z 50 | */ 51 | 52 | char[] idChars = new char[digits]; 53 | // init with random number 54 | for(int i = 0; i < digits; i++) idChars[i] = '0'; 55 | 56 | // let's fill it with code time stamp 57 | int i = 0; 58 | long rest = 0; 59 | while(now > 0) { 60 | rest = now % basis; // rest is number [0,63] 61 | now /= basis; 62 | 63 | idChars[i++] = ASAP.int2charID((int) rest); 64 | } 65 | 66 | // set index 67 | i = timeDigits; 68 | 69 | // random digits 70 | long rValue = now + nowUnchanged; 71 | Random random = new Random(rValue); 72 | //char lastC = ' '; // not value space 73 | while(i < digits) { 74 | int r = random.nextInt(basis); 75 | char c = ASAP.int2charID(r); 76 | //if(c == lastC) continue; 77 | idChars[i++] = c; 78 | rValue = (rValue * r * nowUnchanged) % basis; 79 | random = new Random(rValue); 80 | //random.setSeed(rValue); 81 | } 82 | 83 | return new String(idChars); 84 | } 85 | 86 | private static char int2charID(int value) { 87 | // convert value into valid values; 88 | // 0..9 -> 0..9 89 | if(value <= 9) { 90 | return (char)((int)'0' + value); 91 | } else { 92 | value -= 10; 93 | // a..z 94 | if(value <= 25) { 95 | return (char)((int)'a' + value); 96 | } else { 97 | // A..Z 98 | value -= 26; 99 | return (char)((int)'A' + value); 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * 106 | * This method test if an era is within (or on the edge of) an era - range described by first and last era. 107 | * @param era era that is tested. Is in within a given range 108 | * @param eraFirst first era of that range. 109 | * @param eraLast last era of that range 110 | * @return true - era is in that range or on the edge, false otherwise 111 | */ 112 | public static boolean isEraInRange(int era, int eraFirst, int eraLast) { 113 | // deal wrapping around era numbers 114 | if(eraLast - eraFirst >= 0) { // not wrapped around 115 | // [init].... [eraFirst] ++++ [eraLast] ..... [max] 116 | return (era >= eraFirst && era <= eraLast); 117 | } 118 | // else: wrapped around: 119 | /* 120 | [init]++++++++++++++ [eraLast] .......... [eraFirst] +++++++++[max] 121 | (a) (b) (c) 122 | */ 123 | return ( 124 | isEraInRange(era, eraFirst, ASAP.MAX_ERA) // c) 125 | || 126 | isEraInRange(era, ASAP.INITIAL_ERA, eraLast) // a) 127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/test/java/net/sharksystem/asap/engine/WhiteBoxTests.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap.engine; 2 | 3 | import java.io.IOException; 4 | import java.util.Iterator; 5 | 6 | import net.sharksystem.asap.ASAPException; 7 | import net.sharksystem.fs.FSUtils; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 13 | * @author thsc 14 | */ 15 | public class WhiteBoxTests { 16 | public static final String FORMAT = "format"; 17 | public static final String DUMMY_USER = "dummyUser"; 18 | 19 | public WhiteBoxTests() { 20 | } 21 | 22 | @Test 23 | public void scratch() { 24 | int maxEra = 2^8-1; 25 | int i = 42; 26 | } 27 | 28 | @Test 29 | public void writeReadByteMessages() throws IOException, ASAPException { 30 | String folder = "tests/writeReadByteMessagesTest"; 31 | FSUtils.removeFolder(folder); 32 | 33 | String uri = "test://anURI"; 34 | String firstMessage = "first message"; 35 | String secondMessage = "second message"; 36 | 37 | FSUtils.removeFolder(folder); 38 | ASAPInternalStorage storage = ASAPEngineFS.getASAPStorage(DUMMY_USER, folder, FORMAT); 39 | 40 | // convert message into bytes 41 | byte[] messageBytes = firstMessage.getBytes(); 42 | storage.add(uri, messageBytes); 43 | 44 | // convert message into bytes 45 | messageBytes = secondMessage.getBytes(); 46 | storage.add(uri, messageBytes); 47 | 48 | Iterator byteArrayIter = storage.getChunkStorage().getChunk(uri, storage.getEra()).getMessages(); 49 | messageBytes = byteArrayIter.next(); 50 | String message = new String(messageBytes); 51 | Assert.assertTrue(message.equals(firstMessage)); 52 | 53 | messageBytes = byteArrayIter.next(); 54 | message = new String(messageBytes); 55 | Assert.assertTrue(message.equals(secondMessage)); 56 | } 57 | 58 | @Test 59 | public void writeReadStringMessages() throws IOException, ASAPException { 60 | String folder = "tests/writeReadStringMessagesTest"; 61 | FSUtils.removeFolder(folder); 62 | 63 | String uri = "test://anURI"; 64 | String firstMessage = "first message"; 65 | String secondMessage = "second message"; 66 | 67 | ASAPInternalStorage storage = ASAPEngineFS.getASAPStorage(DUMMY_USER, folder, FORMAT); 68 | 69 | storage.add(uri, firstMessage); 70 | storage.add(uri, secondMessage); 71 | 72 | Iterator messageIter = storage.getChunkStorage().getChunk(uri, storage.getEra()).getMessagesAsCharSequence(); 73 | String message = messageIter.next().toString(); 74 | Assert.assertTrue(message.equals(firstMessage)); 75 | 76 | message = messageIter.next().toString(); 77 | Assert.assertTrue(message.equals(secondMessage)); 78 | } 79 | 80 | @Test 81 | public void persistentMessage1() throws IOException, ASAPException { 82 | String folder = "tests/persistentMessage1"; 83 | FSUtils.removeFolder(folder); 84 | 85 | String uri = "test://anURI"; 86 | String firstMessage = "first message"; 87 | 88 | ASAPInternalStorage storage = ASAPEngineFS.getASAPStorage(DUMMY_USER, folder, FORMAT); 89 | storage.add(uri, firstMessage); 90 | 91 | // re-create storage 92 | storage = ASAPEngineFS.getASAPStorage(DUMMY_USER, folder, FORMAT); 93 | 94 | Assert.assertEquals(storage.getChannelURIs().get(0), uri); 95 | 96 | Iterator messageIter = storage.getChunkStorage().getChunk(uri, storage.getEra()).getMessagesAsCharSequence(); 97 | String message = messageIter.next().toString(); 98 | Assert.assertTrue(message.equals(firstMessage)); 99 | } 100 | 101 | @Test 102 | public void testExtraData() throws IOException, ASAPException { 103 | String folder = "tests/testExtraData"; 104 | FSUtils.removeFolder(folder); 105 | 106 | String uri = "test://anURI"; 107 | 108 | FSUtils.removeFolder(folder); 109 | ASAPInternalStorage storage = ASAPEngineFS.getASAPStorage(DUMMY_USER, folder, FORMAT); 110 | 111 | String key1 = "key1"; 112 | String value1 = "value1"; 113 | String key2 = "key2"; 114 | String value2 = "value2"; 115 | 116 | storage.putExtra(uri, key1, value1); 117 | storage.putExtra(uri, key2, value2); 118 | 119 | // restore 120 | storage = ASAPEngineFS.getASAPStorage(DUMMY_USER, folder, FORMAT); 121 | Assert.assertEquals(storage.getExtra(uri, key1), value1); 122 | Assert.assertEquals(storage.getExtra(uri, key2), value2); 123 | 124 | Assert.assertEquals(storage.getChannelURIs().get(0), uri); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/net/sharksystem/asap/ASAPStorage.java: -------------------------------------------------------------------------------- 1 | package net.sharksystem.asap; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | public interface ASAPStorage { 7 | /** 8 | * Get owner id/name of this storage. It is the name of the local peer. 9 | * @return owner id/name 10 | */ 11 | CharSequence getOwner(); 12 | 13 | /** 14 | * @return list of peers with an incoming storage 15 | * @see #getExistingIncomingStorage(CharSequence) 16 | */ 17 | List getSender(); 18 | 19 | /** 20 | * Peer exchange message during an encounter. Received messages are stored in 21 | * an incoming storage 22 | * @param sender 23 | * @return storage with received messages from a peer 24 | * @throws IOException 25 | * @throws ASAPException 26 | */ 27 | ASAPStorage getExistingIncomingStorage(CharSequence sender) throws IOException, ASAPException; 28 | 29 | /** 30 | * Get storage - parameter decides if to create a non-existing storage 31 | * @param sender 32 | * @param create 33 | * @return 34 | * @throws IOException 35 | * @throws ASAPException 36 | */ 37 | ASAPStorage getIncomingStorage(CharSequence sender, boolean create) throws IOException, ASAPException; 38 | 39 | /** 40 | * Get storage - create of required 41 | * @param sender 42 | * @return 43 | * @throws IOException 44 | * @throws ASAPException 45 | */ 46 | ASAPStorage getIncomingStorage(CharSequence sender) throws IOException, ASAPException; 47 | /** 48 | * 49 | * @return storage format / app 50 | */ 51 | CharSequence getFormat(); 52 | 53 | /** 54 | * 55 | * @return list of channel uris present in this storage 56 | * @throws IOException 57 | */ 58 | List getChannelURIs() throws IOException; 59 | 60 | /** 61 | * Create a channel that includes all messages (send or received) with this uri. 62 | * @param uri 63 | * @return channel containing all message issued by owner of this storage. Received messages are not part 64 | * of this channel. (See makan implementation) 65 | * @throws ASAPException if no channel with that uri exists in this storage 66 | */ 67 | ASAPChannel getChannel(CharSequence uri) throws ASAPException, IOException; 68 | 69 | /** 70 | * 71 | * @param uri channel uri 72 | * @return if channel exists - in other words: at least one message was added with this channel uri 73 | */ 74 | boolean channelExists(CharSequence uri) throws IOException; 75 | 76 | /** 77 | * Get oldest era available on that peer. 78 | * @return 79 | */ 80 | public int getOldestEra(); 81 | 82 | /** 83 | * Get current era. 84 | * @return 85 | */ 86 | public int getEra(); 87 | 88 | /** 89 | * Get next era number. Era numbers are organized in a circle. Number 0 90 | * follows Integer.MAXVALUE. That method takes care of that fact. 91 | * No change is made on current era. 92 | * 93 | * @param era 94 | * @return 95 | */ 96 | public int getNextEra(int era); 97 | 98 | /** 99 | * Get previous era number. Era numbers are organized in a circle. Er 100 | * number 0 is proceeded by era number Integer.MAXVALUE. 101 | * That method takes care of that fact. 102 | * No change is made on current era. 103 | * 104 | * @param era 105 | * @return 106 | */ 107 | int getPreviousEra(int era); 108 | 109 | /** 110 | * 111 | * @return The local chunk storage that is meant to be used by the local 112 | * app. Note: That storage is changed during an ASAP session. 113 | */ 114 | ASAPChunkStorage getChunkStorage(); 115 | 116 | void createChannel(CharSequence urlTarget) throws IOException, ASAPException; 117 | 118 | void removeChannel(CharSequence uri) throws IOException; 119 | /** 120 | * 121 | * @param uri 122 | * @param messageAsBytes 123 | * @throws IOException 124 | * @see ASAPChannel#addMessage(byte[]) 125 | */ 126 | void add(CharSequence uri, byte[] messageAsBytes) throws IOException; 127 | 128 | /** 129 | * Put some extra information on that channel 130 | * @param uri describing the channel 131 | * @param key 132 | * @param value 133 | * @throws IOException 134 | */ 135 | void putExtra(CharSequence uri, String key, String value) throws IOException; 136 | 137 | /** 138 | * Remove a string from extra information set 139 | * @param uri 140 | * @param key 141 | * @return 142 | * @throws IOException 143 | */ 144 | CharSequence removeExtra(CharSequence uri, String key) throws IOException; 145 | 146 | /** 147 | * Get a string from extra information set without removing 148 | * @param uri 149 | * @param key 150 | * @return 151 | * @throws IOException 152 | */ 153 | CharSequence getExtra(CharSequence uri, String key) throws IOException; 154 | } 155 | --------------------------------------------------------------------------------