1/*2 * Licensed to the Apache Software Foundation (ASF) under one3 * or more contributor license agreements. See the NOTICE file4 * distributed with this work for additional information5 * regarding copyright ownership. The ASF licenses this file6 * to you under the Apache License, Version 2.0 (the7 * "License"); you may not use this file except in compliance8 * with the License. You may obtain a copy of the License at9 *10 * http://www.apache.org/licenses/LICENSE-2.011 *12 * Unless required by applicable law or agreed to in writing, software13 * distributed under the License is distributed on an "AS IS" BASIS,14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15 * See the License for the specific language governing permissions and16 * limitations under the License.17 */18package org.apache.hadoop.chukwa.datacollection.adaptor.jms;
1920import org.apache.commons.logging.Log;
21import org.apache.commons.logging.LogFactory;
2223import javax.jms.Message;
24import javax.jms.JMSException;
25import java.util.ArrayList;
2627/**28 * JMSMessageTransformer that uses the properties of a JMS Message to build a29 * Chukwa record payload. The value for each property configured will be used30 * to create the record, with the delimiter value between each. The default31 * delimiter is a tab (i.e., '\t').32 * <P>33 * To configure this transformer, set the -p field of the adaptor to the34 * following (surrounded with double quotes):35 * <code>36 * <propertyNames> [-d <delimiter>] [-r <requiredPropertyNames>]37 * </code>38 * <ul>39 * <li><code>propertyNames</code> - Comma-separated list of JMS properties.</li>40 * <li><code>delimiter</code> - Delimiter to use, in single quotes.</li>41 * <li><code>requiredPropertyNames</code> - Comma-separated list of required42 * JMS properties. Default behavior is that all properties are required.</li>43 * </ul>44 *45 */46publicclassJMSMessagePropertyTransformerimplementsJMSMessageTransformer {
47protected Log log = LogFactory.getLog(getClass());
4849privatestaticfinal String DEFAULT_DELIMITER = "\t";
5051 ArrayList<String> propertyNames = null;
52 ArrayList<String> requiredPropertyNames = null;
53 String delimiter = DEFAULT_DELIMITER;
5455public String parseArgs(String args) {
56if (args == null || args.length() == 0) {
57 log.error("propertyNames must be set for this transformer");
58returnnull;
59 }
6061 log.info("Initializing JMSMessagePropertyTransformer: args=" + args);
6263 propertyNames = new ArrayList<String>();
6465 String[] tokens = args.split(" ");
66for (String propertyName : tokens[0].split(",")) {
67 propertyNames.add(propertyName);
68 }
6970for(int i = 1; i < tokens.length; i++) {
71 String token = tokens[i];
7273if ("-d".equals(token) && i <= tokens.length - 2) {
74 String value = tokens[++i];
7576// we lost all spaces with the split, so we have to put them back, yuck.77while (i <= tokens.length - 2 && !tokens[i + 1].startsWith("-")) {
78 value = value + " " + tokens[++i];
79 }
8081 delimiter = trimSingleQuotes(value);
82 }
83elseif ("-r".equals(token) && i <= tokens.length - 2) {
84// requiredPropertyNames = null means all are required.85 requiredPropertyNames = new ArrayList<String>();
8687 String[] required = tokens[++i].split(",");
88for (String r : required) {
89 requiredPropertyNames.add(r);
90 }
91 }
92 }
9394 log.info("Initialized JMSMessagePropertyTransformer: delimiter='" +
95 delimiter + "', propertyNames=" + propertyNames +
96", requiredProperties=" +
97 (requiredPropertyNames == null ? "ALL" : requiredPropertyNames));
98return args;
99 }
100101/**102 * Transforms message propertes into a byte array delimtied by delimiter. If103 * all of the configured message properties are not found, returns null.104 * <P>105 * The could be enhanced to support the concept of optional/required properties.106 * @param message107 * @return108 * @throws JMSException109 */110public byte[] transform(Message message) throws JMSException {
111112if (propertyNames == null || propertyNames.size() == 0) {
113 log.error("No message properties configured for this JMS transformer.");
114returnnull;
115 }
116117int valuesFound = 0;
118 StringBuilder sb = new StringBuilder();
119for (String propertyName : propertyNames) {
120 Object propertyValue = message.getObjectProperty(propertyName);
121 String value = transformValue(propertyName, propertyValue);
122123// is a required value not found?124if (value == null && (requiredPropertyNames == null ||
125 requiredPropertyNames.contains(propertyName))) {
126returnnull;
127 }
128129if (valuesFound > 0) {
130 sb.append(delimiter);
131 }
132133if (value != null) {
134 sb.append(value);
135 }
136137 valuesFound++;
138 }
139140if (sb.length() == 0 || valuesFound != propertyNames.size()) {
141returnnull;
142 }
143144return sb.toString().getBytes();
145 }
146147/**148 * Transforms the propertyValue found into the string that should be used for149 * the message. Can handle String values and Number values. Override this method150 * to handle other Java types, or to apply other value transformation logic.151 *152 * @param propertyName The name of the JMS property153 * @param propertyValue The value of the property, which might be null.154 * @return155 */156protected String transformValue(String propertyName, Object propertyValue) {
157158if (propertyValue == null) {
159returnnull;
160 }
161elseif (propertyValue instanceof String) {
162return (String)propertyValue;
163 }
164elseif (propertyValue instanceof Number) {
165return propertyValue.toString();
166 }
167168returnnull;
169 }
170171privatestatic String trimSingleQuotes(String value) {
172if (value.length() == 0) {
173return value;
174 }
175176// trim leading and trailing quotes177if (value.charAt(0) == '\'') {
178 value = value.substring(1);
179 }
180if (value.length() > 0 && value.charAt(value.length() - 1) == '\'') {
181 value = value.substring(0, value.length() - 1);
182 }
183184return value;
185 }
186187 }