/** * Copyright 2016 Yahoo Inc. * * Licensed 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 com.yahoo.pulsar.client.cli; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.google.common.collect.Lists; import com.google.common.util.concurrent.RateLimiter; import com.yahoo.pulsar.client.api.ClientConfiguration; import com.yahoo.pulsar.client.api.Message; import com.yahoo.pulsar.client.api.MessageBuilder; import com.yahoo.pulsar.client.api.Producer; import com.yahoo.pulsar.client.api.PulsarClient; import com.yahoo.pulsar.client.api.PulsarClientException; /** * pulsar-client produce command implementation. * */ @Parameters(commandDescription = "Produce messages to a specified topic") public class CmdProduce { private static final Logger LOG = LoggerFactory.getLogger(PulsarClientTool.class); private static final int MAX_MESSAGES = 1000; @Parameter(description = "TopicName", required = true) private List<String> mainOptions; @Parameter(names = { "-m", "--messages" }, description = "Comma separted string messages to send, " + "either -m or -f must be specified.") private List<String> messages = Lists.newArrayList(); @Parameter(names = { "-f", "--files" }, description = "Comma separated file paths to send, either " + "-m or -f must be specified.") private List<String> messageFileNames = Lists.newArrayList(); @Parameter(names = { "-n", "--num-produce" }, description = "Number of times to send message(s), " + "the count of messages/files * num-produce should below than " + MAX_MESSAGES + ".") private int numTimesProduce = 1; @Parameter(names = { "-r", "--rate" }, description = "Rate (in msg/sec) at which to produce, " + "value 0 means to produce messages as fast as possible.") private double publishRate = 0; private String serviceURL = null; ClientConfiguration clientConfig; public CmdProduce() { // Do nothing } /** * Set Pulsar client configuration. * */ public void updateConfig(String serviceURL, ClientConfiguration newConfig) { this.serviceURL = serviceURL; this.clientConfig = newConfig; } /* * Generate a list of message bodies which can be used to build messages * * @param stringMessages List of strings to send * * @param messageFileNames List of file names to read and send * * @return list of message bodies */ private List<byte[]> generateMessageBodies(List<String> stringMessages, List<String> messageFileNames) { List<byte[]> messageBodies = new ArrayList<byte[]>(); for (String m : stringMessages) { messageBodies.add(m.getBytes()); } try { for (String filename : messageFileNames) { File f = new File(filename); FileInputStream fis = new FileInputStream(f); byte[] fileBytes = new byte[(int) f.length()]; fis.read(fileBytes); messageBodies.add(fileBytes); fis.close(); } } catch (Exception e) { LOG.error(e.getMessage(), e); } return messageBodies; } /* * Generates a list of messages that can be produced * * @param stringMessages List of strings to send as messages * * @param messageFileNames List of file names to read and send as messages * * @return list of messages to send */ private List<Message> generateMessages(List<byte[]> messageBodies) { List<Message> messagesToSend = new ArrayList<Message>(); try { for (byte[] msgBody : messageBodies) { MessageBuilder msgBuilder = MessageBuilder.create(); msgBuilder.setContent(msgBody); messagesToSend.add(msgBuilder.build()); } } catch (Exception e) { LOG.error(e.getMessage(), e); } return messagesToSend; } /** * Run the producer. * * @return 0 for success, < 0 otherwise * @throws Exception */ public int run() throws PulsarClientException { if (mainOptions.size() != 1) throw (new ParameterException("Please provide one and only one topic name.")); if (this.serviceURL == null || this.serviceURL.isEmpty()) throw (new ParameterException("Broker URL is not provided.")); if (this.numTimesProduce <= 0) throw (new ParameterException("Number of times need to be positive number.")); if (messages.size() == 0 && messageFileNames.size() == 0) throw (new ParameterException("Please supply message content with either --messages or --files")); int totalMessages = (messages.size() + messageFileNames.size()) * numTimesProduce; if (totalMessages > MAX_MESSAGES) { String msg = "Attempting to send " + totalMessages + " messages. Please do not send more than " + MAX_MESSAGES + " messages"; throw new ParameterException(msg); } String topic = this.mainOptions.get(0); int numMessagesSent = 0; int returnCode = 0; try { PulsarClient client = PulsarClient.create(this.serviceURL, this.clientConfig); Producer producer = client.createProducer(topic); List<byte[]> messageBodies = generateMessageBodies(this.messages, this.messageFileNames); RateLimiter limiter = (this.publishRate > 0) ? RateLimiter.create(this.publishRate) : null; for (int i = 0; i < this.numTimesProduce; i++) { List<Message> messages = generateMessages(messageBodies); for (Message msg : messages) { if (limiter != null) limiter.acquire(); producer.send(msg); numMessagesSent++; } } client.close(); } catch (Exception e) { LOG.error("Error while producing messages"); LOG.error(e.getMessage(), e); returnCode = -1; } finally { LOG.info("{} messages successfully produced", numMessagesSent); } return returnCode; } }