extract LwHttp from MapDatabase

This commit is contained in:
Hannes Janetzek 2013-02-17 17:24:05 +01:00
parent 99dafda4b9
commit d25d967332
2 changed files with 480 additions and 431 deletions

View File

@ -0,0 +1,431 @@
/*
* Copyright 2013 OpenScienceMap
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.database.oscimap;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URL;
import org.oscim.core.Tile;
import android.os.SystemClock;
import android.util.Log;
public class LwHttp {
private static final String TAG = LwHttp.class.getName();
private String mHost;
private int mPort;
private long mContentLenth;
private InputStream mInputStream;
private final int BUFFER_SIZE = 65536;
byte[] buffer = new byte[BUFFER_SIZE];
// position in buffer
int bufferPos;
// bytes available in buffer
int bufferFill;
// overall bytes of content read
private int mReadPos;
private int mMaxReq = 0;
private Socket mSocket;
private OutputStream mCommandStream;
private InputStream mResponseStream;
long mLastRequest = 0;
private SocketAddress mSockAddr;
private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes();
private final static int RESPONSE_EXPECTED_LIVES = 100;
private final static int RESPONSE_EXPECTED_TIMEOUT = 10000;
private byte[] REQUEST_GET_START;
private byte[] REQUEST_GET_END;
private byte[] mRequestBuffer;
boolean setServer(String urlString) {
URL url;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
e.printStackTrace();
return false;
//return new OpenResult("invalid url: " + options.get("url"));
}
int port = url.getPort();
if (port < 0)
port = 80;
String host = url.getHost();
String path = url.getPath();
Log.d(TAG, "open oscim database: " + host + " " + port + " " + path);
REQUEST_GET_START = ("GET " + path).getBytes();
REQUEST_GET_END = (".osmtile HTTP/1.1\n" +
"Host: " + host + "\n" +
"Connection: Keep-Alive\n\n").getBytes();
mHost = host;
mPort = port;
mRequestBuffer = new byte[1024];
System.arraycopy(REQUEST_GET_START, 0,
mRequestBuffer, 0, REQUEST_GET_START.length);
return true;
}
void close() {
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
mSocket = null;
}
}
}
int readHeader() throws IOException {
InputStream is = mResponseStream;
byte[] buf = buffer;
boolean first = true;
int read = 0;
int pos = 0;
int end = 0;
int len = 0;
// header cannot be larger than BUFFER_SIZE for this to work
for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) {
read += len;
while (end < read && (buf[end] != '\n'))
end++;
if (buf[end] == '\n') {
if (first) {
// check only for OK
first = false;
if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15))
return -1;
} else if (end - pos == 1) {
// check empty line (header end)
end += 1;
break;
}
// String line = new String(buf, pos, end - pos - 1);
// Log.d(TAG, ">" + line + "< " + resp_len);
pos += (end - pos) + 1;
end = pos;
}
}
// check 4 bytes available..
while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0)
read += len;
if (read - len < 4)
return -1;
int contentLength = decodeInt(buf, end);
mContentLenth = contentLength;
// buffer fill
bufferFill = read;
// start of content
bufferPos = end + 4;
// bytes of content already read into buffer
mReadPos = read - bufferPos;
mInputStream = mResponseStream;
return contentLength;
}
boolean sendRequest(Tile tile) throws IOException {
bufferFill = 0;
bufferPos = 0;
mReadPos = 0;
mCacheFile = null;
if (mSocket != null && ((mMaxReq-- <= 0)
|| (SystemClock.elapsedRealtime() - mLastRequest
> RESPONSE_EXPECTED_TIMEOUT))) {
try {
mSocket.close();
} catch (IOException e) {
}
// Log.d(TAG, "not alive - recreate connection " + mMaxReq);
mSocket = null;
}
if (mSocket == null) {
lwHttpConnect();
// we know our server
mMaxReq = RESPONSE_EXPECTED_LIVES;
// Log.d(TAG, "create connection");
} else {
// should not be needed
int avail = mResponseStream.available();
if (avail > 0) {
Log.d(TAG, "Consume left-over bytes: " + avail);
mResponseStream.read(buffer, 0, avail);
}
}
byte[] request = mRequestBuffer;
int pos = REQUEST_GET_START.length;
pos = writeInt(tile.zoomLevel, pos, request);
request[pos++] = '/';
pos = writeInt(tile.tileX, pos, request);
request[pos++] = '/';
pos = writeInt(tile.tileY, pos, request);
int len = REQUEST_GET_END.length;
System.arraycopy(REQUEST_GET_END, 0, request, pos, len);
len += pos;
// this does the same but with a few more allocations:
// byte[] request = String.format(REQUEST,
// Integer.valueOf(tile.zoomLevel),
// Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes();
try {
mCommandStream.write(request, 0, len);
mCommandStream.flush();
return true;
} catch (IOException e) {
Log.d(TAG, "recreate connection");
}
lwHttpConnect();
mCommandStream.write(request, 0, len);
mCommandStream.flush();
return true;
}
private boolean lwHttpConnect() throws IOException {
if (mSockAddr == null)
mSockAddr = new InetSocketAddress(mHost, mPort);
mSocket = new Socket();
mSocket.connect(mSockAddr, 30000);
mSocket.setTcpNoDelay(true);
mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream();
mResponseStream = mSocket.getInputStream();
return true;
}
// write (positive) integer as char sequence to buffer
private static int writeInt(int val, int pos, byte[] buf) {
if (val == 0) {
buf[pos] = '0';
return pos + 1;
}
int i = 0;
for (int n = val; n > 0; n = n / 10, i++)
buf[pos + i] = (byte) ('0' + n % 10);
// reverse bytes
for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) {
byte tmp = buf[j];
buf[j] = buf[end];
buf[end] = tmp;
}
return pos + i;
}
private static boolean compareBytes(byte[] buffer, int position, int available,
byte[] string, int length) {
if (available - position < length)
return false;
for (int i = 0; i < length; i++)
if (buffer[position + i] != string[i])
return false;
return true;
}
static int decodeInt(byte[] buffer, int offset) {
return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16
| (buffer[offset + 2] & 0xff) << 8
| (buffer[offset + 3] & 0xff);
}
void readBuffer(int size) throws IOException {
// check if buffer already contains the request bytes
if (bufferPos + size < bufferFill)
return;
// check if inputstream is read to the end
if (mReadPos == mContentLenth)
return;
int maxSize = buffer.length;
if (size > maxSize) {
Log.d(TAG, "increase read buffer to " + size + " bytes");
maxSize = size;
byte[] tmp = new byte[maxSize];
bufferFill -= bufferPos;
System.arraycopy(buffer, bufferPos, tmp, 0, bufferFill);
bufferPos = 0;
buffer = tmp;
}
if (bufferFill == bufferPos) {
bufferPos = 0;
bufferFill = 0;
} else if (bufferPos + size > maxSize) {
// copy bytes left to the beginning of buffer
bufferFill -= bufferPos;
System.arraycopy(buffer, bufferPos, buffer, 0, bufferFill);
bufferPos = 0;
}
int max = maxSize - bufferFill;
while ((bufferFill - bufferPos) < size && max > 0) {
max = maxSize - bufferFill;
if (max > mContentLenth - mReadPos)
max = (int) (mContentLenth - mReadPos);
// read until requested size is available in buffer
int len = mInputStream.read(buffer, bufferFill, max);
if (len < 0) {
// finished reading, mark end
buffer[bufferFill] = 0;
break;
}
mReadPos += len;
// if (mCacheFile != null)
// mCacheFile.write(mReadBuffer, mBufferFill, len);
if (mReadPos == mContentLenth)
break;
bufferFill += len;
}
}
private FileOutputStream mCacheFile;
boolean cacheRead(Tile tile, File f) {
if (f.exists() && f.length() > 0) {
FileInputStream in;
try {
in = new FileInputStream(f);
mContentLenth = f.length();
Log.d(TAG, tile + " - using cache: " + mContentLenth);
mInputStream = in;
//decode();
in.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
f.delete();
return false;
}
return false;
}
boolean cacheBegin(Tile tile, File f) {
if (MapDatabase.USE_CACHE) {
try {
Log.d(TAG, tile + " - writing cache");
mCacheFile = new FileOutputStream(f);
if (mReadPos > 0) {
try {
mCacheFile.write(buffer, bufferPos,
bufferFill - bufferPos);
} catch (IOException e) {
e.printStackTrace();
mCacheFile = null;
return false;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
mCacheFile = null;
return false;
}
}
return true;
}
void cacheFinish(Tile tile, File file, boolean success) {
if (MapDatabase.USE_CACHE) {
if (success) {
try {
mCacheFile.flush();
mCacheFile.close();
Log.d(TAG, tile + " - cache written " + file.length());
} catch (IOException e) {
e.printStackTrace();
}
} else {
file.delete();
}
}
mCacheFile = null;
}
}

View File

@ -15,19 +15,9 @@
package org.oscim.database.oscimap;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Arrays;
@ -52,48 +42,42 @@ import android.util.Log;
*
*/
public class MapDatabase implements IMapDatabase {
private static final String TAG = "MapDatabase";
private static final String TAG = MapDatabase.class.getName();
static final boolean USE_CACHE = false;
private static final MapInfo mMapInfo =
new MapInfo(new BoundingBox(-180, -90, 180, 90),
new Byte((byte) 4), new GeoPoint(53.11, 8.85),
null, 0, 0, 0, "de", "comment", "author", null);
private boolean mOpenFile = false;
private static final boolean USE_CACHE = false;
private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/";
private static final String CACHE_FILE = "%d-%d-%d.tile";
//private static String SERVER_ADDR = "city.informatik.uni-bremen.de";
//private static int PORT = 80;
//private static final String URL = "/osci/map-live/";
//private static String TILE_URL = "/osci/oscim/";
private final static float REF_TILE_SIZE = 4096.0f;
// 'open' state
private boolean mOpen = false;
private static File cacheDir;
private int MAX_TILE_TAGS = 100;
private final int MAX_TILE_TAGS = 100;
private Tag[] curTags = new Tag[MAX_TILE_TAGS];
private int mCurTagCnt;
private IMapDatabaseCallback mMapGenerator;
private float mScaleFactor;
private JobTile mTile;
private FileOutputStream mCacheFile;
private String mHost;
private int mPort;
private long mContentLenth;
private InputStream mInputStream;
private final boolean debug = false;
private LwHttp lwHttp;
@Override
public QueryResult executeQuery(JobTile tile, IMapDatabaseCallback mapDatabaseCallback) {
QueryResult result = QueryResult.SUCCESS;
mCacheFile = null;
mTile = tile;
@ -104,24 +88,20 @@ public class MapDatabase implements IMapDatabase {
File f = null;
mBufferFill = 0;
mBufferPos = 0;
mReadPos = 0;
if (USE_CACHE) {
f = new File(cacheDir, String.format(CACHE_FILE,
f = new File(cacheDir, String.format(CACHE_FILE,
Integer.valueOf(tile.zoomLevel),
Integer.valueOf(tile.tileX),
Integer.valueOf(tile.tileY)));
if (cacheRead(tile, f))
if (lwHttp.cacheRead(tile, f))
return QueryResult.SUCCESS;
}
try {
if (lwHttpSendRequest(tile) && lwHttpReadHeader() >= 0) {
cacheBegin(tile, f);
if (lwHttp.sendRequest(tile) && (mContentLenth = lwHttp.readHeader()) >= 0) {
lwHttp.cacheBegin(tile, f);
decode();
} else {
Log.d(TAG, tile + " Network Error");
@ -141,22 +121,14 @@ public class MapDatabase implements IMapDatabase {
result = QueryResult.FAILED;
}
mLastRequest = SystemClock.elapsedRealtime();
lwHttp.mLastRequest = SystemClock.elapsedRealtime();
if (result == QueryResult.SUCCESS) {
cacheFinish(tile, f, true);
lwHttp.cacheFinish(tile, f, true);
} else {
cacheFinish(tile, f, false);
if (mSocket != null) {
// clear connection, TODO cleanup properly
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
mSocket = null;
}
lwHttp.cacheFinish(tile, f, false);
lwHttp.close();
}
return result;
}
@ -173,47 +145,23 @@ public class MapDatabase implements IMapDatabase {
@Override
public boolean isOpen() {
return mOpenFile;
return mOpen;
}
@Override
public OpenResult open(MapOptions options) {
if (mOpenFile)
if (mOpen)
return OpenResult.SUCCESS;
if (options == null || !options.containsKey("url"))
return new OpenResult("options missing");
URL url;
try {
url = new URL(options.get("url"));
} catch (MalformedURLException e) {
lwHttp = new LwHttp();
e.printStackTrace();
if (!lwHttp.setServer(options.get("url"))){
return new OpenResult("invalid url: " + options.get("url"));
}
int port = url.getPort();
if (port < 0)
port = 80;
String host = url.getHost();
String path = url.getPath();
Log.d(TAG, "open oscim database: " + host + " " + port + " " + path);
REQUEST_GET_START = ("GET " + path).getBytes();
REQUEST_GET_END = (".osmtile HTTP/1.1\n" +
"Host: " + host + "\n" +
"Connection: Keep-Alive\n\n").getBytes();
mHost = host;
mPort = port;
//mSockAddr = new InetSocketAddress(host, port);
mRequestBuffer = new byte[1024];
System.arraycopy(REQUEST_GET_START, 0,
mRequestBuffer, 0, REQUEST_GET_START.length);
if (USE_CACHE) {
if (cacheDir == null) {
String externalStorageDirectory = Environment
@ -224,7 +172,7 @@ public class MapDatabase implements IMapDatabase {
}
}
mOpenFile = true;
mOpen = true;
initDecorder();
@ -233,17 +181,9 @@ public class MapDatabase implements IMapDatabase {
@Override
public void close() {
mOpenFile = false;
mOpen = false;
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
mSocket = null;
}
}
lwHttp.close();
if (USE_CACHE) {
cacheDir = null;
@ -270,21 +210,20 @@ public class MapDatabase implements IMapDatabase {
// /////////////// hand sewed tile protocol buffers decoder ///////////////
private final int MAX_WAY_COORDS = 16384;
private final int BUFFER_SIZE = 65536;
private byte[] mReadBuffer = new byte[BUFFER_SIZE];
//private final byte[] mReadBuffer = new byte[BUFFER_SIZE];
// position in buffer
private int mBufferPos;
// private int mBufferPos;
// bytes available in buffer
private int mBufferFill;
// private int mBufferFill;
// overall bytes of content processed
private int mBytesProcessed;
// overall bytes of content read
private int mReadPos;
// private int mReadPos;
private static final int TAG_TILE_NUM_TAGS = 1;
private static final int TAG_TILE_TAG_KEYS = 2;
@ -305,7 +244,7 @@ public class MapDatabase implements IMapDatabase {
private short[] mTmpKeys = new short[100];
private short[] mIndices = new short[10];
private Tag[] mTmpTags = new Tag[20];
private final Tag[] mTmpTags = new Tag[20];
private float[] mTmpCoords;
@ -533,17 +472,17 @@ public class MapDatabase implements IMapDatabase {
private int decodeWayCoordinates(boolean skip, int nodes) throws IOException {
int bytes = decodeVarint32();
readBuffer(bytes);
lwHttp.readBuffer(bytes);
if (skip) {
mBufferPos += bytes;
lwHttp.bufferPos += bytes;
return nodes;
}
int pos = mBufferPos;
int pos = lwHttp.bufferPos;
int end = pos + bytes;
float[] coords = mTmpCoords;
byte[] buf = mReadBuffer;
byte[] buf = lwHttp.buffer;
int cnt = 0;
int result;
@ -606,73 +545,12 @@ public class MapDatabase implements IMapDatabase {
}
}
mBufferPos = pos;
lwHttp.bufferPos = pos;
mBytesProcessed += bytes;
return cnt;
}
private void readBuffer(int size) throws IOException {
// check if buffer already contains the request bytes
if (mBufferPos + size < mBufferFill)
return;
// check if inputstream is read to the end
if (mReadPos == mContentLenth)
return;
int maxSize = mReadBuffer.length;
if (size > maxSize) {
Log.d(TAG, mTile + "increase read buffer to " + size + " bytes");
maxSize = size;
byte[] tmp = new byte[maxSize];
mBufferFill -= mBufferPos;
System.arraycopy(mReadBuffer, mBufferPos, tmp, 0, mBufferFill);
mBufferPos = 0;
mReadBuffer = tmp;
}
if (mBufferFill == mBufferPos) {
mBufferPos = 0;
mBufferFill = 0;
} else if (mBufferPos + size > maxSize) {
// copy bytes left to the beginning of buffer
mBufferFill -= mBufferPos;
System.arraycopy(mReadBuffer, mBufferPos, mReadBuffer, 0, mBufferFill);
mBufferPos = 0;
}
int max = maxSize - mBufferFill;
while ((mBufferFill - mBufferPos) < size && max > 0) {
max = maxSize - mBufferFill;
if (max > mContentLenth - mReadPos)
max = (int) (mContentLenth - mReadPos);
// read until requested size is available in buffer
int len = mInputStream.read(mReadBuffer, mBufferFill, max);
if (len < 0) {
// finished reading, mark end
mReadBuffer[mBufferFill] = 0;
break;
}
mReadPos += len;
if (mCacheFile != null)
mCacheFile.write(mReadBuffer, mBufferFill, len);
if (mReadPos == mContentLenth)
break;
mBufferFill += len;
}
}
private short[] decodeShortArray(int num, short[] array) throws IOException {
int bytes = decodeVarint32();
@ -681,13 +559,13 @@ public class MapDatabase implements IMapDatabase {
index = new short[num];
}
readBuffer(bytes);
lwHttp.readBuffer(bytes);
int cnt = 0;
int pos = mBufferPos;
int pos = lwHttp.bufferPos;
int end = pos + bytes;
byte[] buf = mReadBuffer;
byte[] buf = lwHttp.buffer;
int result;
while (pos < end) {
@ -730,40 +608,40 @@ public class MapDatabase implements IMapDatabase {
index[cnt++] = (short) result;
}
mBufferPos = pos;
lwHttp.bufferPos = pos;
mBytesProcessed += bytes;
return index;
}
private int decodeVarint32() throws IOException {
int pos = mBufferPos;
int pos = lwHttp.bufferPos;
if (pos + 10 > mBufferFill) {
readBuffer(4096);
pos = mBufferPos;
if (pos + 10 > lwHttp.bufferFill) {
lwHttp.readBuffer(4096);
pos = lwHttp.bufferPos;
}
byte[] buf = mReadBuffer;
byte[] buf =lwHttp.buffer;
if (buf[pos] >= 0) {
mBufferPos += 1;
lwHttp.bufferPos += 1;
mBytesProcessed += 1;
return buf[pos];
} else if (buf[pos + 1] >= 0) {
mBufferPos += 2;
lwHttp.bufferPos += 2;
mBytesProcessed += 2;
return (buf[pos] & 0x7f)
| (buf[pos + 1]) << 7;
} else if (buf[pos + 2] >= 0) {
mBufferPos += 3;
lwHttp.bufferPos += 3;
mBytesProcessed += 3;
return (buf[pos] & 0x7f)
| (buf[pos + 1] & 0x7f) << 7
| (buf[pos + 2]) << 14;
} else if (buf[pos + 3] >= 0) {
mBufferPos += 4;
lwHttp.bufferPos += 4;
mBytesProcessed += 4;
return (buf[pos] & 0x7f)
| (buf[pos + 1] & 0x7f) << 7
@ -788,7 +666,7 @@ public class MapDatabase implements IMapDatabase {
if (read == 10)
throw new IOException("X malformed VarInt32 in " + mTile);
mBufferPos += read;
lwHttp.bufferPos += read;
mBytesProcessed += read;
return result;
@ -796,10 +674,10 @@ public class MapDatabase implements IMapDatabase {
private String decodeString() throws IOException {
final int size = decodeVarint32();
readBuffer(size);
final String result = new String(mReadBuffer, mBufferPos, size, "UTF-8");
lwHttp.readBuffer(size);
final String result = new String(lwHttp.buffer, lwHttp.bufferPos, size, "UTF-8");
mBufferPos += size;
lwHttp.bufferPos += size;
mBytesProcessed += size;
return result;
@ -811,264 +689,4 @@ public class MapDatabase implements IMapDatabase {
| (buffer[offset + 3] & 0xff);
}
// ///////////////////////// Lightweight HttpClient ///////////////////////
private int mMaxReq = 0;
private Socket mSocket;
private OutputStream mCommandStream;
private InputStream mResponseStream;
private long mLastRequest = 0;
private SocketAddress mSockAddr;
private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes();
private final static int RESPONSE_EXPECTED_LIVES = 100;
private final static int RESPONSE_EXPECTED_TIMEOUT = 10000;
private byte[] REQUEST_GET_START;
private byte[] REQUEST_GET_END;
private byte[] mRequestBuffer;
int lwHttpReadHeader() throws IOException {
InputStream is = mResponseStream;
byte[] buf = mReadBuffer;
boolean first = true;
int read = 0;
int pos = 0;
int end = 0;
int len = 0;
// header cannot be larger than BUFFER_SIZE for this to work
for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) {
read += len;
while (end < read && (buf[end] != '\n'))
end++;
if (buf[end] == '\n') {
if (first) {
// check only for OK
first = false;
if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15))
return -1;
} else if (end - pos == 1) {
// check empty line (header end)
end += 1;
break;
}
// String line = new String(buf, pos, end - pos - 1);
// Log.d(TAG, ">" + line + "< " + resp_len);
pos += (end - pos) + 1;
end = pos;
}
}
// check 4 bytes available..
while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0)
read += len;
if (read - len < 4)
return -1;
mContentLenth = decodeInt(buf, end);
// buffer fill
mBufferFill = read;
// start of content
mBufferPos = end + 4;
// bytes of content already read into buffer
mReadPos = read - mBufferPos;
mInputStream = mResponseStream;
return 1;
}
private boolean lwHttpSendRequest(Tile tile) throws IOException {
if (mSocket != null && ((mMaxReq-- <= 0)
|| (SystemClock.elapsedRealtime() - mLastRequest
> RESPONSE_EXPECTED_TIMEOUT))) {
try {
mSocket.close();
} catch (IOException e) {
}
// Log.d(TAG, "not alive - recreate connection " + mMaxReq);
mSocket = null;
}
if (mSocket == null) {
lwHttpConnect();
// we know our server
mMaxReq = RESPONSE_EXPECTED_LIVES;
// Log.d(TAG, "create connection");
} else {
// should not be needed
int avail = mResponseStream.available();
if (avail > 0) {
Log.d(TAG, "Consume left-over bytes: " + avail);
mResponseStream.read(mReadBuffer, 0, avail);
}
}
byte[] request = mRequestBuffer;
int pos = REQUEST_GET_START.length;
pos = writeInt(tile.zoomLevel, pos, request);
request[pos++] = '/';
pos = writeInt(tile.tileX, pos, request);
request[pos++] = '/';
pos = writeInt(tile.tileY, pos, request);
int len = REQUEST_GET_END.length;
System.arraycopy(REQUEST_GET_END, 0, request, pos, len);
len += pos;
// this does the same but with a few more allocations:
// byte[] request = String.format(REQUEST,
// Integer.valueOf(tile.zoomLevel),
// Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes();
try {
mCommandStream.write(request, 0, len);
mCommandStream.flush();
return true;
} catch (IOException e) {
Log.d(TAG, mTile + " recreate connection");
}
lwHttpConnect();
mCommandStream.write(request, 0, len);
mCommandStream.flush();
return true;
}
private boolean lwHttpConnect() throws IOException {
if (mSockAddr == null)
mSockAddr = new InetSocketAddress(mHost, mPort);
mSocket = new Socket();
mSocket.connect(mSockAddr, 30000);
mSocket.setTcpNoDelay(true);
mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream();
mResponseStream = mSocket.getInputStream();
return true;
}
// write (positive) integer as char sequence to buffer
private static int writeInt(int val, int pos, byte[] buf) {
if (val == 0) {
buf[pos] = '0';
return pos + 1;
}
int i = 0;
for (int n = val; n > 0; n = n / 10, i++)
buf[pos + i] = (byte) ('0' + n % 10);
// reverse bytes
for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) {
byte tmp = buf[j];
buf[j] = buf[end];
buf[end] = tmp;
}
return pos + i;
}
private static boolean compareBytes(byte[] buffer, int position, int available,
byte[] string, int length) {
if (available - position < length)
return false;
for (int i = 0; i < length; i++)
if (buffer[position + i] != string[i])
return false;
return true;
}
// ///////////////////////// Tile cache /////////////////////////////////
private boolean cacheRead(Tile tile, File f) {
if (f.exists() && f.length() > 0) {
FileInputStream in;
try {
in = new FileInputStream(f);
mContentLenth = f.length();
Log.d(TAG, tile + " - using cache: " + mContentLenth);
mInputStream = in;
decode();
in.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
f.delete();
return false;
}
return false;
}
private boolean cacheBegin(Tile tile, File f) {
if (USE_CACHE) {
try {
Log.d(TAG, tile + " - writing cache");
mCacheFile = new FileOutputStream(f);
if (mReadPos > 0) {
try {
mCacheFile.write(mReadBuffer, mBufferPos,
mBufferFill - mBufferPos);
} catch (IOException e) {
e.printStackTrace();
mCacheFile = null;
return false;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
mCacheFile = null;
return false;
}
}
return true;
}
private void cacheFinish(Tile tile, File file, boolean success) {
if (USE_CACHE) {
if (success) {
try {
mCacheFile.flush();
mCacheFile.close();
Log.d(TAG, tile + " - cache written " + file.length());
} catch (IOException e) {
e.printStackTrace();
}
} else {
file.delete();
}
}
mCacheFile = null;
}
}