Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance testing java-apns #95

Merged
merged 7 commits into from
Apr 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/test/java/com/notnoop/apns/AbstractApnsServerSocket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.notnoop.apns;

import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;

/**
* Represents the Apple server. This allows testing outside of the Apple
* servers. Sub-classes should implement the specific handing of new socket
* connections.
*/
public abstract class AbstractApnsServerSocket {
private final SSLServerSocket serverSocket;
private final ExecutorService executorService;
private final ApnsServerExceptionDelegate exceptionDelegate;

public AbstractApnsServerSocket(SSLContext sslContext, int port,
ExecutorService executorService,
ApnsServerExceptionDelegate exceptionDelegate) throws IOException {
SSLServerSocketFactory serverSocketFactory = sslContext
.getServerSocketFactory();
serverSocket = (SSLServerSocket) serverSocketFactory
.createServerSocket(port);
this.executorService = executorService;
this.exceptionDelegate = exceptionDelegate;
}

/**
* Start the server accept process. This method is non-blocking.
*/
public final void start() {
new Thread(new Runnable() {
@Override
public void run() {
startAccept();
}
}).start();
}

private void startAccept() {

try {
while (true) {
Socket accept = serverSocket.accept();
executorService.execute(new SocketHandler(accept));
}
} catch (IOException ioe) {
executorService.shutdown();
}
}

/**
* Stops the server socket. This method is blocking.
*/
public final void stop() {
try {
serverSocket.close();
} catch (IOException ioe) {
// don't care
}

executorService.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow(); // Cancel currently executing
// tasks
// Wait a while for tasks to respond to being cancelled
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
executorService.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}

private class SocketHandler implements Runnable {
private final Socket socket;

SocketHandler(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try {
handleSocket(socket);
} catch (IOException ioe) {
exceptionDelegate.handleRequestFailed(ioe);
}
}
}

abstract void handleSocket(Socket socket) throws IOException;
}
42 changes: 42 additions & 0 deletions src/test/java/com/notnoop/apns/ApnsFeedbackServerSocket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.notnoop.apns;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;

import javax.net.ssl.SSLContext;

/**
* Represents the Apple Feedback server. This allows testing outside of the
* Apple servers.
*/
public class ApnsFeedbackServerSocket extends AbstractApnsServerSocket {
private final ApnsServerService requestDelegate;

public ApnsFeedbackServerSocket(SSLContext sslContext, int port,
ExecutorService executorService, ApnsServerService requestDelegate,
ApnsServerExceptionDelegate exceptionDelegate) throws IOException {
super(sslContext, port, executorService, exceptionDelegate);
this.requestDelegate = requestDelegate;
}

@Override
void handleSocket(Socket socket) throws IOException {
Map<byte[], Date> inactiveDevices = requestDelegate
.getInactiveDevices();
DataOutputStream dataStream = new DataOutputStream(
socket.getOutputStream());
for (Entry<byte[], Date> entry : inactiveDevices.entrySet()) {
int time = (int) (entry.getValue().getTime() / 1000L);
dataStream.writeInt(time);
byte[] bytes = entry.getKey();
dataStream.writeShort(bytes.length);
dataStream.write(bytes);
}
dataStream.close();
}
}
97 changes: 97 additions & 0 deletions src/test/java/com/notnoop/apns/ApnsGatewayServerSocket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.notnoop.apns;

import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;

import javax.net.ssl.SSLContext;

import sun.java2d.pipe.BufferedPaints;

/**
* Represents the Apple APNS server. This allows testing outside of the Apple
* servers.
*/
public class ApnsGatewayServerSocket extends AbstractApnsServerSocket {
private final ApnsServerService apnsServerService;

public ApnsGatewayServerSocket(SSLContext sslContext, int port,
ExecutorService executorService,
ApnsServerService apnsServerService,
ApnsServerExceptionDelegate exceptionDelegate) throws IOException {
super(sslContext, port, executorService, exceptionDelegate);
this.apnsServerService = apnsServerService;
}

@Override
void handleSocket(Socket socket) throws IOException {
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
while (true) {
int identifier = 0;
try {
int read = dataInputStream.read();
if (read == -1) {
break;
}

boolean enhancedFormat = read == 1;
int expiry = 0;
if (enhancedFormat) {
identifier = dataInputStream.readInt();
expiry = dataInputStream.readInt();
}

int deviceTokenLength = dataInputStream.readShort();
byte[] deviceTokenBytes = toArray(inputStream,
deviceTokenLength);

int payloadLength = dataInputStream.readShort();
byte[] payloadBytes = toArray(inputStream, payloadLength);

ApnsNotification message;
if (enhancedFormat) {
message = new EnhancedApnsNotification(identifier, expiry,
deviceTokenBytes, payloadBytes);
} else {
message = new SimpleApnsNotification(deviceTokenBytes,
payloadBytes);
}
apnsServerService.messageReceived(message);
} catch (IOException ioe) {
writeResponse(socket, identifier, 8, 1);
break;
} catch (Exception expt) {
writeResponse(socket, identifier, 8, 1);
break;
}
}
}

private void writeResponse(Socket socket, int identifier, int command,
int status) {
try {
BufferedOutputStream bos = new BufferedOutputStream(
socket.getOutputStream());
DataOutputStream dataOutputStream = new DataOutputStream(bos);
dataOutputStream.writeByte(command);
dataOutputStream.writeByte(status);
dataOutputStream.writeInt(identifier);
dataOutputStream.flush();
} catch (IOException ioe) {
// if we can't write a response, nothing we can do
}
}

private byte[] toArray(InputStream inputStream, int size)
throws IOException {
byte[] bytes = new byte[size];
inputStream.read(bytes);
return bytes;
}
}
15 changes: 15 additions & 0 deletions src/test/java/com/notnoop/apns/ApnsServerExceptionDelegate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.notnoop.apns;

/**
* A delegate that gets notified of failures.
*/
public interface ApnsServerExceptionDelegate {

void handleRequestFailed(Throwable thr);

public static final ApnsServerExceptionDelegate EMPTY = new ApnsServerExceptionDelegate() {
@Override
public void handleRequestFailed(Throwable thr) {
}
};
}
39 changes: 39 additions & 0 deletions src/test/java/com/notnoop/apns/ApnsServerService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.notnoop.apns;

import java.util.Collections;
import java.util.Date;
import java.util.Map;

/**
* A delegate that gets notified of the delivery of messages.
*/
public interface ApnsServerService {


/**
* Called when message was successfully received
*
* @param message the notification that was received.
* @throws Exception
*/
void messageReceived(ApnsNotification message) throws Exception;


/**
* Called to determine if any of the devices is judged to be inactive.
*
* @return a map of inactive devices.
*/
Map<byte[], Date> getInactiveDevices();

public static final ApnsServerService EMPTY = new ApnsServerService() {
@Override
public void messageReceived(ApnsNotification message) throws Exception {
}

@Override
public Map<byte[], Date> getInactiveDevices() {
return Collections.emptyMap();
}
};
}
Loading