/*
* Copyright (C) 2013-2014 Sony Computer Science Laboratories, Inc. All Rights Reserved.
* Copyright (C) 2014 Sony Corporation. All Rights Reserved.
*/
package com.sonycsl.Kadecot.server;
import android.content.Context;
import com.sonycsl.Kadecot.call.RequestProcessor;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// JSONPで返せるように
public class KadecotJSONPServer {
private static KadecotJSONPServer sInstance;
static final String TYPE_JSON = "application/json";
private ServerSocket mServerSocket;
private final List<HttpGet> mHttpGetList = new ArrayList<HttpGet>();
private final String[] ACCESSIBLE_METHODS = {
"refreshList", "list", "get", "set"
};
private final HashSet<String> callableHomeNetWorkMethod = new HashSet<String>();
// DeviceInfoDB mDeviceInfoDB;
private final Context mContext;
private boolean mRunning = false;
private KadecotJSONPServer(Context context) {
super();
callableHomeNetWorkMethod.add("list");
callableHomeNetWorkMethod.add("refreshList");
callableHomeNetWorkMethod.add("access");
callableHomeNetWorkMethod.add("getPowerHistory");
// mDeviceInfoDB = new DeviceInfoDB(context);
mContext = context;
mHttpGetList.add(new HttpGet("/call.json") {
@Override
public void run(Request req, Response res) throws IOException {
String callback = null;
String method = urldecode(req.query.get("method"));
JSONArray params = null;
try {
if (req.query.containsKey("params")) {
params = new JSONArray(urldecode(req.query.get("params")));
} else {
params = new JSONArray();
}
} catch (JSONException e) {
e.printStackTrace();
}
if (req.query.containsKey("callback")) {
callback = urldecode(req.query.get("callback"));
} else if (req.query.containsKey("jsoncallback")) {
callback = urldecode(req.query.get("jsoncallback"));
}
// String id = UUID.randomUUID().toString().replaceAll("-", "");
com.sonycsl.Kadecot.call.Response response = null;
if (method != null && params != null) {
for (String m : ACCESSIBLE_METHODS) {
if (m.equals(method)) {
// TODO: fix Dummy json
// response = (new RequestProcessor(mContext,
// 1)).process(method, params);
response = (new RequestProcessor(mContext, 1)).process(method,
new JSONObject());
}
}
}
String result = "error";
try {
if (response != null) {
result = response.toJSON().toString();
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (callback != null) {
result = callback + "(" + result + ")";
}
res.success(TYPE_JSON, result);
}
});
}
public static KadecotJSONPServer getInstance(Context context) {
if (sInstance == null) {
sInstance = new KadecotJSONPServer(context.getApplicationContext());
}
return sInstance;
}
public void start(int port) throws IOException {
if (mRunning == true)
return;
mServerSocket = new ServerSocket();
mServerSocket.setReuseAddress(true);
mServerSocket.bind(new InetSocketAddress(port));
// Log.v(TAG, "start");
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
try {
service();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
stop();
}
}
});
// ServerCallHttp.getInstance(mContext).start();
mRunning = true;
}
public void stop() {
// Log.v(TAG, "stop");
// ServerCallHttp.getInstance(mContext).stop();
try {
if (mServerSocket != null) {
mServerSocket.close();
mServerSocket = null;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
mRunning = false;
}
}
public boolean isRunning() {
return mRunning;
}
// main
private void service() throws IOException {
// System.out.println("service");
for (Socket sock = accept(); sock != null; sock = accept()) {
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new HttpServerService(sock));
}
}
private Socket accept() throws IOException {
// Log.v(TAG, "accept");
try {
return mServerSocket.accept();
} catch (SocketException e) {
// System.out.println("done");
}
return null;
}
// assumute encoding is utf-8
private String urldecode(String s) {
if (s == null)
return null;
try {
return URLDecoder.decode(s, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return s;
}
}
// /////////////////////////////////
public String access(Map<String, String> arguments) {
String nickname_s = arguments.get("nickname");
String epc_s = arguments.get("epc");
if (nickname_s == null || epc_s == null)
return "{\"error\":\"Incomplete parameters for callmethod : nickname, epc are mandatory parameters.\"}";
// DeviceInfoDB db = new DeviceInfoDB(mContext);
// DeviceDatabaseManager db =
// DeviceDatabaseManager.getInstance(mContext);
// if(!db.containsNickname(nickname_s)) {
// db.close();
// return "{\"error\":\""+nickname_s+" does not exist.\"}" ;
// }
// byte epc = Integer.decode(epc_s).byteValue() ; //.byteValue() ;
String args_sp = arguments.get("arg");
// distinguish set/get by the existence of the parameter 'arg'
// String node_id = db.getEchoDeviceAddress(nickname_s);
// int obj = db.getEchoDeviceObjectCode(nickname_s);
// db.close();
// get method
if (args_sp == null) {
// return mDevManager.callMethod( node_id,obj,epc,null ,null) ;
// return DeviceManager.getInstance(mContext).callMethod(null, null,
// nickname_s,
// epc_s,null) ;
return "";
}
// set method
// split args_s into byte array
// String[] args_s = args_sp.split(",");
// byte[] params = new byte[args_s.length] ;
// for( int ai=0;ai<args_s.length;++ai ) params[ai] =
// Integer.decode(args_s[ai]).byteValue()
// ;
// return mDevManager.callMethod( node_id,obj,epc,params ,null) ;
// return DeviceManager.getInstance(mContext).callMethod(null, null,
// nickname_s,
// epc_s,args_s) ;
return "";
}
// /////////////////////////////////
private class Request {
public String path = null;
// public Map<String, String> params;
public Map<String, String> query;
public List<String> requests;
Pattern REQUEST_LINE_PATTERN = Pattern.compile("^(\\w+)\\s+(.+?)\\s+HTTP/([\\d.]+)$");
public Request(InputStream in) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
query = new HashMap<String, String>();
requests = new ArrayList<String>();
String line;
try {
line = reader.readLine();
while (line != null && line.length() != 0) {
// System.out.println(line);
requests.add(line);
line = reader.readLine();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (requests.size() < 1)
return;
line = requests.get(0);
Matcher m = REQUEST_LINE_PATTERN.matcher(line);
if (m.matches()) {
String p = m.group(2);
String[] ss = p.split("\\?");
path = ss[0];
if (ss.length == 1) {
return;
} else {
String q = p.substring(path.length() + 1);
ss = q.split("&");
for (String s : ss) {
String[] r = s.split("=");
if (r.length != 2)
continue;
query.put(r[0], r[1]);
}
}
} else {
return;
}
}
}
private class Response {
OutputStream out;
public Response(OutputStream out) {
this.out = out;
}
public void success(String type, String content) {
// System.out.println("success");
PrintWriter writer = new PrintWriter(out);
writer.print("HTTP/1.1 200 OK\r\n");
writer.print("Connection: close\r\n");
writer.print("ETag: \"" + UUID.randomUUID().toString().replaceAll("-", "") + "\"\r\n");
writer.print("Content-Length: ");
writer.print(content.getBytes().length);
writer.print("\r\n");
writer.print("Content-Type: ");
writer.print(type);
writer.print("\r\n\r\n");
writer.print(content);
writer.flush();
}
public void failure(String type, String content) {
// System.out.println("failure");
PrintWriter writer = new PrintWriter(out);
writer.print("HTTP/1.0 404 Not Found\r\n");
writer.print("Connection: close\r\n");
writer.print("ETag: \"" + UUID.randomUUID().toString().replaceAll("-", "") + "\"\r\n");
writer.print("Content-Length: ");
writer.print(content.getBytes().length);
writer.print("\r\n");
writer.print("Content-Type: ");
writer.print(type);
writer.print("\r\n\r\n");
writer.print(content);
writer.flush();
}
public void sendNotFound() {
failure("text", "Not Found");
}
}
private abstract class HttpGet {
String path;
public HttpGet(String path) {
this.path = path;
}
public boolean match(String path) {
return this.path.equalsIgnoreCase(path);
}
public abstract void run(Request req, Response res) throws IOException;
}
private class HttpServerService implements Runnable {
Socket socket;
HttpServerService(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
OutputStream out = null;
InputStream in = null;
try {
out = socket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
try {
socket.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return;
}
try {
in = socket.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return;
}
try {
// System.out.println("receive request");
Request req = new Request(in);
// System.out.println("received request");
Response res = new Response(out);
if (req.path == null) {
res.sendNotFound();
return;
}
// Log.v(TAG, "path:"+req.path);
for (HttpGet httpGet : mHttpGetList) {
if (httpGet.match(req.path)) {
httpGet.run(req, res);
return;
}
}
res.sendNotFound();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}