Java tutorial
/* Copyright (C) 2007 Helge Hess This file is part of JOPE. JOPE 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 2, or (at your option) any later version. JOPE 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 JOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.getobjects.appserver.publisher; import java.sql.SQLException; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.getobjects.appserver.core.WOContext; import org.getobjects.appserver.core.WORequest; import org.getobjects.appserver.core.WOResponse; import org.getobjects.foundation.NSKeyValueCoding; import org.getobjects.foundation.NSObject; import org.getobjects.foundation.UString; import org.getobjects.foundation.kvc.MissingPropertyException; /** * JoSimpleJSONRenderer * <p> * This is a simple renderer which can render plist objects into JSON. It checks * whether the client accepts JSON requests by:<br> * a) checking the 'accept' HTTP header (must allow application/json)<br> * b) checking for a 'format' form parameter with value 'json'<br> * <p> * If you want to enforce rendering using JSON, wrap your object in a * JoJSONResult object. */ public class JoSimpleJSONRenderer extends NSObject implements IJoObjectRenderer { protected static final Log log = LogFactory.getLog("JoSimpleJSONRenderer"); /* control rendering */ public boolean isJSONRequest(WORequest _rq) { if (_rq == null) return false; if (_rq.acceptsContentType("application/json", false /* no wildcard */)) return true; String fmt = _rq.stringFormValueForKey("format"); if (fmt != null && "json".equals(fmt)) return true; return false; } public boolean canRenderObjectInContext(Object _object, WOContext _ctx) { /* enforce JSON */ if (_object instanceof JoJSONResult) return true; /* check whether the client accepts JSON */ WORequest rq = _ctx.request(); if (rq == null) { log.warn("missing request in context: " + _ctx); return false; } if (!this.isJSONRequest(rq)) { if (log.isInfoEnabled()) log.info("request accepts no JSON: " + rq); return false; } /* Note: do *NOT* move up, first we need to check whether the client * actually wants JSON! */ if (_object == null) return true; if (_object instanceof String) return true; if (_object instanceof Boolean) return true; if (_object instanceof Number) return true; if (_object instanceof List) return true; if (_object instanceof Map) return true; if (_object instanceof Throwable) return true; log.warn("object type unsupported by this renderer: " + _object.getClass()); return false; } /* rendering */ public Exception renderObjectInContext(Object _object, WOContext _ctx) { StringBuilder json = new StringBuilder(4096); Exception error = this.appendObjectToString(_object, json); if (error != null) return error; WOResponse r = _ctx.response(); r.setContentEncoding("utf8"); r.setHeaderForKey("application/json; utf-8", "content-type"); r.enableStreaming(); r.appendContentString(json.toString()); return null; } /* JSON rendering */ public static final String[] JSEscapeList = { "\\", "\\\\", "'", "\\'", "\"", "\\\"", "\n", "\\n", "\b", "\\b", "\f", "\\f", "\r", "\\r", "\t", "\\t", }; public Exception appendObjectToString(Object _object, StringBuilder _sb) { if (_object == null) { _sb.append("null"); return null; } if (_object instanceof JoJSONResult) return this.appendObjectToString(((JoJSONResult) _object).result(), _sb); if (_object instanceof String) { String s = (String) _object; _sb.append("\""); _sb.append(UString.replaceInSequence(s, JSEscapeList)); _sb.append("\""); return null; } if (_object instanceof Boolean) { _sb.append(((Boolean) _object).booleanValue() ? "true" : "false"); return null; } if (_object instanceof Number) { _sb.append(_object); return null; } if (_object instanceof List) return this.appendListToString((List) _object, _sb); if (_object instanceof Map) return this.appendMapToString((Map) _object, _sb); if (_object instanceof Throwable) return this.appendExceptionToString((Throwable) _object, _sb); return this.appendCustomObjectToString(_object, _sb); } /* specific appenders */ public Exception appendExceptionToString(Throwable _ex, StringBuilder _sb) { if (_ex == null) { _sb.append("null"); return null; } Exception error; _sb.append('{'); /* add message code */ Object v = null; if (v == null) { try { v = NSKeyValueCoding.Utility.valueForKey(_ex, "code"); } catch (MissingPropertyException e) { } /* we do not care and continue */ } if (v == null) { try { v = NSKeyValueCoding.Utility.valueForKey(_ex, "errorCode"); } catch (MissingPropertyException e) { } /* we do not care and continue */ } if (_ex instanceof SQLException) v = "sql" + ((SQLException) _ex).getSQLState(); if (v == null) v = _ex.getClass().getName(); if (v == null) v = "unknown"; error = this.appendKeyValuePair("error", v, true, _sb); if (error != null) return error; /* added messages */ String pm = _ex.getMessage(); String sm = _ex.getLocalizedMessage(); if (pm == sm || (pm != null && sm != null && pm.equals(sm))) sm = null; error = this.appendKeyValuePair("message", pm, false, _sb); if (error != null) return error; error = this.appendKeyValuePair("localizedMessage", sm, false, _sb); if (error != null) return error; /* additional standard keys */ v = null; try { v = NSKeyValueCoding.Utility.valueForKey(_ex, "httpStatus"); } catch (MissingPropertyException e) { } /* we do not care and continue */ error = this.appendKeyValuePair("httpStatus", v, false, _sb); if (error != null) return error; if (_ex instanceof SQLException) { error = this.appendKeyValuePair("sqlstate", ((SQLException) _ex).getSQLState(), false, _sb); if (error != null) return error; } _sb.append('}'); return null; } public Exception appendKeyValuePair(Object _key, Object _value, boolean _isFirst, StringBuilder _sb) { if (_value == null) return null; if (!_isFirst) _sb.append(","); Exception error = this.appendObjectToString(_key, _sb); if (error != null) return error; _sb.append(':'); return this.appendObjectToString(_value, _sb); } public Exception appendListToString(List _list, StringBuilder _sb) { if (_list == null) { _sb.append("null"); return null; } _sb.append('('); boolean isFirst = true; for (Object value : _list) { if (isFirst) isFirst = false; else _sb.append(','); Exception error = this.appendObjectToString(value, _sb); if (error != null) return error; _sb.append(':'); } _sb.append(')'); return null; } public Exception appendMapToString(Map _map, StringBuilder _sb) { if (_map == null) { _sb.append("null"); return null; } _sb.append('{'); boolean isFirst = true; for (Object key : _map.keySet()) { if (isFirst) isFirst = false; else _sb.append(','); Exception error = this.appendObjectToString(key, _sb); if (error != null) return error; _sb.append(':'); error = this.appendObjectToString(_map.get(key), _sb); if (error != null) return error; } _sb.append('}'); return null; } public Exception appendCustomObjectToString(Object _obj, StringBuilder _sb) { if (_obj instanceof Exception) return (Exception) _obj; log.warn("cannot render object as JSON: " + _obj); return new JoInternalErrorException("cannot render given object as JSON"); } }