Huge amount of packet drops and latency faced in netty - tcp

I am using netty 3.5.11 with Jdk 1.7 on Ubuntu to receive a large amount of updates of stocks rates at a very high frequency. The message format being sent is JSON. The data is subscribed from topic on a redis server. There is a Subscriber for each symbol. The channel object is passed to multiple Subscribers and on receiving the data it is written to the client.
Now the amount of data received is around 25,000 records in 2 minutes. Each record size is on an average around 500 bytes long.
During test runs around 7500/8000 records were dropped because the channel was not writable.
How do i avoid this. ?
I also noticed that the latency increases systematically leading to updates being received after a long period. This happened when i I used Bufferedwritehandler to avoid packet drops.
Here are the options that i set on bootstrap.
executionHandler = new ExecutionHandler(
new OrderedMemoryAwareThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2, 1000000, 10000000, 100,
TimeUnit.MILLISECONDS));
serverBootStrap.setPipelineFactory(new ChannelPipelineFactory()
{
#Override
public ChannelPipeline getPipeline() throws Exception
{
return Channels.pipeline(new PortUnificationServerHandler(getConfiguration(), executionHandler));
}
});
serverBootStrap.setOption("child.tcpNoDelay", true);
serverBootStrap.setOption("tcpNoDelay", true);
serverBootStrap.setOption("child.keepAlive", true);
serverBootStrap.setOption("child.reuseAddress", true);
//setting buffer size can improve I/O
serverBootStrap.setOption("child.sendBufferSize", 16777216);
serverBootStrap.setOption("receiveBufferSize", 16777216);//1048576);
// better to have an receive buffer predictor
serverBootStrap.setOption("receiveBufferSizePredictorFactory", new AdaptiveReceiveBufferSizePredictorFactory(1024, 1024 * 16, 16777216));//1048576));
//if the server is sending 1000 messages per sec, optimum write buffer water marks will
//prevent unnecessary throttling, Check NioSocketChannelConfig doc
serverBootStrap.setOption("backlog", 1000);
serverBootStrap.setOption("sendBufferSize", 16777216);//1048576);
serverBootStrap.setOption("writeBufferLowWaterMark", 1024 * 1024 * 25);
serverBootStrap.setOption("writeBufferHighWaterMark", 1024 * 1024 * 50);
The pipeline and handlers class
public class PortUnificationServerHandler extends FrameDecoder
{
private AppConfiguration appConfiguration;
private final ExecutionHandler executionHandler;
public PortUnificationServerHandler(AppConfiguration pAppConfiguration, ExecutionHandler pExecutionHandler)
{
appConfiguration = pAppConfiguration;
this.executionHandler = pExecutionHandler;
}
#Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception
{
String lRequest = buffer.toString(CharsetUtil.UTF_8);
if (ConnectionServiceHelper.isValidJSON(lRequest))
{
ObjectMapper lObjectMapper = new ObjectMapper();
StringReader lStringReader = new StringReader(lRequest);
JsonNode lNode = lObjectMapper.readTree(lStringReader);
if (lNode.get(Constants.REQUEST_TYPE).asText().trim().equalsIgnoreCase(Constants.LOGIN_REQUEST))
{
JsonNode lDataNode1 = lNode.get(Constants.REQUEST_DATA);
LoginRequest lLogin = lObjectMapper.treeToValue(lDataNode1, LoginRequest.class);
if (lLogin.getCompress() != null)
{
if (lLogin.getCompress().trim().equalsIgnoreCase(Constants.COMPRESS_FLAG_TRUE))
{
enableJSON(ctx);
enableGzip(ctx);
ctx.getPipeline().remove(this);
}
else
{
enableJSON(ctx);
ctx.getPipeline().remove(this);
}
}
else
{
enableJSON(ctx);
ctx.getPipeline().remove(this);
}
}
}
// Forward the current read buffer as is to the new handlers.
return buffer.readBytes(buffer.readableBytes());
}
private void enableJSON(ChannelHandlerContext ctx)
{
ChannelPipeline pipeline = ctx.getPipeline();
boolean lHandlerExists = pipeline.getContext("bufferedwriter") != null;
if (!lHandlerExists)
{
pipeline.addFirst("bufferedwriter", new MyBufferedWriteHandler()); // 80960
}
boolean lHandlerExists = pipeline.getContext("framer") != null;
if (!lHandlerExists)
{
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(65535,
new ChannelBuffer[]
{
ChannelBuffers.wrappedBuffer(
new byte[]
{
'\n'
})
}));
}
lHandlerExists = pipeline.getContext("decoder") != null;
if (!lHandlerExists)
{
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
}
lHandlerExists = pipeline.getContext("encoder") != null;
if (!lHandlerExists)
{
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
}
lHandlerExists = pipeline.getContext("executor") != null;
if (!lHandlerExists)
{
pipeline.addLast("executor", executionHandler);
}
lHandlerExists = pipeline.getContext("handler") != null;
if (!lHandlerExists)
{
pipeline.addLast("handler", new ConnectionServiceUpStreamHandler(appConfiguration));
}
lHandlerExists = pipeline.getContext("unite") != null;
if (!lHandlerExists)
{
pipeline.addLast("unite", new PortUnificationServerHandler(appConfiguration, executionHandler));
}
}
private void enableGzip(ChannelHandlerContext ctx)
{
ChannelPipeline pipeline = ctx.getPipeline();
//pipeline.remove("decoder");
//pipeline.addLast("decoder", new MyStringDecoder(CharsetUtil.UTF_8, true));
//pipeline.addLast("compress", new CompressionHandler(80, "gzipdeflater"));
boolean lHandlerExists = pipeline.getContext("encoder") != null;
if (lHandlerExists)
{
pipeline.remove("encoder");
}
lHandlerExists = pipeline.getContext("gzipdeflater") != null;
if (!lHandlerExists)
{
pipeline.addBefore("executor", "gzipdeflater", new ZlibEncoder(ZlibWrapper.GZIP));
}
lHandlerExists = pipeline.getContext("lengthprepender") != null;
if (!lHandlerExists)
{
pipeline.addAfter("gzipdeflater", "lengthprepender", new LengthFieldPrepender(4));
}
}
}
The BufferedWriterHandler
public class MyBufferedWriteHandler extends BufferedWriteHandler
{
private final AtomicLong bufferSize = new AtomicLong();
final Logger logger = LoggerFactory.getLogger(getClass());
public MyBufferedWriteHandler() {
// Enable consolidation by default.
super(true);
}
#Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
ChannelBuffer data = (ChannelBuffer) e.getMessage();
if (e.getChannel().isWritable())
{
long newBufferSize = bufferSize.get();
// Flush the queue if it gets larger than 8KiB.
if (newBufferSize > 0)
{
flush();
bufferSize.set(0);
}
ctx.sendDownstream(e);
}
else
{
logger.warn( "Buffering data for : " + e.getChannel().getRemoteAddress() );
super.writeRequested(ctx, e);
bufferSize.addAndGet(data.readableBytes());
}
}
#Override
public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
if (e.getChannel().isWritable())
{
flush();
}
}
The function used in the Subscriber class to write data
public void writeToClient(Channel pClientChannel, String pMessage) throws IOException
{
String lMessage = pMessage;
if (pClientChannel.isWritable())
{
lMessage += Constants.RESPONSE_DELIMITER;
pClientChannel.write(lMessage);
}
else
{
logger.warn(DroppedCounter++ + " droppped : " + pMessage);
}
}
I have implemented some of the suggestions that i read on stackoverflow and other sites. But i have not been successfull in resolving this issue.
Kindly suggest or advice as to what am i missing ?
Thanks

Related

How to manage WebSocket objects that are no longer needed ASP.Net Core

I am using Asp.Net core 3.1 . If I want to create a WebSockets backend for example for
a chat app , I need to store all the related WebSocket objects for broadcasting events , my question is what is the best way to manage removing objects that are no longer useful (if disconnected or no longer open). keeping in mind that I want other parts of the application to access the WebScoket groups to also broadcast events if needed. I store the related connections in a ConnectionNode which is the nearest layer to the Websocket objects , a class called WebsocketsManager manage these nodes, a service in the background runs to clear the unused objects every timeout period. but since I want the group(related connections)to be accessible for the application (for example other endpoints); to avoid any concurrent modification errors , if a broadcast is required during the cleaning process,the broadcast will have to wait for the cleaning process to finish, thats why the WebsocketsManager if the related connections are larger than a certain limit it will divide them into multiple related ConnectionNodes , that way the cleaning process can continue partially for related connection while broadcasting if needed. I want to know how good my solution will behave or what is the best way to do it. any help would be really appreciated.
ConnectionNode
public class ConnectionNode
{
private List<WebSocket> connections;
private BroadcastQueue BroadcastQueue = new BroadcastQueue();
private bool isBroadCasting = false;
private bool isCleaning = false;
public void AddConnection(WebSocket socket)
{
if (connections == null)
connections = new List<WebSocket>();
connections.Add(socket);
}
public void Broadcast(Broadcast broadCast)
{
while (isCleaning)
{
}
BroadcastQueue.QueueBroadcast(broadCast);
if (isBroadCasting)
{
return;
}
isBroadCasting = true;
var broadcast = BroadcastQueue.GetNext();
while (broadCast != null)
{
foreach (var ws in connections)
{
broadCast.Dispatch(ws);
}
broadCast = BroadcastQueue.GetNext();
}
isBroadCasting = false;
}
public int CleanUnUsedConnections()
{
if (isBroadCasting)
return 0;
isCleaning = true;
var i =connections.RemoveAll(s => s.State != WebSocketState.Open);
isCleaning = false;
return i;
}
public int ConnectionsCount()
{
return connections.Count;
}
}
Manager class
public class WebSocketsManager
{
static int ConnectionNodesDividerLimit = 1000;
private ConcurrentDictionary<String, List<ConnectionNode>> mConnectionNodes;
private readonly ILogger<WebSocketsManager> logger;
public WebSocketsManager(ILogger<WebSocketsManager> logger)
{
this.logger = logger;
}
public ConnectionNode RequireNode(string Id)
{
if (mConnectionNodes == null)
mConnectionNodes = new ConcurrentDictionary<String, List<ConnectionNode>>();
var node = mConnectionNodes.GetValueOrDefault(Id);
if (node == null)
{
node = new List<ConnectionNode>();
node.Add(new ConnectionNode());
mConnectionNodes.TryAdd(Id, node);
return node[0];
}
if (ConnectionNodesDividerLimit != 0)
{
if (node[0].ConnectionsCount() == ConnectionNodesDividerLimit)
{
node.Insert(0,new ConnectionNode());
}
}
return node[0];
}
public void ClearUnusedConnections()
{
logger.LogInformation("Manager is Clearing ..");
if (mConnectionNodes == null)
return;
if (mConnectionNodes.IsEmpty)
{
logger.LogInformation("Empty ## Nothing to clear ..");
return;
}
Dictionary<String,ConnectionNode> ToBeRemovedNodes = new Dictionary<String, ConnectionNode>();
foreach (var pair in mConnectionNodes)
{
bool shoudlRemoveStack = true;
foreach (var node in pair.Value)
{
int i = node.CleanUnUsedConnections();
logger.LogInformation($"Removed ${i} from connection node(s){pair.Key}");
if (node.ConnectionsCount() == 0)
{
ToBeRemovedNodes[pair.Key] = node;
logger.LogInformation($"To be Removed A node From ..{pair.Key}");
}
else
{
shoudlRemoveStack = false;
}
}
if (shoudlRemoveStack)
{
ToBeRemovedNodes.Remove(pair.Key);
List<ConnectionNode> v =null;
var b = mConnectionNodes.TryRemove(pair.Key,out v);
logger.LogInformation($"Removing the Stack ..{pair.Key} Removed ${b}");
}
}
foreach (var pair in ToBeRemovedNodes)
{
mConnectionNodes[pair.Key].Remove(pair.Value);
logger.LogInformation($"Clearing Nodes : Clearing Nodes from stack #{pair.Key}");
}
}
public void Broadcast(string id, Broadcast broadcast)
{
var c = mConnectionNodes.GetValueOrDefault(id);
foreach (var node in c)
{
node.Broadcast(broadcast);
}
}
the service
public class SocketsConnectionsCleaningService : BackgroundService
{
private readonly IServiceProvider Povider;
private Timer Timer = null;
private bool isRunning = false;
private readonly ILogger Logger;
public SocketsConnectionsCleaningService(IServiceProvider Provider, ILogger<SocketsConnectionsCleaningService> Logger )
{
this.Povider = Provider;
this.Logger = Logger;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Logger.LogInformation("Execute Sync is called ");
Timer = new Timer(DeleteClosedConnections, null, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(2));
return Task.CompletedTask;
}
private void DeleteClosedConnections(object state)
{
Logger.LogInformation("Clearing ");
if (isRunning)
{
Logger.LogInformation("A Task is Running Return ");
return;
}
isRunning = true;
var connectionManager = Povider.GetService(typeof(WebSocketsManager)) as WebSocketsManager;
connectionManager.ClearUnusedConnections();
isRunning = false;
Logger.LogInformation($"Finished Cleaning !");
}
}
Usage in a controller be like
[HttpGet("ws")]
public async Task SomeRealtimeFunction()
{
if (HttpContext.IsWebSocketsRequest())
{
using var socket = await HttpContext.AcceptSocketRequest();
try
{
await socket.SendString(" Connected! ");
webSocketsManager.RequireNode("Chat Room")
.AddConnection(socket);
var RecieverHelper = socket.GetRecieveResultsHelper();
string str = await RecieverHelper.ReceiveString();
while (!RecieverHelper.Result.CloseStatus.HasValue)
{
webSocketsManager
.Broadcast("Chat Room", new StringBroadcast(str));
str = await RecieverHelper.ReceiveString();
}
}
catch (Exception e)
{
await socket.SendString("Error!");
await socket.SendString(e.Message);
await socket.SendString(e.ToString());
}
}
else
{
HttpContext.Response.StatusCode = 400;
}
}

Circuit Breaker with gRPC

In a REST service adding a circuit breaker with hystrix, I could do the following:
#HystrixCommand(fallbackMethod = "getBackupResult")
#GetMapping(value = "/result")
public ResponseEntity<ResultDto> getResult(#RequestParam("request") String someRequest) {
ResultDto resultDto = service.parserRequest(someRequest);
return new ResponseEntity<>(resultDto, HttpStatus.OK);
}
public ResponseEntity<ResultDto> getBackupResult(#RequestParam("request") String someRequest) {
ResultDto resultDto = new ResultDto();
return new ResponseEntity<>(resultDto, HttpStatus.OK);
}
Is there something similar I can do for the gRPC call?
public void parseRequest(ParseRequest request, StreamObserver<ParseResponse> responseObserver) {
try {
ParseResponse parseResponse = service.parseRequest(request.getSomeRequest());
responseObserver.onNext(parseResponse);
responseObserver.onCompleted();
} catch (Exception e) {
logger.error("Failed to execute parse request.", e);
responseObserver.onError(new StatusException(Status.INTERNAL));
}
}
I solved my problem by implementing the circuit-breaker on my client. I used the sentinel library
To react on exceptions ratio for example I added this rule:
private static final String KEY = "callGRPC";
private void callGRPC(List<String> userAgents) {
initDegradeRule();
ManagedChannel channel = ManagedChannelBuilder.forAddress(grpcHost, grpcPort).usePlaintext()
.build();
for (String userAgent : userAgents) {
Entry entry = null;
try {
entry = SphU.entry(KEY);
UserAgentServiceGrpc.UserAgentServiceBlockingStub stub
= UserAgentServiceGrpc.newBlockingStub(channel);
UserAgentParseRequest request = UserAgentRequest.newBuilder().setUserAgent(userAgent).build();
UserAgentResponse userAgentResponse = stub.getUserAgentDetails(request);
} catch (BlockException e) {
logger.error("Circuit-breaker is on and the call has been blocked");
} catch (Throwable t) {
logger.error("Exception was thrown", t);
} finally {
if (entry != null) {
entry.exit();
}
}
}
channel.shutdown();
}
private void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
rule.setCount(0.5);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
rule.setTimeWindow(60);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}

How do you print TextArea to a USB Thermal Printer 58mm?(JAVAFX)

So I'm trying to make a billing system in which I want to print a receipt.I was able to do it with some code that I found online,but the font size is too big to print in the 58mm wide paper.I'm not able to adjust the font size.Any kind of help with this issue will be highly appreciated.Thank You.
Here is The Code :
public class PrinterService implements Printable {
public List<String> getPrinters(){
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
PrintService printServices[] = PrintServiceLookup.lookupPrintServices(
flavor, pras);
List<String> printerList = new ArrayList<String>();
for(PrintService printerService: printServices){
printerList.add( printerService.getName());
}
return printerList;
}
#Override
public int print(Graphics g, PageFormat pf, int page)
throws PrinterException {
if (page > 0) { /* We have only one page, and 'page' is zero-based */
return NO_SUCH_PAGE;
}
/*
* User (0,0) is typically outside the imageable area, so we must
* translate by the X and Y values in the PageFormat to avoid clipping
*/
Graphics2D g2d = (Graphics2D) g;
g2d.translate(pf.getImageableX(), pf.getImageableY());
/* Now we perform our rendering */
g.setFont(new Font("Roman", 0, 8));
g.drawString("Hello world !", 0, 10);
return PAGE_EXISTS;
}
public void printString(String printerName, String text) {
// find the printService of name printerName
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
PrintService printService[] = PrintServiceLookup.lookupPrintServices(
flavor, pras);
PrintService service = findPrintService(printerName, printService);
DocPrintJob job = service.createPrintJob();
try {
byte[] bytes;
// important for umlaut chars
bytes = text.getBytes("CP437");
Doc doc = new SimpleDoc(bytes, flavor, null);
job.print(doc, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void printBytes(String printerName, byte[] bytes) {
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
PrintService printService[] = PrintServiceLookup.lookupPrintServices(
flavor, pras);
PrintService service = findPrintService(printerName, printService);
DocPrintJob job = service.createPrintJob();
try {
Doc doc = new SimpleDoc(bytes, flavor, null);
job.print(doc, null);
} catch (Exception e) {
e.printStackTrace();
}
}
private PrintService findPrintService(String printerName,
PrintService[] services) {
for (PrintService service : services) {
if (service.getName().equalsIgnoreCase(printerName)) {
return service;
}
}
return null;
}
}
#FXML
public void printit(ActionEvent actionEvent)
{
PrinterService printerService = new PrinterService();
System.out.println(printerService.getPrinters());
//print some stuff
printerService.printString("POS-58-Series", area.getText());
}

Unable to run second WebClient request after timed out and aborting request

I have a desktop app which is downloading 1 or more small files (jpg with less than 400KB in size and no more than 20 at a time) simultaneously using a CustomWebClient object and calling OpenReadAsync(). The download process is working just fine if there is no problem in the process. I want to limit the response to a certain time (15 sec) so I have introduced a timeOut handling which is Aborting the request. Even the timeout is working and after that my “OpenReadCompletedEventHandler” method is receiving System.Net.WebException: The request was aborted: The request was canceled (which is the right behaviour).
Now, my problem is that I want to allow the user to try re-loading the picture(s). So the next webClient request(s) are failing with the same WebException. Below is my code.
Here is my Custom WebClient class (used in order to have more than 2 async connections at a time):
internal class ExtendedWebClient : WebClient
{
private Timer _timer;
public int ConnectionLimit { get; set; }
public int ConnectionTimeout { get; set; }
public ExtendedWebClient()
{
this.ConnectionLimit = 2;
}
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address) as HttpWebRequest;
if (request != null){_timer = new Timer(TimeoutRequest, request, ConnectionTimeout, Timeout.Infinite);
request.ServicePoint.ConnectionLimit = this.ConnectionLimit;
request.ServicePoint.MaxIdleTime = 5000;
request.ServicePoint.ConnectionLeaseTimeout = 5000;
}
return request;
}
private void TimeoutRequest(object state)
{
_timer.Dispose();
_timer = null;
((WebRequest)state).Abort();
}
protected override void Dispose(bool disposing)
{
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
base.Dispose(disposing);
}
}
Here is the code to download the files using my custom WebClient class:
internal struct PageWaitHandleState
{
public int WaitHandleIndexInPage;
public bool ImageIsLoaded;
public string ErrMessage;
}
public Image[] downloadedImages;
private PageWaitHandleState[] waitHandlesInPage;
private OpenReadCompletedEventHandler[] downloadComplete;
private EventWaitHandle[] pagesEWH = null;
private EventWaitHandle[] downloadImageEvent;
private int availableImages = 1; // Set here to simplify, but as I stated in my description, it may be more than 1.
int downloadTimeOut = 15000;
int maxSimultaneousDownloads = 20;
private void DownloadImages(int pageIndex = 0, string[] imageUrl)
{
if (pagesEWH[pageIndex] != null)
{
ReloadImages(pageIndex, imageUrl); // Executed in the second request
return;
else
{
pagesEWH[pageIndex] = new EventWaitHandle[availableImages];
downloadedImages = new Image[availableImages];
downloadComplete = new OpenReadCompletedEventHandler[availableImages];
downloadImageEvent = new EventWaitHandle[availableImages];
waitHandlesInPage = new PageWaitHandleState[availableImages];
// Set the downloadComplete deletages
for (int i = 0; i < availableImages; i++)
{
downloadComplete[i] = ProcessImage;
}
}
for (int imgCounter = 0; i < availableImages; i++)
{
waitHandlesInPage[imgCounter] = new PageWaitHandleState() { ImageIsLoaded = false, WaitHandleIndexInPage = imgCounter, ErrMessage = null };
downloadImageEvent[imgCounter] = GrabImageAsync(imageUrl[imgCounter], downloadComplete[imgCounter], imgCounter, downloadTimeOut, maxSimultaneousDownloads);
pagesEWH[imgCounter] = downloadImageEvent[imgCounter];
}
offenderIndex++;
}
}
private static EventWaitHandle GrabImageAsync(string url, OpenReadCompletedEventHandler openReadCompletedEventHandler, int imgCounter, int downloadTimeOut, int maxSimultaneousDownloads)
{
var myClient = new ExtendedWebClient();
myClient.ConnectionLimit = maxSimultaneousDownloads;
myClient.ConnectionTimeout = downloadTimeOut;
myClient.OpenReadCompleted += openReadCompletedEventHandler;
var iewh = new ImageEventWaitHandle() { ewh = new EventWaitHandle(false, EventResetMode.ManualReset), ImageIndex = imgCounter };
myClient.OpenReadAsync(new Uri(url), iewh);
return iewh.ewh;
}
internal void ProcessImage(object sender, OpenReadCompletedEventArgs e)
{
ImageEventWaitHandle iewh = (ImageEventWaitHandle)e.UserState;
bool disposeObject = false;
try
{
if (e.Cancelled)
{
this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = false;
this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = "WebClient request was cancelled";
}
else if (e.Error != null)
{
this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = false;
this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = e.Error.Message;
iewh.ewh.Set();
this.downloadImageEvent[iewh.ImageIndex].Close();
}
else
{
using (Stream inputStream = e.Result)
using (MemoryStream ms = new MemoryStream())
{
byte[] buffer = new byte[4096];
int bytesRead;
int totalReadBytes = 0;
do
{
bytesRead = inputStream.Read(buffer, 0, buffer.Length); // Exception fired here with the second request
ms.Write(buffer, 0, bytesRead);
totalReadBytes += bytesRead;
} while (inputStream.CanRead && bytesRead > 0);
this.downloadedImages[iewh.ImageIndex] = Image.FromStream(ms);
this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = true;
this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = null;
}
disposeObject = true;
}
}
catch (Exception exc)
{
this.downloadedImages[iewh.ImageIndex] = null;
}
finally
{
// Signal the wait handle
if (disposeObject)
{
iewh.ewh.Set();
((WebClient)sender).Dispose();
}
}
}
private void ReloadImages(int pageIndex, string[] imageUrl)
{
for (int imgCounter = 0; imgCounter < availableImages; imgCounter++)
{
this.downloadComplete[imgCounter] = this.ProcessImage;
this.waitHandlesInPage[imgCounter] = new PageWaitHandleState() { ImageIsLoaded = false, WaitHandleIndexInPage = imgCounter, ErrMessage = null };
this.downloadImageEvent[imgCounter] = GrabImageAsync(ImageUrl[imgCounter],this.downloadComplete[imgCounter], imgCounter, downloadTimeOut, maxSimultaneousDownloads);
this.pagesEWH[imgCounter] = this.downloadImageEvent[imgCounter];
}
}
Finally, when I want to access the images I check if they are ready by using:
private bool ImagesInPageReady(int pageIndex, int recordsInCurrentPage)
{
if (_PagesEWH[pageIndex] != null)
{
int completedDownloadsCount = 0;
bool waitHandleSet;
// Wait for the default images first (imgCounter = 0). When moving page or asking for more pictures, then wait for the others.
for (int ewhIndexInPage = 0; ewhIndexInPage < recordsInCurrentPage; ewhIndexInPage++)
{
if (this.pagesEWH[ewhIndexInPage].WaitOne(this.downloadTimeOut))
{
if (this.WaitHandlesInPage[ewhIndexInPage].ImageIsLoaded)
{
completedDownloadsCount++;
}
}
else
{
this.pagesEWH[ewhIndexInPage].Set();
}
}
return (completedDownloadsCount > 0);
}
return false;
}
#usr, thanks for pointing me in the right direction. HttpClient was the solution. So I basically encapsulated my HttpClient object in a new class, together with the ProcessImage() method and exposing and event fired by the same method.

BlackBerry - Exception when sending SMS

The code below should send a text message to a mobile number. It currently fails to work properly.
When the program attempts a message, the following error is reported:
Blocking operation not permitted on event dispatch thread
I created a separate thread to execute the SMS code, but I am still observing the same exception.
What am I doing wrong?
class DummyFirst extends MainScreen {
private Bitmap background;
private VerticalFieldManager _container;
private VerticalFieldManager mainVerticalManager;
private HorizontalFieldManager horizontalFldManager;
private BackGroundThread _thread;
CustomControl buttonControl1;
public DummyFirst() {
super();
LabelField appTitle = new LabelField("Dummy App");
setTitle(appTitle);
background = Bitmap.getBitmapResource("HomeBack.png");
_container = new VerticalFieldManager(Manager.NO_VERTICAL_SCROLL
| Manager.NO_VERTICAL_SCROLLBAR) {
protected void paint(Graphics g) {
// Instead of these next two lines, draw your bitmap
int y = DummyFirst.this.getMainManager()
.getVerticalScroll();
g.clear();
g.drawBitmap(0, 0, background.getWidth(), background
.getHeight(), background, 0, 0);
super.paint(g);
}
protected void sublayout(int maxWidth, int maxHeight) {
int width = background.getWidth();
int height = background.getHeight();
super.sublayout(width, height);
setExtent(width, height);
}
};
mainVerticalManager = new VerticalFieldManager(
Manager.NO_VERTICAL_SCROLL |
Manager.NO_VERTICAL_SCROLLBAR) {
protected void sublayout(int maxWidth, int maxHeight) {
int width = background.getWidth();
int height = background.getHeight();
super.sublayout(width, height);
setExtent(width, height);
}
};
HorizontalFieldManager horizontalFldManager =
new HorizontalFieldManager(Manager.USE_ALL_WIDTH);
buttonControl1 = new CustomControl("Send", ButtonField.CONSUME_CLICK,
83, 15);
horizontalFldManager.add(buttonControl1);
this.setStatus(horizontalFldManager);
FieldListener listner = new FieldListener();
buttonControl1.setChangeListener(listner);
_container.add(mainVerticalManager);
this.add(_container);
}
class FieldListener implements FieldChangeListener {
public void fieldChanged(Field f, int context) {
if (f == buttonControl1) {
_thread = new BackGroundThread();
_thread.start();
}
}
}
private class BackGroundThread extends Thread {
public BackGroundThread() {
/*** initialize parameters in constructor *****/
}
public void run() {
// UiApplication.getUiApplication().invokeLater(new Runnable()
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
try {
MessageConnection msgConn =
(MessageConnection) Connector
.open("sms://:0");
Message msg = msgConn
.newMessage(
MessageConnection.TEXT_MESSAGE);
TextMessage txtMsg = (TextMessage) msg;
String msgAdr = "sms://+919861348735";
txtMsg.setAddress(msgAdr);
txtMsg.setPayloadText("Test Message");
// here exception is thrown
msgConn.send(txtMsg);
System.out.println("Sending" +
" SMS success !!!");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
} // run
});
}
}
public boolean onClose() {
System.out.println("close event called, request to be" +
" in the backgroud....");
UiApplication.getUiApplication().requestBackground();
return true;
}
}
Dec 14, 2009 Stella answered their own question:
I resolved this issue by creating a separate thread and then not using Port etc.
Here it is:
SMSThread smsthread = new SMSThread("Some message",mobNumber);
smsthread.start();
class SMSThread extends Thread {
Thread myThread;
MessageConnection msgConn;
String message;
String mobilenumber;
public SMSThread( String textMsg, String mobileNumber ) {
message = textMsg;
mobilenumber = mobileNumber;
}
public void run() {
try {
msgConn = (MessageConnection) Connector.open("sms://+"+ mobilenumber);
TextMessage text = (TextMessage) msgConn.newMessage(MessageConnection.TEXT_MESSAGE);
text.setPayloadText(message);
msgConn.send(text);
msgConn.close();
} catch (Exception e) {
System.out.println("Exception: " + e);
}
}
}

Resources