org.apache.hadoop.chukwa.datacollection.adaptor.jms.JMSMessagePropertyTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.chukwa.datacollection.adaptor.jms.JMSMessagePropertyTransformer.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.hadoop.chukwa.datacollection.adaptor.jms;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.jms.Message;
import javax.jms.JMSException;

import java.nio.charset.Charset;
import java.util.ArrayList;

/**
 * JMSMessageTransformer that uses the properties of a JMS Message to build a
 * Chukwa record payload. The value for each property configured will be used
 * to create the record, with the delimiter value between each. The default
 * delimiter is a tab (i.e., '\t').
 * <P>
 * To configure this transformer, set the -p field of the adaptor to the
 * following (surrounded with double quotes):
 * <code>
 * <propertyNames> [-d <delimiter>] [-r <requiredPropertyNames>]
 * </code>
 * <ul>
 * <li><code>propertyNames</code> - Comma-separated list of JMS properties.</li>
 * <li><code>delimiter</code> - Delimiter to use, in single quotes.</li>
 * <li><code>requiredPropertyNames</code> - Comma-separated list of required
 * JMS properties. Default behavior is that all properties are required.</li>
 * </ul>
 *
 */
public class JMSMessagePropertyTransformer implements JMSMessageTransformer {
    protected Log log = LogFactory.getLog(getClass());

    private static final String DEFAULT_DELIMITER = "\t";

    ArrayList<String> propertyNames = null;
    ArrayList<String> requiredPropertyNames = null;
    String delimiter = DEFAULT_DELIMITER;

    public String parseArgs(String args) {
        if (args == null || args.length() == 0) {
            log.error("propertyNames must be set for this transformer");
            return null;
        }

        log.info("Initializing JMSMessagePropertyTransformer: args=" + args);

        propertyNames = new ArrayList<String>();

        String[] tokens = args.split(" ");
        for (String propertyName : tokens[0].split(",")) {
            propertyNames.add(propertyName);
        }

        for (int i = 1; i < tokens.length; i++) {
            String token = tokens[i];

            if ("-d".equals(token) && i <= tokens.length - 2) {
                StringBuilder value = new StringBuilder();
                value.append(tokens[++i]);

                // we lost all spaces with the split, so we have to put them back, yuck.
                while (i <= tokens.length - 2 && !tokens[i + 1].startsWith("-")) {
                    value.append(" ");
                    value.append(tokens[++i]);
                }

                delimiter = trimSingleQuotes(value.toString());
            } else if ("-r".equals(token) && i <= tokens.length - 2) {
                // requiredPropertyNames = null means all are required.
                requiredPropertyNames = new ArrayList<String>();

                String[] required = tokens[++i].split(",");
                for (String r : required) {
                    requiredPropertyNames.add(r);
                }
            }
        }

        log.info("Initialized JMSMessagePropertyTransformer: delimiter='" + delimiter + "', propertyNames="
                + propertyNames + ", requiredProperties="
                + (requiredPropertyNames == null ? "ALL" : requiredPropertyNames));
        return args;
    }

    /**
     * Transforms message propertes into a byte array delimtied by delimiter. If
     * all of the configured message properties are not found, returns null.
     * <P>
     * The could be enhanced to support the concept of optional/required properties.
     * @param message
     * @return
     * @throws JMSException
     */
    public byte[] transform(Message message) throws JMSException {

        if (propertyNames == null || propertyNames.size() == 0) {
            log.error("No message properties configured for this JMS transformer.");
            return null;
        }

        int valuesFound = 0;
        StringBuilder sb = new StringBuilder();
        for (String propertyName : propertyNames) {
            Object propertyValue = message.getObjectProperty(propertyName);
            String value = transformValue(propertyName, propertyValue);

            // is a required value not found?
            if (value == null && (requiredPropertyNames == null || requiredPropertyNames.contains(propertyName))) {
                return null;
            }

            if (valuesFound > 0) {
                sb.append(delimiter);
            }

            if (value != null) {
                sb.append(value);
            }

            valuesFound++;
        }

        if (sb.length() == 0 || valuesFound != propertyNames.size()) {
            return null;
        }

        return sb.toString().getBytes(Charset.forName("UTF-8"));
    }

    /**
     * Transforms the propertyValue found into the string that should be used for
     * the message. Can handle String values and Number values. Override this method
     * to handle other Java types, or to apply other value transformation logic.
     *
     * @param propertyName The name of the JMS property
     * @param propertyValue The value of the property, which might be null.
     * @return
     */
    protected String transformValue(String propertyName, Object propertyValue) {

        if (propertyValue == null) {
            return null;
        } else if (propertyValue instanceof String) {
            return (String) propertyValue;
        } else if (propertyValue instanceof Number) {
            return propertyValue.toString();
        }

        return null;
    }

    private static String trimSingleQuotes(String value) {
        if (value.length() == 0) {
            return value;
        }

        // trim leading and trailing quotes
        if (value.charAt(0) == '\'') {
            value = value.substring(1);
        }
        if (value.length() > 0 && value.charAt(value.length() - 1) == '\'') {
            value = value.substring(0, value.length() - 1);
        }

        return value;
    }

}