/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.apmrouter.dataservice.json; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.helios.apmrouter.OpCode; import org.helios.apmrouter.dataservice.json.marshalling.netty.ChannelBufferizable; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.DownstreamMessageEvent; import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.annotations.SerializedName; /** * <p>Title: JsonResponse</p> * <p>Description: The standard object container for sending a response to a JSON data service caller</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.dataservice.json.JsonResponse</code></p> */ public class JsonResponse implements ChannelBufferizable { /** The client provided request ID that this response is being sent for */ @SerializedName("rerid") protected final long reRequestId; /** The response type */ @SerializedName("t") protected final String type; /** The response instance id */ @SerializedName("id") protected final int id = System.identityHashCode(this); /** The content payload */ @SerializedName("msg") protected Object content = null; /** The response op code */ @SerializedName("op") protected OpCode opCode = null; /** The gson serializer */ protected static final Gson gson = new GsonBuilder().create(); /** Response flag for an error message */ public static final String RESP_TYPE_ERR = "err"; /** Response flag for a request response */ public static final String RESP_TYPE_RESP = "resp"; /** Response flag for a subscription event delivery */ public static final String RESP_TYPE_SUB = "sub"; /** Response flag for a subscription start confirm */ public static final String RESP_TYPE_SUB_STARTED = "subst"; /** Response flag for a subscription stop notification */ public static final String RESP_TYPE_SUB_STOPPED = "xsub"; /** Response flag for a growl */ public static final String RESP_TYPE_GROWL = "growl"; /** * {@inheritDoc} * @see java.lang.Object#clone() */ @Override public JsonResponse clone() { return new JsonResponse(reRequestId, type); } /** * Clones this json response with a new type * @param type the new type * @return an updated type clone of this response */ public JsonResponse clone(String type) { return new JsonResponse(reRequestId, type); } /** * Creates a new JsonResponse * @param reRequestId The client provided request ID that this response is being sent for * @param type The type flag. Currently "err" for an error message, "resp" for a response, "sub" for subcription event */ public JsonResponse(long reRequestId, String type) { super(); this.reRequestId = reRequestId; this.type = type; } public static void main(String[] args) { log("GSON Test"); JsonResponse resp = new JsonResponse(3, RESP_TYPE_RESP); Map<Integer, String> hosts = new HashMap<Integer, String>(); int cnt = 0; List<String> sp = new ArrayList<String>(System.getProperties().stringPropertyNames()); while(cnt < 10) { hosts.put(cnt, sp.get(cnt) + ":" + System.getProperty(sp.get(cnt))); cnt++; } resp.setContent(hosts); Gson gson = new GsonBuilder().setPrettyPrinting().create(); log(gson.toJson(resp)); } private static class MapSerializer implements JsonSerializer<Map<?,?>> { @Override public JsonElement serialize(Map<?,?> src, Type typeOfSrc, JsonSerializationContext context) { if(!src.isEmpty()) { Map.Entry<?, ?> entry = src.entrySet().iterator().next(); boolean keysAreNumbers = isNumber(entry.getKey()); boolean valuesAreNumbers = isNumber(entry.getValue()); if(keysAreNumbers || valuesAreNumbers) { JsonObject map = new JsonObject(); } } return context.serialize(src, typeOfSrc); } private static boolean isNumber(Object obj) { if(obj==null) return false; if(obj instanceof Number) return true; try { Double.parseDouble(obj.toString()); return true; } catch (Exception ex) { return false; } } } public static void log(Object msg) { System.out.println(msg); } /** * Returns the content payload * @return the content */ public Object getContent() { return content; } /** * Sets the payload content * @param content the content to set * @return this json response */ public JsonResponse setContent(Object content) { this.content = content; return this; } /** * Returns the in reference to request id * @return the in reference to request id */ public long getReRequestId() { return reRequestId; } /** * Returns the type flag * @return the type */ public String getType() { return type; } /** * Returns the response op code * @return the response op code */ public OpCode getOpCode() { return opCode; } /** * Sets the response op code * @param opCode the response op code * @return this response */ public JsonResponse setOpCode(OpCode opCode) { this.opCode = opCode; return this; } /** * {@inheritDoc} * @see org.helios.apmrouter.dataservice.json.marshalling.netty.ChannelBufferizable#toChannelBuffer() */ @Override public ChannelBuffer toChannelBuffer() { return ChannelBuffers.wrappedBuffer(gson.toJson(this).getBytes()); } /** An empty ChannelFuture const. */ private static final ChannelFuture[] EMPTY_CHANNEL_FUTURE_ARR = {}; /** * Sends this response to all the passed channels as a {@link TextWebSocketFrame} * @param listener A channel future listener to attach to each channel future. Ignored if null. * @param channels The channels to send this response to * @return An array of the futures for the write of this response to each channel written to */ public ChannelFuture[] send(ChannelFutureListener listener, Channel...channels) { if(channels!=null && channels.length>0) { Set<ChannelFuture> futures = new HashSet<ChannelFuture>(channels.length); TextWebSocketFrame frame = new TextWebSocketFrame(this.toChannelBuffer()); for(Channel channel: channels) { if(channel!=null && channel.isWritable()) { ChannelFuture cf = Channels.future(channel); if(listener!=null) cf.addListener(listener); channel.getPipeline().sendDownstream(new DownstreamMessageEvent(channel, cf, frame, channel.getRemoteAddress())); futures.add(cf); } } return futures.toArray(new ChannelFuture[futures.size()]); } return EMPTY_CHANNEL_FUTURE_ARR; } /** * Sends this response to all the passed channels as a {@link TextWebSocketFrame} * @param channels The channels to send this response to * @return An array of the futures for the write of this response to each channel written to */ public ChannelFuture[] send(Channel...channels) { return send(null, channels); } }