001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.activemq.transport.stomp; 018 019 import java.io.IOException; 020 import java.io.Serializable; 021 import java.io.StringReader; 022 import java.io.StringWriter; 023 import java.util.HashMap; 024 import java.util.Map; 025 026 import javax.jms.JMSException; 027 028 import org.apache.activemq.command.ActiveMQMapMessage; 029 import org.apache.activemq.command.ActiveMQMessage; 030 import org.apache.activemq.command.ActiveMQObjectMessage; 031 import org.springframework.beans.BeansException; 032 import org.springframework.context.ApplicationContext; 033 import org.springframework.context.ApplicationContextAware; 034 035 import com.thoughtworks.xstream.XStream; 036 import com.thoughtworks.xstream.io.HierarchicalStreamReader; 037 import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 038 import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; 039 import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; 040 import com.thoughtworks.xstream.io.xml.XppReader; 041 042 /** 043 * Frame translator implementation that uses XStream to convert messages to and 044 * from XML and JSON 045 * 046 * @author <a href="mailto:dejan@nighttale.net">Dejan Bosanac</a> 047 */ 048 public class JmsFrameTranslator extends LegacyFrameTranslator implements 049 ApplicationContextAware { 050 051 XStream xStream = null; 052 ApplicationContext applicationContext; 053 054 public ActiveMQMessage convertFrame(ProtocolConverter converter, 055 StompFrame command) throws JMSException, ProtocolException { 056 Map headers = command.getHeaders(); 057 ActiveMQMessage msg; 058 String transformation = (String) headers.get(Stomp.Headers.TRANSFORMATION); 059 if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH) || transformation.equals(Stomp.Transformations.JMS_BYTE.toString())) { 060 msg = super.convertFrame(converter, command); 061 } else { 062 HierarchicalStreamReader in; 063 064 try { 065 String text = new String(command.getContent(), "UTF-8"); 066 switch (Stomp.Transformations.getValue(transformation)) { 067 case JMS_OBJECT_XML: 068 in = new XppReader(new StringReader(text)); 069 msg = createObjectMessage(in); 070 break; 071 case JMS_OBJECT_JSON: 072 in = new JettisonMappedXmlDriver().createReader(new StringReader(text)); 073 msg = createObjectMessage(in); 074 break; 075 case JMS_MAP_XML: 076 in = new XppReader(new StringReader(text)); 077 msg = createMapMessage(in); 078 break; 079 case JMS_MAP_JSON: 080 in = new JettisonMappedXmlDriver().createReader(new StringReader(text)); 081 msg = createMapMessage(in); 082 break; 083 default: 084 throw new Exception("Unkown transformation: " + transformation); 085 } 086 } catch (Throwable e) { 087 command.getHeaders().put(Stomp.Headers.TRANSFORMATION_ERROR, e.getMessage()); 088 msg = super.convertFrame(converter, command); 089 } 090 } 091 FrameTranslator.Helper.copyStandardHeadersFromFrameToMessage(converter, command, msg, this); 092 return msg; 093 } 094 095 public StompFrame convertMessage(ProtocolConverter converter, 096 ActiveMQMessage message) throws IOException, JMSException { 097 if (message.getDataStructureType() == ActiveMQObjectMessage.DATA_STRUCTURE_TYPE) { 098 StompFrame command = new StompFrame(); 099 command.setAction(Stomp.Responses.MESSAGE); 100 Map<String, String> headers = new HashMap<String, String>(25); 101 command.setHeaders(headers); 102 103 FrameTranslator.Helper.copyStandardHeadersFromMessageToFrame( 104 converter, message, command, this); 105 ActiveMQObjectMessage msg = (ActiveMQObjectMessage) message.copy(); 106 command.setContent(marshall(msg.getObject(), 107 headers.get(Stomp.Headers.TRANSFORMATION)) 108 .getBytes("UTF-8")); 109 return command; 110 111 } else if (message.getDataStructureType() == ActiveMQMapMessage.DATA_STRUCTURE_TYPE) { 112 StompFrame command = new StompFrame(); 113 command.setAction(Stomp.Responses.MESSAGE); 114 Map<String, String> headers = new HashMap<String, String>(25); 115 command.setHeaders(headers); 116 117 FrameTranslator.Helper.copyStandardHeadersFromMessageToFrame( 118 converter, message, command, this); 119 ActiveMQMapMessage msg = (ActiveMQMapMessage) message.copy(); 120 command.setContent(marshall((Serializable)msg.getContentMap(), 121 headers.get(Stomp.Headers.TRANSFORMATION)) 122 .getBytes("UTF-8")); 123 return command; 124 } else { 125 return super.convertMessage(converter, message); 126 } 127 } 128 129 /** 130 * Marshalls the Object to a string using XML or JSON encoding 131 */ 132 protected String marshall(Serializable object, String transformation) 133 throws JMSException { 134 StringWriter buffer = new StringWriter(); 135 HierarchicalStreamWriter out; 136 if (transformation.toLowerCase().endsWith("json")) { 137 out = new JettisonMappedXmlDriver().createWriter(buffer); 138 } else { 139 out = new PrettyPrintWriter(buffer); 140 } 141 getXStream().marshal(object, out); 142 return buffer.toString(); 143 } 144 145 protected ActiveMQObjectMessage createObjectMessage(HierarchicalStreamReader in) throws JMSException { 146 ActiveMQObjectMessage objMsg = new ActiveMQObjectMessage(); 147 Object obj = getXStream().unmarshal(in); 148 objMsg.setObject((Serializable) obj); 149 return objMsg; 150 } 151 152 protected ActiveMQMapMessage createMapMessage(HierarchicalStreamReader in) throws JMSException { 153 ActiveMQMapMessage mapMsg = new ActiveMQMapMessage(); 154 Map<String, Object> map = (Map<String, Object>)getXStream().unmarshal(in); 155 for (String key : map.keySet()) { 156 mapMsg.setObject(key, map.get(key)); 157 } 158 return mapMsg; 159 } 160 161 162 163 // Properties 164 // ------------------------------------------------------------------------- 165 public XStream getXStream() { 166 if (xStream == null) { 167 xStream = createXStream(); 168 } 169 return xStream; 170 } 171 172 public void setXStream(XStream xStream) { 173 this.xStream = xStream; 174 } 175 176 // Implementation methods 177 // ------------------------------------------------------------------------- 178 protected XStream createXStream() { 179 XStream xstream = null; 180 if (applicationContext != null) { 181 String[] names = applicationContext 182 .getBeanNamesForType(XStream.class); 183 for (int i = 0; i < names.length; i++) { 184 String name = names[i]; 185 xstream = (XStream) applicationContext.getBean(name); 186 if (xstream != null) { 187 break; 188 } 189 } 190 } 191 192 if (xstream == null) { 193 xstream = new XStream(); 194 } 195 return xstream; 196 197 } 198 199 public void setApplicationContext(ApplicationContext applicationContext) 200 throws BeansException { 201 this.applicationContext = applicationContext; 202 } 203 204 }