/* * Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package och.comp.web; import static java.util.Collections.*; import static och.api.model.PropKey.*; import static och.comp.web.ErrorCode.*; import static och.util.NetUtil.*; import static och.util.Util.*; import static och.util.json.GsonUtil.*; import static och.util.servlet.WebUtil.*; import java.io.IOException; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import javax.servlet.http.HttpServletResponse; import och.api.exception.web.RemoteServerException; import och.service.props.Props; import org.apache.commons.logging.Log; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; public class JsonOps { private static final Log log = getLog(JsonOps.class); private static CopyOnWriteArrayList<PostEncryptedJsonListener> postListeners = new CopyOnWriteArrayList<>(); public static void addPostEncryptedJsonListener(PostEncryptedJsonListener l){ postListeners.add(l); } public static void setJsonResponse(HttpServletResponse resp){ resp.setCharacterEncoding("UTF-8"); resp.setContentType("application/json; charset=UTF-8"); } public static String jsonOk(){ return "{\"status\":\"ok\"}"; } public static String jsonOk(Object ob){ String data = defaultGson.toJson(ob); return "{\"status\":\"ok\", \"data\":"+data+"}"; } public static String jsonUnexpectedError(String msg){ return jsonError(UNEXPECTED, msg, null); } public static String jsonValidationError(Exception e){ return jsonError(VALIDATION, e.getMessage(), e.getClass()); } public static String jsonValidationError(String msg){ return jsonError(VALIDATION, msg, null); } public static String jsonValidationError(String msg, Class<?> errorType){ return jsonError(VALIDATION, msg, errorType); } public static String jsonAccessDeniedError(){ return jsonError(ACCESS_DENIED, "need sign in", null); } public static String jsonError(ErrorCode codeType, String msg, Class<?> type){ String typeBlock = type == null? "" : ", \"type\":\""+type.getName()+"\""; if(msg == null) msg = ""; return "{\"status\":\"error\", \"code\":"+codeType.code+", \"msg\":\""+msg+"\""+typeBlock+"}"; } public static Map<String, Void> postEncryptedJsonToAny(Props props, Collection<String> urls, Object req) { return postEncryptedJsonToAny(props, urls, true, req, null); } public static <T> Map<String, T> postEncryptedJsonToAny(Props props, Collection<String> urls, Object req, Class<T> respType) { return postEncryptedJsonToAny(props, urls, true, req, respType); } public static <T> Map<String, T> postEncryptedJsonToAny(Props props, Collection<String> urlsCollection, boolean removeCurServerUrl, Object req, Class<T> respType) { if(isEmpty(urlsCollection)) return emptyMap(); ArrayList<String> urls = toList(urlsCollection); if(removeCurServerUrl){ String httpUrl = props.getVal(httpServerUrl); String httpsUrl = props.getVal(httpsServerUrl); for (int i = urls.size()-1; i > -1; i--) { String url = urls.get(i); if( (hasText(httpUrl) && url.startsWith(httpUrl)) || ( hasText(httpsUrl) && url.startsWith(httpsUrl))){ urls.remove(i); } } } HashMap<String, T> out = new HashMap<>(); for (String url : urls) { try { T result = (T) postEncryptedJson(props, url, req, respType); out.put(url, result); }catch (Throwable t) { log.error("can't postEncryptedJson: url="+url+", ex="+t); } } return out; } public static void postEncryptedJson(Props props, String url, Object req) throws Exception{ postEncryptedJson(props, url, req, Void.class); } public static <T> T postEncryptedJson(Props props, String url, Object req, Class<T> respType) throws Exception{ String key = props.getVal(remote_encyptedKey); int connTimeout = props.getIntVal(remote_connTimeout); int readTimeout = props.getIntVal(remote_readTimeout); return postEncryptedJson(url, key, req, respType, connTimeout, readTimeout); } public static <T> T postEncryptedJson(String url, String key, Object req, Class<T> respType, int connTimeout, int readTimeout) throws Exception{ String data = defaultGson.toJson(req); String encoded = isEmpty(key) ? data : encodeToken(data, key); String postBody = createPostBody(map("data", encoded)); HttpURLConnection conn = openPostConn(url, connTimeout, readTimeout); setTrustAnyHttps(conn); String encodedResp = invokePost(conn, postBody); boolean hasError = false; try { if(isEmpty(encodedResp)) return null; String resp = isEmpty(key) ? encodedResp : decodeToken(encodedResp, key); JsonObject parsed = parseJsonResp(resp); if(isEmpty(parsed)) return null; if( ! isOkStatus(parsed)){ throw new RemoteServerException(getErrorMsg(parsed)); } T out = respType == null? null : getData(parsed, respType); return out; }catch (Exception e) { hasError = true; throw e; }finally { //listeners if(!hasError){ try { for(PostEncryptedJsonListener l : postListeners) l.onPosted(url, req); }catch (Exception e) { log.error("can't call listeners", e); } } } } public static JsonObject parseJsonResp(String resp) throws IOException{ if(isEmpty(resp)) return null; try { return new JsonParser().parse(resp).getAsJsonObject(); }catch (JsonSyntaxException e) { throw new RemoteServerException("can't parse server's resp: "+resp); } } public static boolean isOkStatus(JsonObject json){ JsonElement statusElem = json.get("status"); return statusElem != null && statusElem.getAsString().equals("ok"); } public static String getErrorMsg(JsonObject json){ ErrorResp errorResp = null; try { errorResp = defaultGson.fromJson(json, ErrorResp.class); }catch (Exception e) { //unknown resp answer } return errorResp == null? json.toString() : errorResp.getExceptionMsg(); } public static <T> T getData(JsonObject json, Class<T> respType){ JsonElement dataElem = json.get("data"); if(dataElem == null) return null; T out = defaultGson.fromJson(dataElem, respType); return out; } }