How can I adjust the reading speed for the mongodb? - reactor

Flux.range(1,100).delayElements(Duration.ofSeconds(10)).subscribe(i-> System.out.println(i));
When the Flux emmiter to 10 , I want to delayElement to 1 minute to this subscribe
The background was when I read some data from mongoDB and write to Elasticsearch,
but I want to dynamic to control the reading speed and don't want to exhaust the mongodb resource.
Flux<List<Document>> readFromMongoDB = getFromMongoDB();
product_2014.subscribe(new BaseSubscriber<List<Document>>() {
int counter;
#Override
protected void hookOnSubscribe(Subscription subscription) {
subscription.request(1);
}
#SneakyThrows
#Override
protected void hookOnNext(List<Document> value) {
Thread.sleep(1000);
if (counter == 1) {
counter = 0;
}
else {
counter++;
}
upstream().request(0);
upstream().cancel();
log.info("aaaaaaaaaaa");
}
});
ParallelFlux<List<Document>> getFromMongoDB(String product,
int size,
MongoDatabase mongoDatabase,
int parallel,
Duration duration) {
Publisher<Long> publisher = mongoDatabase.getCollection(product)
.countDocuments();
Mono<Long> count = Mono.from(publisher);
return count.flatMapMany(l -> {
log.info("split counter");
return Flux.range(0, (int) (l / size) + 1);
})
.log().doOnSubscribe(subscription -> {
log.info("doOnSubscribe");
})
.parallel(parallel,1)
.runOn(scheduler)
.doOnNext(integer -> log.info("get page = {}", integer))
.concatMap(page -> {
log.info("page in {}", page);
FindPublisher<Document> limit =
mongoDatabase.getCollection(product)
.find(Document.class)
.skip(page * size)
.limit(size);
Mono<List<Document>> listDocument = Flux.from(limit)
.publishOn(scheduler)
.collectList()
.doOnNext(list -> {
log.info("{} in list",
page);
});
return listDocument;
});
}
Why I can cacel the subscrition.And the Flux still emit the page element?
And how should I do?
I want to paralle to read from mongodb and dynamic control the reading speed.

I only see it like this:
Flux.range(1, 100)
.flatMap(el -> {
Mono<Integer> elMono = Mono.just(el);
if (el <= 10) {
return elMono.delayElement(Duration.ofMinutes(1));
}
else {
return elMono.delayElement(Duration.ofSeconds(10));
}
})
.subscribe(i-> System.out.println(i));
You examine each element and decide how to delay exactly this one.

Related

Pagination and Sorting - custom sorting of data

I have a problem with sorting data in my project.
Since I implemented pagination I don't know how solve this issue.
Before pagination I fetched whole list of entities and sort it by this class:
public class EntitySorter {
private static final Logger LOG = Logger.getLogger(EntitySorter.class);
public static int sort(String s1, String s2) {
if (StringUtils.isBlank(s1) || StringUtils.isBlank(s2)) {
return -1;
}
if (!s1.contains("/") || !s2.contains("/")) {
return -1;
}
if (s1.substring(s1.lastIndexOf("/") + 1).length() != 4 ||
s2.substring(s2.lastIndexOf("/") + 1).length() != 4) {
return -1;
}
final String year1 = s1.substring(s1.lastIndexOf("/") + 1);
final String year2 = s2.substring(s2.lastIndexOf("/") + 1);
if (!NumberUtils.isDigits(year1) || !NumberUtils.isDigits(year2)) {
return -1;
}
final int result = NumberUtils.toInt(year1) - NumberUtils.toInt(year2);
if (result != 0) {
return result;
}
final String caseNumber1 = s1.substring(0, s1.indexOf("/"));
final String caseNumber2 = s2.substring(0, s2.indexOf("/"));
if (!NumberUtils.isDigits(caseNumber1) && NumberUtils.isDigits(caseNumber2)) {
try {
final int intCaseNumber1 = Integer.parseInt(caseNumber1.replaceAll("[^0-9]", ""));
return intCaseNumber1 - Integer.parseInt(caseNumber2);
} catch (NumberFormatException e) {
LOG.error(e.getMessage(), e);
}
return -1;
}
if (NumberUtils.isDigits(caseNumber1) && !NumberUtils.isDigits(caseNumber2)) {
try {
final int intCaseNumber2 = Integer.parseInt(caseNumber2.replaceAll("[^0-9]", ""));
return Integer.parseInt(caseNumber1) - intCaseNumber2;
} catch (NumberFormatException e) {
LOG.error(e.getMessage(), e);
}
return -1;
}
if (!NumberUtils.isDigits(caseNumber1) && !NumberUtils.isDigits(caseNumber2)) {
try {
final int intCaseNumber1 = Integer.parseInt(caseNumber1.replaceAll("[^0-9]", ""));
final int intCaseNumber2 = Integer.parseInt(caseNumber2.replaceAll("[^0-9]", ""));
return intCaseNumber1 - intCaseNumber2;
} catch (NumberFormatException e) {
LOG.error(e.getMessage(), e);
}
return -1;
}
return NumberUtils.toInt(caseNumber1) - NumberUtils.toInt(caseNumber2);
}
}
Let's take some example:
We have a list of IDs:
101/2021
102/2021
1/2022
86/2020
Correct sorted list is:
1/2022
102/2021
101/2021
86/2020
In database this ID is one column. It's not split to number and year. I tried to use Sort.by() but I didn't make a success. How can I use pagination and keep correct sorting?
For pagination to work optimally, the data should be indexed correctly..
If there is a different representation of the data you can use with one column then it's the best way.
If not then the easy way would just to decompose one column to multiple columns, create a multi column index and sort by these columns + you need to understand if the natural ordering of the columns fits your logic.
The hard way would be to create user defined function and index on it and other solutions, but I would avoid the unnecessary complexity.
Keep it simple!

MediaFormat options, the KEY_ROTATION is not work

The current image is encoded and stored in the output buffer as it is.
However, Samsung (Galaxy S6(Nouga), 7(Nouga), 8(Nouga)) KEY_ROTATION option works well,
This option does not work on the Google reference phone (Pixel 2 XL -> Oreo, Nexus 5 -> Lollipop).
In other words, while the KEY_ROTATION option works well on the Samsung device, the value output to the output buffer is rotated,
This option does not work on Google reference phones.
The surface created by encoderSurface () serves as an input buffer
Check the output buffer through onOutputBufferAvailable of MediaCodec.Callback.
Anyone who knows the reason needs help.
Here is the code I wrote.
private final MediaCodec.Callback _mediaCodecCallback = new MediaCodec.Callback() {
#Override
public void finalize(){
}
#Override
public void onInputBufferAvailable(#NonNull MediaCodec mediaCodec, int i) {
}
#Override
public void onOutputBufferAvailable(#NonNull MediaCodec mediaCodec, int i, #NonNull MediaCodec.BufferInfo bufferInfo) {
ByteBuffer buffer = mediaCodec.getOutputBuffer(i);
byte[] outData = new byte[bufferInfo.size];
buffer.get(outData);
mediaCodec.releaseOutputBuffer(i, false);
switch (bufferInfo.flags) {
case MediaCodec.BUFFER_FLAG_CODEC_CONFIG:
if(DEBUG_LOG) {
Log.v(TAG, "CONFIG FRAME");
}
_configFrame = new byte[bufferInfo.size];
System.arraycopy(outData, 0, _configFrame, 0, outData.length);
break;
case MediaCodec.BUFFER_FLAG_KEY_FRAME:
// I Frame;
if(DEBUG_LOG) {
Log.v(TAG, "I FRAME" +
", SIZE = " + bufferInfo.size + ", " + currentDateandTime);
}
try {
_outputFrame.reset();
_outputFrame.write(_configFrame);
_outputFrame.write(outData);
} catch (IOException e) {
e.printStackTrace();
}
break;
default:
// P Frame;
if(DEBUG_LOG) {
Log.v(TAG, "P FRAME" +
", SIZE = " + bufferInfo.size);
}
break;
}
}
//
public boolean initialize(int width,int height) {
try {
_mediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
} catch (IOException e) {
e.printStackTrace();
return false;
}
MediaCodecInfo codec = selectCodec(MIME_TYPE);
if (codec == null) {
return false;
}
MediaCodecInfo.CodecCapabilities cap = codec.getCapabilitiesForType(MIME_TYPE);
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRateInfo.getInstance().getBitRate()); // 300000
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mediaFormat.setInteger(MediaFormat.KEY_ROTATION, 90);
// in under API 21, not use MediaFormat.KEY_ROTATION, but use "rotation-degrees"
// This does not work on a Google reference phone.
_mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
_surface = _mediaCodec.createInputSurface();
_mediaCodec.setCallback(_mediaCodecCallback);
_mediaCodec.start();
Log.d(TAG,"Encoder Start SUCCESS");
return true;
}
public Surface encoderSurface() {
//Return the value of this function and use it as the input surface.
return _surface;
}
please Help me ..

I have a long time to stay at making some RxJava codes better to average some data

I have a collection of data like dummy below
class Place {
userId,
price
}
That means a collection of some places.
Use-case:
There is a user with userId and login.
How to calc average place-price that equal to userId ?
RxJava is nice and I have tried filter and toList, however it is not so performance nice.
Observable.fromIterable(places)
.subscribeOn(Schedulers.newThread())
.filter(new Predicate<Place>() {
#Override
public boolean test(Place place) throws Exception {
return place.userId == global.login.userId;
}
})
.toList()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Place>>() {
#Override
public void accept(List<Place> filteredPlace) throws Exception {
//Here I have to use loop to do math-average, it is not nice to average.
}
});
If the places is something that is already available in-memory, you can rearrange the evaluation such as this:
Observable.just(places)
.subscribeOn(Schedulers.computation())
.map((Iterable<Place> list) -> {
double sum = 0.0;
int count = 0;
for (Place p : list) {
if (p.userId == global.login.userId) {
sum += p.price;
count++;
}
}
return sum / count;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(average -> { /* display average */ });
If the sequence of places becomes available over time (through an Observable):
Observable<Place> places = ...
places
.observeOn(Schedulers.computation())
.filter((Place p) -> p.userId == global.login.userId)
.compose(o -> MathObservable.averageDouble(o.map(p -> p.price)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(average -> { /* display average */ });
MathObservable is part of the RxJava 2 Extensions library.

Running Multiple threads in queue using BlockingCollections

My program has 3 functions. Each function takes a list of Items and fill certain information.
For example
class Item {
String sku,upc,competitorName;
double price;
}
function F1 takes a List and fills upc
function F2 takes List (output of F1) and fills price.
function F3 takes List (output of F2) and fills competitorName
F1 can process 5 items at a time,
F2 can process 20 items at a time,
F3 also 20.
Right now I am running F1 -> F2 -> F3 in serial because F2 needs info(UPC code) from F1. F3 needs price from F2.
I would like to make this process efficient by running F1 run continuously instead of waiting for F2 and F3 to be completed. F1 executes and output into queue then F2 takes 20 items at a time and process them. and then follows F3.
How can i achieve this by using BlockingCollection and Queue?
This is a typical use case of Apache Storm in case you've continuous items coming in to F1. You can implement this in Storm in matter of minutes and you'll have fast and perfectly parallel system in place. Your F1, F2 and F3 will become bolts and your Items producer will become spout.
Since you asked how to do it using BlockingCollections here is an implementation. You'll need 3 threads in total.
ItemsProducer: It is producing 5 items at a time and feeding it to F1.
F2ExecutorThread: It is consuming 20 items at a time and feeding it to F2.
F3ExecutorThread: It is consuming 20 items at a time and feeding it to F3.
You also have 2 blocking queues one is used to transfer data from F1->F2 and one from F2->F3. You can also have a queue to feed data to F1 in similar fashion if required. It depends upon how you are getting the items. I've used Thread.sleep to simulate the time required to execute the function.
Each function will keep looking for items in their assigned queue, irrespective of what other functions are doing and wait until the queue has items. Once they've processed the item they'll put it in another queue for another function. They'll wait until the other queue has space if it is full.
Since all your functions are running in different threads, F1 won't be waiting for F2 or F3 to finish. If your F2 and F3 are significantly faster then F1 you can assign more threads to F1 and keep pushing to same f2Queue.
public class App {
final BlockingQueue<Item> f2Queue = new ArrayBlockingQueue<>(100);
final BlockingQueue<Item> f3Queue = new ArrayBlockingQueue<>(100);
public static void main(String[] args) throws InterruptedException {
App app = new App();
app.start();
}
public void start() throws InterruptedException {
Thread t1 = new ItemsProducer(f2Queue);
Thread t2 = new F2ExecutorThread(f2Queue, f3Queue);
Thread t3 = new F3ExecutorThread(f3Queue);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
/**
* Thread producing 5 items at a time and feeding it to f1()
*/
class ItemsProducer extends Thread {
private BlockingQueue<Item> f2Queue;
private static final int F1_BATCH_SIZE = 5;
public ItemsProducer(BlockingQueue<Item> f2Queue) {
this.f2Queue = f2Queue;
}
public void run() {
Random random = new Random();
while (true) {
try {
List<Item> items = new ArrayList<>();
for (int i = 0; i < F1_BATCH_SIZE; i++) {
Item item = new Item(String.valueOf(random.nextInt(100)));
Thread.sleep(20);
items.add(item);
System.out.println("Item produced: " + item);
}
// Feed items to f1
f1(items);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void f1(List<Item> items) throws InterruptedException {
Random random = new Random();
for (Item item : items) {
Thread.sleep(100);
item.upc = String.valueOf(random.nextInt(100));
f2Queue.put(item);
}
}
}
/**
* Thread consuming items produced by f1(). It takes 20 items at a time, but if they are not
* available it waits and starts processesing as soon as one gets available
*/
class F2ExecutorThread extends Thread {
static final int F2_BATCH_SIZE = 20;
private BlockingQueue<Item> f2Queue;
private BlockingQueue<Item> f3Queue;
public F2ExecutorThread(BlockingQueue<Item> f2Queue, BlockingQueue<Item> f3Queue) {
this.f2Queue = f2Queue;
this.f3Queue = f3Queue;
}
public void run() {
try {
List<Item> items = new ArrayList<>();
while (true) {
items.clear();
if (f2Queue.drainTo(items, F2_BATCH_SIZE) == 0) {
items.add(f2Queue.take());
}
f2(items);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void f2(List<Item> items) throws InterruptedException {
Random random = new Random();
for (Item item : items) {
Thread.sleep(100);
item.price = random.nextInt(100);
f3Queue.put(item);
}
}
}
/**
* Thread consuming items produced by f2(). It takes 20 items at a time, but if they are not
* available it waits and starts processesing as soon as one gets available.
*/
class F3ExecutorThread extends Thread {
static final int F3_BATCH_SIZE = 20;
private BlockingQueue<Item> f3Queue;
public F3ExecutorThread(BlockingQueue<Item> f3Queue) {
this.f3Queue = f3Queue;
}
public void run() {
try {
List<Item> items = new ArrayList<>();
while (true) {
items.clear();
if (f3Queue.drainTo(items, F3_BATCH_SIZE) == 0) {
items.add(f3Queue.take());
}
f3(items);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void f3(List<Item> items) throws InterruptedException {
Random random = new Random();
for (Item item : items) {
Thread.sleep(100);
item.competitorName = String.valueOf(random.nextInt(100));
System.out.println("Item done: " + item);
}
}
}
class Item {
String sku, upc, competitorName;
double price;
public Item(String sku) {
this.sku = sku;
}
public String toString() {
return "sku: " + sku + " upc: " + upc + " price: " + price + " compName: " + competitorName;
}
}
I guess you can follow the exact same approach in .Net as well. For better understanding I suggest you to go through basic architecture of http://storm.apache.org/releases/current/Tutorial.html
I tried to do same thing in .NET and i think it is working.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace BlockingCollectionExample
{
class Program
{
static void Main(string[] args)
{
BlockingCollection<Listing> needUPCJobs = new BlockingCollection<Listing>();
BlockingCollection<Listing> needPricingJobs = new BlockingCollection<Listing>();
// This will have final output
List<Listing> output = new List<Listing>();
// start executor 1 which waits for data until available
var executor1 = Task.Factory.StartNew(() =>
{
int maxSimutenousLimit = 5;
int gg = 0;
while (true)
{
while (needUPCJobs.Count >= maxSimutenousLimit)
{
List<Listing> tempListings = new List<Listing>();
for (int i = 0; i < maxSimutenousLimit; i++)
{
Listing listing = new Listing();
if (needUPCJobs.TryTake(out listing))
tempListings.Add(listing);
}
// Simulating some delay for first executor
Thread.Sleep(1000);
foreach (var eachId in tempListings)
{
eachId.UPC = gg.ToString();
gg++;
needPricingJobs.Add(eachId);
}
}
if (needUPCJobs.IsAddingCompleted)
{
if (needUPCJobs.Count == 0)
break;
else
maxSimutenousLimit = needUPCJobs.Count;
}
}
needPricingJobs.CompleteAdding();
});
// start executor 2 which waits for data until available
var executor2 = Task.Factory.StartNew(() =>
{
int maxSimutenousLimit = 10;
int gg = 10;
while (true)
{
while (needPricingJobs.Count >= maxSimutenousLimit)
{
List<Listing> tempListings = new List<Listing>();
for (int i = 0; i < maxSimutenousLimit; i++)
{
Listing listing = new Listing();
if (needPricingJobs.TryTake(out listing))
tempListings.Add(listing);
}
// Simulating more delay for second executor
Thread.Sleep(10000);
foreach (var eachId in tempListings)
{
eachId.Price = gg;
gg++;
output.Add(eachId);
}
}
if (needPricingJobs.IsAddingCompleted)
{
if(needPricingJobs.Count==0)
break;
else
maxSimutenousLimit = needPricingJobs.Count;
}
}
});
// producer thread
var producer = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100; i++)
{
needUPCJobs.Add(new Listing() { ID = i });
}
needUPCJobs.CompleteAdding();
});
// wait for producer to finish producing
producer.Wait();
// wait for all executors to finish executing
Task.WaitAll(executor1, executor2);
Console.WriteLine();
Console.WriteLine();
}
}
public class Listing
{
public int ID;
public string UPC;
public double Price;
public Listing() { }
}
}

JeroMQ: connection does not recover reliably

I have two applications, sending messages asynchronously in both directions. I am using sockets of type ZMQ.DEALER on both sides. The connection status is additionally controlled by heartbeating.
I have now problems to get the connection reliably recovering after connection problems (line failure or application restart on one side). When I restart the applicaton on the server side (the side doing the bind()), the client side will not always reconnect successfully and then needs to be restarted, especially when the local buffer has reached the HWM limit.
I did not find any other way to make the connection recovery reliable, other than resetting the complete ZMQ.Context in case of heartbeat failures or if send() returned false. I will then call Context.term() and will create Context and Socket again. This seemed to work fine in my tests. But now I observed occasional and hangups inside Context.term(), which are rare and hard to reproduce. I know, that creating the Context should be done just once at application startup, but as said I found no other way to re-establish a broken connection.
I am using JeroMQ 0.3.4. The source of a test application is below, ~200 lines of code.
Any hints to solve this are very much appreciated.
import java.util.Calendar;
import org.zeromq.ZMQ;
public class JeroMQTest {
public interface IMsgListener {
public void newMsg(byte[] message);
}
final static int delay = 100;
final static boolean doResetContext = true;
static JeroMQTest jeroMQTest;
static boolean isServer;
private ZMQ.Context zContext;
private ZMQ.Socket zSocket;
private String address = "tcp://localhost:9889";
private long lastHeartbeatReceived = 0;
private long lastHeartbeatReplyReceived;
private boolean sendStat = true, serverIsActive = false, receiverInterrupted = false;
private Thread receiverThread;
private IMsgListener msgListener;
public static void main(String[] args) {
isServer = args.length > 0 && args[0].equals("true");
if (isServer) {
new JeroMQTest().runServer();
}
else {
new JeroMQTest().runClient();
}
}
public void runServer() {
msgListener = new IMsgListener() {
public void newMsg(byte[] message) {
String msgReceived = new String(message);
if (msgReceived.startsWith("HEARTBEAT")) {
String msgSent = "HEARTBEAT_REP " + msgReceived.substring(10);
sendStat = zSocket.send(msgSent.getBytes());
System.out.println("heartbeat rcvd, reply sent, status:" + sendStat);
lastHeartbeatReceived = getNow();
} else {
System.out.println("msg received:" + msgReceived);
}
}
};
createJmq();
sleep(1000);
int ct = 1;
while (true) {
boolean heartbeatsOk = lastHeartbeatReceived > getNow() - delay * 4;
if (heartbeatsOk) {
serverIsActive = true;
String msg = "SERVER " + ct;
sendStat = zSocket.send(msg.getBytes());
System.out.println("msg sent:" + msg + ", status:" + sendStat);
ct++;
}
if (serverIsActive && (!heartbeatsOk || !sendStat)) {
serverIsActive = false;
if (doResetContext) {
resetContext();
}
}
sleep(delay);
}
}
public void runClient() {
msgListener = new IMsgListener() {
public void newMsg(byte[] message) {
String msgReceived = new String(message);
if (msgReceived.startsWith("HEARTBEAT_REP")) {
System.out.println("HEARTBEAT_REP received:" + msgReceived);
lastHeartbeatReplyReceived = getNow();
}
else {
System.out.println("msg received:" + msgReceived);
}
}
};
createJmq();
sleep(1000);
int ct = 1;
boolean reconnectDone = false;
while (true) {
boolean heartbeatsOK = lastHeartbeatReplyReceived > getNow() - delay * 4;
String msg = "HEARTBEAT " + (ct++);
sendStat = zSocket.send(msg.getBytes());
System.out.println("heartbeat sent:" + msg + ", status:" + sendStat);
sleep(delay / 2);
if (sendStat) {
msg = "MSG " + ct;
sendStat = zSocket.send(msg.getBytes());
System.out.println("msg sent:" + msg + ", status:" + sendStat);
reconnectDone = false;
}
if ((!heartbeatsOK && lastHeartbeatReplyReceived > 0) || (!sendStat && !reconnectDone)) {
if (doResetContext) {
resetContext();
}
lastHeartbeatReplyReceived = 0;
reconnectDone = true;
}
sleep(delay / 2);
}
}
public void resetContext() {
closeJmq();
sleep(1000);
createJmq();
System.out.println("resetContext done");
}
private void createJmq() {
zContext = ZMQ.context(1);
zSocket = zContext.socket(ZMQ.DEALER);
zSocket.setSendTimeOut(100);
zSocket.setReceiveTimeOut(100);
zSocket.setSndHWM(10);
zSocket.setRcvHWM(10);
zSocket.setLinger(100);
if (isServer) {
zSocket.bind(address);
} else {
zSocket.connect(address);
}
receiverThread = new Thread() {
public void run() {
receiverInterrupted = false;
try {
ZMQ.Poller poller = new ZMQ.Poller(1);
poller.register(zSocket, ZMQ.Poller.POLLIN);
while (!receiverInterrupted) {
if (poller.poll(100) > 0) {
byte byteArr[] = zSocket.recv(0);
msgListener.newMsg(byteArr);
}
}
poller.unregister(zSocket);
} catch (Throwable e) {
System.out.println("Exception in ReceiverThread.run:" + e.getMessage());
}
}
};
receiverThread.start();
}
public void closeJmq() {
receiverInterrupted = true;
sleep(100);
zSocket.close();
zContext.term();
}
long getNow() {
Calendar now = Calendar.getInstance();
return (long) (now.getTime().getTime());
}
private static void sleep(int mSleep) {
try {
Thread.sleep(mSleep);
} catch (InterruptedException e) {
}
}
}

Resources