is Eclipse/Californium CoAP observer much slower than Aiocoap observer? - java

I'm trying to build a system in which I can connect some devices to a server over the internet.
I want to stream some data over CoAP (10-30FPS), frame size = 3KB.
Firstly, I used Aiocoap, it sends up to 100FPS but uses too much CPU,
requests are NON, got low lose rate in Aiocoap,
while Eclipse/Californium could not send more than 3FPS,
when i use higher FPS, either I receive only the first block of each message or receiving nothing, also not ordered most of the times.
I was wondering if this is the real performance of Californium or am I using it in a wrong way?
I will share some code:
server.java
static class CoapObserverServer extends CoapResource {
int i = -1;
public CoapObserverServer() {
super("alarm");
setObservable(true); // enable observing
setObserveType(Type.NON); // configure the notification type to CONs
getAttributes().setObservable(); // mark observable in the Link-Format
System.out.println(this);
// schedule a periodic update task, otherwise let events call changed()
//new Timer().schedule(new UpdateTask(), 0, 1000/2);
}
private class UpdateTask extends TimerTask {
#Override
public void run() {
changed(); // notify all observers
}
}
#Override
public void handleGET(CoapExchange exchange) {
// the Max-Age value should match the update interval
exchange.setMaxAge(1);
//++i;
int leng = 2000;
String s = "" + i + "-" + fillString('X', leng - 1 - Integer.toString(i).len>
exchange.respond(s);
}
public static String fillString(char fillChar, int count){
// creates a string of 'x' repeating characters
char[] chars = new char[count];
while (count>0) chars[--count] = fillChar;
return new String(chars);
}
#Override
public void handleDELETE(CoapExchange exchange) {
delete(); // will also call clearAndNotifyObserveRelations(ResponseCode.NOT_>
exchange.respond(ResponseCode.DELETED);
}
#Override
public void handlePUT(CoapExchange exchange) {
exchange.accept();
int format = exchange.getRequestOptions().getContentFormat();
if (format == MediaTypeRegistry.TEXT_PLAIN) {
// ...
String plain = exchange.getRequestText();
try{
i = Integer.valueOf(plain);
} catch(NumberFormatException ex){
System.out.println("error converting string"+ plain);
}
exchange.respond(ResponseCode.CHANGED);
changed(); // notify all observers
}
}
Observer.java
private static final File CONFIG_FILE = new File("Californium3.properties");
private static final String CONFIG_HEADER = "Californium CoAP Properties file for client";
private static final int DEFAULT_MAX_RESOURCE_SIZE = 2 * 1024 * 1024; // 2 MB
private static final int DEFAULT_BLOCK_SIZE = 512;
static {
CoapConfig.register();
UdpConfig.register();
}
private static DefinitionsProvider DEFAULTS = new DefinitionsProvider() {
#Override
public void applyDefinitions(Configuration config) {
config.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
config.set(CoapConfig.MAX_MESSAGE_SIZE, DEFAULT_BLOCK_SIZE);
config.set(CoapConfig.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
}
};
private static class AsynchListener implements CoapHandler {
#Override
public void onLoad(CoapResponse response) {
System.out.println( response.getResponseText() );
}
#Override
public void onError() {
System.err.println("Error");
}
}
/*
* Application entry point.
*/
public static void main(String args[]) {
Configuration config = Configuration.createWithFile(CONFIG_FILE, CONFIG_HEADER, DEFAULTS);
Configuration.setStandard(config);
URI uri = null; // URI parameter of the request
if (args.length > 0) {
// input URI from command line arguments
try {
uri = new URI(args[0]);
} catch (URISyntaxException e) {
System.err.println("Invalid URI: " + e.getMessage());
System.exit(-1);
}
CoapClient client = new CoapClient(uri);
client.useNONs();
// observe
AsynchListener asynchListener = new AsynchListener();
CoapObserveRelation observation = client.observe(asynchListener);
// User presses ENTER to exit
System.out.println("Press ENTER to exit...");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try { br.readLine(); } catch (IOException e) { }
System.out.println("Exiting...");
observation.proactiveCancel();
}
So i'm controlling the FPS by sending PUT requests with a server that has a counter 0-50.

Not sure, what your doing.
That seems to be wired and not related to RFC7252 nor RFC7641.
CoAP is designed for REST, I don't see any benefit in using it for video streaming.
Using Eclipse/Californium on a Intel n6005 with 16GB RAM, the CoAP/DTLS server runs on about 60000 requests/second. The benchmark uses 2000 clients in parallel.
See also Eclipse/Californium - Benchmarks j5005
Using only one client with CON requests, the performance is mainly limited by the RTT. 30 requests/second should work, if that RTT is accordingly small.
Using NON requests doesn't really help. CoAP RFC7252 defines two layers, a messaging layer and an application layer. NON affects only the messaging layer, but a NON request will wait for it's response, if NSTART-1 should be used.
If your RTT is the issue, you may try to escape that either using requests with "No Server Response" (RFC7967) or multiple NON responses (RFC7641). The first is not intended for fast requests, the second is more a work-around of the initial statement, that CoAP is REST not video-streaming.
So, what is your RTT?

Related

Handle multiple user at the same time with TelegramLongPollingBot and thread

I'm building my first telegram bot. It send one message every 5 seconds to the user.
While it sends it to one user it cannot receive update from other chat.
public void foo(msg, Update update){
msg.setChatId(update.getMessage().getChatId());
for (int i = 1; i < links.size(); i++){
msg.setText(links.get(i));
execute(msg);
}
Thread.sleep(wait * 1000);
}
How can I use Thread? I've tried creating multiple thread here
public static void bot(){
ApiContextInitializer.init();
TelegramBotsApi telegramBotsApi = new TelegramBotsApi();
try {
telegramBotsApi.registerBot(new myBot());
} catch (TelegramApiException e) {
e.printStackTrace();
}
But he tries to create multiple bots and fails. Same if this is the runnable function:
How can I do it? I'm Stuck. I cannot create this function in different thread
public void onUpdateReceived(Update update) {
leggi(new SendMessage(), update.getMessage().getText(), update);
//.setChatId(update.getMessage().getChatId())
public void leggi(SendMessage msg, String command, Update update){
if(command.equals("test") {
foo( msg, update);
}
Here the full code... https://github.com/siamoInPochi/Ilsottomarinobot/tree/prova/src/main/java/Ilsottomarinobot
If you spawn a thread for every bot user who wants to receive messages, you will quickly be out of computer's resources in case of high number of users. So I think threads is not a good idea for your task.
In my mind more natural approach is the following:
Find a library with an HTTP server.
Switch from GetUpdates to webhooks.
Schedule send-message-to-user-every-5-seconds tasks to server's event loop.
Send messages every 5 seconds asynchronously.
You can make it with this library https://github.com/pengrad/java-telegram-bot-api
<dependency>
<groupId>com.github.pengrad</groupId>
<artifactId>java-telegram-bot-api</artifactId>
<version>4.2.0</version>
</dependency>
Subscribe to new updates via bot.setUpdatesListener
Send messages via bot.execute(new SendMessage(chatId, link), callback)
Full working example:
static String[] links = {"1", "2", "3"};
static Callback emptyCallback = new Callback() {
#Override
public void onResponse(BaseRequest request, BaseResponse response) {
}
#Override
public void onFailure(BaseRequest request, IOException e) {
e.printStackTrace();
}
};
static void foo(TelegramBot bot, Update update) {
Message message = update.message();
if (message == null) return;
Long chatId = message.chat().id();
for (String link : links) {
bot.execute(new SendMessage(chatId, link), emptyCallback);
}
}
public static void main(String[] args) {
TelegramBot bot = new TelegramBot(TOKEN);
bot.setUpdatesListener(updates -> {
for (Update update : updates) {
foo(bot, update);
}
return UpdatesListener.CONFIRMED_UPDATES_ALL;
});
}

Java netty rxtx pipeline blocks after writeAndFlush()

I'm currently trying to achieve a somewhat stable connection between a micro-controller and a Java-application using netty 4.0.44.Final and rxtx. From time to time the controller asks for a time-stamp, otherwise it is just forwarding sensor data to my application. The application is able to receive as many packages as I want to until i call writeAndFlush() somewhere in the pipeline (i.e. answering a time-request). The pipeline correctly writes data on the outputstream (when writeAndFlush() is called) and from that point onwards my application is never receiving data again and I have no idea why.
public class WsnViaRxtxConnector extends AbstractWsnConnector{
private static final Logger LOG = LoggerFactory.getLogger(WsnViaRxtxConnector.class);
private String port;
private Provider<MessageDeserializer> deserializerProvider;
private ChannelFuture channelFuture;
public ChannelKeeper keeper;
#Inject
public WsnViaRxtxConnector(Provider<MessageDeserializer> deserializerProvider, ChannelKeeper keeper) {
this.deserializerProvider = deserializerProvider;
this.port = Configuration.getConfig().getString("rest.wsn.port");
this.keeper = keeper;
System.setProperty("gnu.io.rxtx.SerialPorts", this.port);
}
#Override
protected void run() throws Exception
{
EventLoopGroup group = new OioEventLoopGroup();
//final EventExecutorGroup group2 = new DefaultEventExecutorGroup(1500);
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(RxtxChannel.class)
.handler(new ChannelInitializer<RxtxChannel>() {
#Override
public void initChannel(RxtxChannel ch) throws Exception {
ch.pipeline().addLast(new DleStxEtxFrameDecoder(), new DleStxEtxFrameEncoder());
ch.pipeline().addLast(new IntegrityCheck(),new IntegrityCalculation());
ch.pipeline().addLast(new AesCcmDecrypter(),new AesCcmEncrypter());
ch.pipeline().addLast(deserializerProvider.get(),new MessageSerializer());
ch.pipeline().addLast(new TimeStampJockel());
}
})
.option(RxtxChannelOption.BAUD_RATE, 19200);
ChannelFuture f = b.connect(new RxtxDeviceAddress(this.port)).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
The handlers are all pretty much standard implementations and seem to work when receiving packages only. The pipeline should first generate an object from the raw data, checkCRC, decrypt, deserialize and then compute some logic (aka generate a time-response).
public class TimeStampJockel extends ChannelInboundHandlerAdapter{
private static final Logger LOG = LoggerFactory.getLogger(TimeStampJockel.class);
private EventBus bus;
private ChannelKeeper keeper;
#Inject
public TimeStampJockel(){
this.bus = GlobalEventBus.getInstance();
this.keeper = keeper;
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg){
LOG.debug("Creating packet from received data");
RawPacket raw = (RawPacket)msg;
//EventExecutor ex = ctx.executor();
//LOG.debug("inexecutor.EventLoop(1):" + ex.inEventLoop());
//keeper.addChannelHandlerContext(raw.getSource(),ctx);
ByteBuf buf = raw.getContent();
LOG.debug("\tBuffer: {}", HelperFunctions.getBufferAsHexString(buf));
UnsignedLong mac = UnsignedLong.fromLongBits(21);
while(buf.readerIndex()<buf.writerIndex())
{
int type = buf.readShort();
int length = buf.readShort();
ByteBuf value = buf.readBytes(length);
if(PacketType.getEnum(type).equals(PacketType.MAC))
{
mac = UnsignedLong.valueOf(value.readLong());
}
else
{
AbstractPacket packet = PacketFactory.createPacket(PacketType.getEnum(type), raw.getVersion(), raw.getPacketType(), raw.getSource(), raw.getSource(), raw.getDestination(), mac, value);
if(packet instanceof TimeReqPacket) {
TimeReqPacket timeReqPacket = (TimeReqPacket) packet;
Duration d = HelperFunctions.timeSinceYear2000();
TimeRespPacket newPacket = new TimeRespPacket(Constants.PROTOCOL_VERSION, PacketType.TIME_RESP.getValue(), packet.getGatewayAdr(),UnsignedLong.valueOf(Configuration.getConfig().getLong("rest.wsn.mac", Long.MAX_VALUE)),timeReqPacket.getMac(),timeReqPacket.getMac(),d.getStandardSeconds(),HelperFunctions.getMillisOfDuration(d));
ctx.write(newPacket);
} else {
bus.post(packet);
}
}
}
}
The received sensor data is pushed to a Guava-bus (unless its a time-request) and processed by other components. If the incoming package is a time-request-packet, the previously displayed component should generate a time-stamp-packet and writeAndFlush() is down the pipeline. Any ideas what may cause that issue? I'm pretty much out of ideas - I have been googling the last 10 hours without meaningful results and I have no unchecked resources left. I'm using ubuntu 16.04, thanks in advance.
[EDIT] I tried checking the ChannelFuture, by adding the following code-snippet to the last pipeline handler
ChannelFuture f = ctx.writeAndFlush(newPacket);
f.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
LOG.error("Server failed to send message", future.cause());
future.channel().close();
}
}
[EDIT2] Found my error. It was a netty version conflict. I am working with multiple versions of netty in different projects and was using an older netty version (4.0.13) instead of netty 4.044.final. I have no idea what changed between those versions but I am glad that everything is working properly now.

Alljoyn framework - Java - Signals . BUS_NO_SUCH_INTERFACE

It's a quite specific question but after days stuck in the same place and not getting any response in the Alljoyn forum, I decided to post it here. Maybe someone worked with this framework.
I am doing a project and I need the use of the signal mechanish that Alljoyn framework provides. However, I do need the signals inside a session and the example provides in the api core is sessionless.
In my case I need the service (server) to raise a signal and the client to receive it, but I am getting this error. Following the api samples, I managed to create a simple app that exchange messages through the methods defined in the interface, so I know that the communication is working. In this case, the service waits until the client connects and the send the signal. The client joins the session and right after register the signal and I am getting the BUS_NO_SUCH_INTERFACE error. I tried also to register after and before sending the signal, same problem. I think that for some reason the client does not find the bussignalhandler but I don't know why. I also put it in an external class and it didn't work.
I'm following the example in the core api guide:
https://allseenalliance.org/developers/develop/api-guide/core/android
This is the part where the service register and emit the signal:
SignalEmitter emitter = new SignalEmitter(mySignalInterface, joinerName,
sessionId,
SignalEmitter.GlobalBroadcast.Off);
myInterface = emitter.getInterface(SampleInterface.class);
// Emitting signals myInterface.buttonClicked(1);
myInterface.playerPosition(12, 1, -24);
However, in that example, I can't see a definition for myInterface. and I know it is not a mistake and they meant mySignalInterface because the method getInterface asks for an interface object and mySignalInterface is a class which implements that interface.
I put here the example I created and I'll upload the files in case someone wants to try them.
#BusInterface(name = "org.alljoyn.bus.samples.simple.SimpleInterface")
public interface SimpleInterface {
#BusMethod
String Ping(String inStr) throws BusException;
#BusSignal
public void playerPosition(int x, int y, int z) throws BusException;
}
Service:
Class inside the service to implement the signal and method
class SimpleService implements SimpleInterface, BusObject {
public String Ping(String inStr) {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_PING, inStr));
return inStr;
}
public void playerPosition(int x, int y, int z) { /* no implementation needed here*/}
}
Here part of the code that makes the connection in the service:
case CONNECT: {
org.alljoyn.bus.alljoyn.DaemonInit.PrepareDaemon(getApplicationContext());
mBus = new BusAttachment(getPackageName(), BusAttachment.RemoteMessage.Receive);
mBus.registerBusListener(new BusListener());
Status status = mBus.registerBusObject(mSimpleService, "/SimpleService");
if (status != Status.OK) {...}
status = mBus.connect();
logStatus("BusAttachment.connect()", status);
if (status != Status.OK) {...}
int flag = 0;
status = mBus.requestName(SERVICE_NAME, flag);
logStatus(String.format("BusAttachment.requestName(%s, 0x%08x)", SERVICE_NAME, flag), status);
if (status == Status.OK) {
status = mBus.advertiseName(SERVICE_NAME, SessionOpts.TRANSPORT_ANY);
logStatus(String.format("BusAttachement.advertiseName(%s)", SERVICE_NAME), status);
if (status != Status.OK) {...}
}
Mutable.ShortValue contactPort = new Mutable.ShortValue(CONTACT_PORT);
SessionOpts sessionOpts = new SessionOpts();
sessionOpts.traffic = SessionOpts.TRAFFIC_MESSAGES;
sessionOpts.isMultipoint = false;
sessionOpts.proximity = SessionOpts.PROXIMITY_ANY;
sessionOpts.transports = SessionOpts.TRANSPORT_ANY + SessionOpts.TRANSPORT_WFD;
status = mBus.bindSessionPort(contactPort, sessionOpts, new SessionPortListener() {
#Override
public boolean acceptSessionJoiner(short sessionPort, String joiner, SessionOpts sessionOpts) {
return sessionPort == CONTACT_PORT;
}
#Override
public void sessionJoined(short port, int id, String s) {
sessionId = id; joinerName = s; sessionEstablished = true;
}
});
logStatus(String.format("BusAttachment.bindSessionPort(%d, %s)",
contactPort.value, sessionOpts.toString()), status);
if (status != Status.OK) {...}
try {
while (!sessionEstablished) {
Thread.sleep(10);
}
SignalEmitter emitter = new SignalEmitter(mSimpleService, joinerName, sessionId, SignalEmitter.GlobalBroadcast.Off);
SimpleInterface myInterface = emitter.getInterface(SimpleInterface.class);
myInterface.playerPosition(12,1,1);
}
catch (BusException ex) {... }
}
Client:
/*On create of the activity which has a button and a text view to send text to the server */
mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_NULL
&& event.getAction() == KeyEvent.ACTION_UP) {
/* Call the remote object's Ping method. */
Message msg = mBusHandler.obtainMessage(BusHandler.PING,
view.getText().toString());
mBusHandler.sendMessage(msg);
}
return true;
}
});
private static final String SERVICE_NAME = "org.alljoyn.bus.samples.simple";
private static final short CONTACT_PORT=42;
private BusAttachment mBus;
private ProxyBusObject mProxyObj;
private SimpleInterface mSimpleInterface;
case CONNECT: {
org.alljoyn.bus.alljoyn.DaemonInit.PrepareDaemon(getApplicationContext());
mBus = new BusAttachment(getPackageName(), BusAttachment.RemoteMessage.Receive);
mBus.registerBusListener(new BusListener() {
#Override
public void foundAdvertisedName(String name, short transport, String namePrefix) {
if(!mIsConnected) {
Message msg = obtainMessage(JOIN_SESSION);
msg.arg1 = transport;
msg.obj = name;
sendMessage(msg);
}
}
});
Status status = mBus.connect();
logStatus("BusAttachment.connect()", status);
if (Status.OK != status) {...}
status = mBus.findAdvertisedName(SERVICE_NAME);
logStatus(String.format("BusAttachement.findAdvertisedName(%s)", SERVICE_NAME), status);
if (Status.OK != status) {...}
break;
}
case (JOIN_SESSION): {
if (mIsStoppingDiscovery) {
break;
}
short contactPort = CONTACT_PORT;
SessionOpts sessionOpts = new SessionOpts();
sessionOpts.transports = (short)msg.arg1;
Mutable.IntegerValue sessionId = new Mutable.IntegerValue();
Status status = mBus.joinSession((String) msg.obj, contactPort, sessionId, sessionOpts, new SessionListener() {
#Override
public void sessionLost(int sessionId, int reason) {
mIsConnected = false;
logInfo(String.format("MyBusListener.sessionLost(sessionId = %d, reason = %d)", sessionId,reason));
mHandler.sendEmptyMessage(MESSAGE_START_PROGRESS_DIALOG);
}
});
if (status == Status.OK) {
mProxyObj = mBus.getProxyBusObject(SERVICE_NAME,
"/SimpleService",
sessionId.value,
new Class<?>[] { SimpleInterface.class });
mSimpleInterface = mProxyObj.getInterface(SimpleInterface.class);
mSessionId = sessionId.value;
mIsConnected = true;
mHandler.sendEmptyMessage(MESSAGE_STOP_PROGRESS_DIALOG);
}
break;
status = mBus.registerSignalHandlers(this);
if (status != Status.OK) {...}
}
case PING: {
try {
if (mSimpleInterface != null) {
sendUiMessage(MESSAGE_PING, msg.obj);
String reply = mSimpleInterface.Ping((String) msg.obj);
sendUiMessage(MESSAGE_PING_REPLY, reply);
} catch {...}
}
...here some more code...
#BusSignalHandler(iface="org.alljoyn.bus.samples.simple.SimpleInterface", signal="playerPosition")
public void playerPosition(int x, int y, int z) {
sendUiMessage(MESSAGE_POST_TOAST, "Signal captured");
}
In this example, I have my client which has a textview where I can add text and send a ping to the server. This is working if I get rid of the registering signal part. I tried to do it as the api core says, with the Thread.sleep but doesn't work either.
Here I add the code of both of my applications (client & server) based on the samples that are in the alljoyn api.
https://github.com/JavierT/Alljoyn_signal_sample
Please let me know if you have some doubts, it was hard to put all the information in one post.
Thank you in advance.
I think this should solve your problem
Alljoyn BusSignalHandler
either of the following may be used to annotate a signal handler:
#BusSignalHandler(iface = "org.sample.MyInterface", signal = "MySignal")
public void handleSignal(String str)
{
}
#BusSignalHandler(iface = "org.myapp.IMyInterface", signal = "EmitMySignal")
public void handleSignal(String str)
{
}
The first example may be used succesfully when IMyInterface is known to the BusAttachment via a previous call to BusAttachment.registerBusObject(BusObject, String) or BusAttachment.getProxyBusObject(String, String, int, Class[]).
The second example may be used succesfully when IMyInterface is unknown to the BusAttachment.
using the second example should solve your problem.
Also, in your Client program as per the link provided above,
#BusSignalHandler(iface="org.alljoyn.bus.samples.simple.SimpleInterface", signal="playerPosition")
should be replaced with
#BusSignalHandler(iface="org.alljoyn.bus.samples.simpleclient.SimpleInterface", signal="playerPosition")
When you call registerSignalHandlers you must pass an object that implements the interface SimpleInterface and the method playerPosition will be called in that object when you receives a signal.
I believe (I've read the code quickly, I hope I'm not wrong) that you pass your BusHandler object, which does not implements SimpleInterface.
Please change :
private static final String SERVICE_NAME = "org.alljoyn.bus.samples.simple";
to
"org.alljoyn.bus.samples.simple.SimpleInterface" as you have interface name ,
both should be same . as when the Get method of BusObject is called the interface names does not match.

Play Framework 2.3.x ByteChunks MP3 streaming has no playback, is not 'scrollable' in the browser

Using Play Framework (version 2.3.x) (Java style), I am trying to serve an .mp3 file to the browser. Since it is a 'large' file I have decided to go with Play's ByteChunks Object, as follows.
#With(MP3Headers.class)
public static Result test() {
Chunks<byte[]> chunks = new ByteChunks() {
public void onReady(Chunks.Out<byte[]> out) {
try {
byte[] song = Files.readAllBytes(Paths.get("public/mp3/song.mp3"));
out.write(song);
} catch(Exception e) {
e.printStackTrace();
} finally {
out.close();
}
}
};
return ok(chunks);
}
For clarification, my Mp3Headers file, which is responsable for setting the headers so that the browser knows what type the payload has:
public class MP3Headers extends Action.Simple {
public Promise<Result> call(Http.Context ctx) throws Throwable {
ctx.response().setContentType("audio/mpeg");
return delegate.call(ctx);
}
}
For completion, my routes file:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index()
GET /test controllers.Application.test()
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
As is to be expected, navigating to localhost:9000/test renders to a nice HTML5 audio player (see picture).
The problem I have is that 'scrolling' in the audio player does not work. If I do scroll, the music pauses, and when I let go (when I 'chose' a position in time), it continues where it first paused.
I hope that I make sense, and I hope that you guys know something more about this. Thanks in advance.
You will need to tell your browser that your server support range requests and implement the ranges responses (ie just provide the part of the music the browser needs). You can get an overview of the request/response cycle in this answer.
#With(MP3Headers.class)
public static Result test() {
final int begin, end;
final boolean isRangeReq;
response().setHeader("Accept-Ranges", "bytes");
if (request().hasHeader("RANGE")) {
isRangeReq = true;
String[] range = request().getHeader("RANGE").split("=")[1].split("-");
begin = Integer.parseInt(range[0]);
if (range.length > 1) {
end = Integer.parseInt(range[1]);
} else {
end = song.length-1;
}
response().setHeader("Content-Range", String.format("bytes %d-%d/%d", begin, end, song.length));
} else {
isRangeReq = false;
begin = 0;
end = song.length - 1;
}
Chunks<byte[]> chunks = new ByteChunks() {
public void onReady(Chunks.Out<byte[]> out) {
if(isRangeReq) {
out.write(Arrays.copyOfRange(song, begin, end));
} else {
out.write(song);
}
out.close();
}
};
response().setHeader("Content-Length", (end - begin + 1) + "");
if (isRangeReq) {
return status(206, chunks);
} else {
return status(200, chunks);
}
}
Note that in this code the song was already loaded in song. Also the parsing of the RANGE header is very dirty (you can get values like RANGE:)
I Found this code very easy implementation.
Put the below action and its private helper method in your controller.
Controller Action
public static Result file(Long id, String filename) throws IOException {
Item item = Item.fetch(id);
File file = item.getFile();
if(file== null || !file.exists()) {
Logger.error("File no longer exist item"+id+" filename:"+filename);
return notFound();
}
String rangeheader = request().getHeader(RANGE);
if(rangeheader != null) {
String[] split = rangeheader.substring("bytes=".length()).split("-");
if(Logger.isDebugEnabled()) { Logger.debug("Range header is:"+rangeheader); }
if(split.length == 1) {
long start = Long.parseLong(split[0]);
long length = file.length()-1l;
return stream(start, length, file);
} else {
long start = Long.parseLong(split[0]);
long length = Long.parseLong(split[1]);
return stream(start, length, file);
}
}
// if no streaming is required we simply return the file as a 200 OK
if(Play.isProd()) {
response().setHeader("Cache-Control", "max-age=3600, must-revalidate");
}
return ok(file);
}
Stream Helper method
private static Result stream(long start, long length, File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
fis.skip(start);
response().setContentType(MimeTypes.forExtension("mp4").get());
response().setHeader(CONTENT_LENGTH, ((length - start) +1l)+"");
response().setHeader(CONTENT_RANGE, String.format("bytes %d-%d/%d", start, length,file.length()));
response().setHeader(ACCEPT_RANGES, "bytes");
response().setHeader(CONNECTION, "keep-alive");
return status(PARTIAL_CONTENT, fis);
}
Complete example link is here Byte range requests in Play 2 Java Controllers

CometD doesnt start Jetty server in Java Multithreading

I`m using CometD-3.0.2 in Java multi-threading application following this tutorial. Everything works fine when I create a service channel. I have created an object of my Socket Connection class, that also starts 10 Java Threads, in the CometD initialise class
public class Initializer extends GenericServlet
{
private MainConnect connect;
#Override
public void init() throws ServletException
{
connect = new MainConnect();
// Retrieve the CometD service instantiated by AnnotationCometdServlet
StockPriceService service = (StockPriceService)getServletContext().getAttribute(StockPriceService.class.getName());
// Register the service as a listener of the emitter
connect.getListeners().add(service);
System.out.println("Service Added!");
//Initiate the Threads
connect.InitiateConnection();
}
My Socket class gets initiated and java threads are running as well. However, the jetty server never get started here.
My Service class
#Service
public class StockPriceService implements StockPriceEmitter.Listener
{
#Inject
private BayeuxServer bayeuxServer;
#Session
private LocalSession sender;
public void onUpdates(List<StockPriceEmitter.Update> updates)
{
for (StockPriceEmitter.Update update : updates)
{
// Create the channel name using the stock symbol
String channelName = "/stock/" + update.getSymbol().toLowerCase(Locale.ENGLISH);
// Initialize the channel, making it persistent and lazy
bayeuxServer.createIfAbsent(channelName, new ConfigurableServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
// Convert the Update business object to a CometD-friendly format
Map<String, Object> data = new HashMap<String, Object>(4);
data.put("symbol", update.getSymbol());
data.put("oldValue", update.getOldValue());
data.put("newValue", update.getNewValue());
// Publish to all subscribers
ServerChannel channel = bayeuxServer.getChannel(channelName);
channel.publish(sender, data, null);
System.out.println("Service hit!!");
}
}
}
Java multi-thread run method
if (workerID == 0) {
try {
System.out.println("Running");
Random random = new Random();
List<Update> updates = new ArrayList<Update>();
// Randomly choose how many stocks to update
int howMany = random.nextInt(symbols.size()) + 1;
for (int i = 0; i < howMany; ++i)
{
// Randomly choose which one to update
int which = random.nextInt(symbols.size());
String symbol = symbols.get(1);
float oldValue = values.get(symbol);
// Randomly choose how much to update
boolean sign = random.nextBoolean();
float howMuch = random.nextFloat();
float newValue = oldValue + (sign ? howMuch : -howMuch);
// Store the new value
values.put(symbol, newValue);
updates.add(new Update(symbol, oldValue, newValue));
// System.out.println("Updates from Emitter Class: " + updates);
}
// Notify the listeners
for (Listener listener : MainConnect.getListeners())
{
System.out.println("Listners value: " + listener);
listener.onUpdates(updates);
}
// Randomly choose how long for the next update
// We use a max delay of 1 second to simulate a high rate of updates
long howLong = random.nextInt(1000);
scheduler.schedule(this, howLong, TimeUnit.MILLISECONDS);
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Overall scenario, Im implementing a Sever that uses Sockets to receive client A connection then receive data from clients. I need this data to stream to client B. Jetty server doesnt start when I initiate the Socket class/ open a socket connection.
Any information is much appreciated

Categories