/* jBilling - The Enterprise Open Source Billing System Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde This file is part of jbilling. jbilling is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jbilling 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with jbilling. If not, see <http://www.gnu.org/licenses/>. */ package com.sapienter.jbilling.server.provisioning; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Message; import javax.jms.Session; import org.apache.log4j.Logger; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; import com.sapienter.jbilling.common.SessionInternalError; import com.sapienter.jbilling.server.pluggableTask.admin.PluggableTaskException; import com.sapienter.jbilling.server.pluggableTask.admin.PluggableTaskManager; import com.sapienter.jbilling.server.provisioning.config.Command; import com.sapienter.jbilling.server.provisioning.config.Field; import com.sapienter.jbilling.server.provisioning.config.Processor; import com.sapienter.jbilling.server.provisioning.config.Provisioning; import com.sapienter.jbilling.server.provisioning.config.Request; import com.sapienter.jbilling.server.provisioning.task.IExternalProvisioning; import com.sapienter.jbilling.server.util.Constants; import com.sapienter.jbilling.server.util.Context; /** * Logic for external provisioning module. Receives a command from the * commands rules task via JMS. The configuration file * jbilling-provisioning.xml is used to map this command to command * strings for specific external provisioning processors. Publishes * results in a JMS topic. */ public class ExternalProvisioning { private static final Logger LOG = Logger.getLogger( ExternalProvisioning.class); private MapMessage message; /** * Receives and processes a command message from the command rules task. * This method is called through the ProvisioningProcessSessionBean * so that it runs in a transaction. ExternalProvisioningMDB is * the class that actually receives the message. */ public void onMessage(Message myMessage) { try { message = (MapMessage) myMessage; Provisioning config = (Provisioning) Context.getBean( Context.Name.PROVISIONING); List<Command> commandsConfig = config.getCommands(); String command = message.getStringProperty("command"); // find command config for(Command commandConfig : commandsConfig) { if (command.equals(commandConfig.getId())) { LOG.debug("Found a command configuration"); processCommand(commandConfig); break; // no more configurations for this command? } } } catch (Exception e) { throw new SessionInternalError(e); } } /** * Processes a command according to the given configuration. */ private void processCommand(Command config) throws JMSException, PluggableTaskException { // process fields List<Field> fieldConfig = config.getFields(); Map<String, String> fields = new HashMap<String, String>(); for (Field field : fieldConfig) { String value = message.getStringProperty(field.getName()); if (value == null) { value = field.getDefaultValue(); } fields.put(field.getName(), value); } LOG.debug("Externalprovisioning.ProcessCommand()-> List of Command Fields:"); LOG.debug(fields); // call each configured processor for (Processor processor : config.getProcessors()) { PluggableTaskManager<IExternalProvisioning> taskManager = new PluggableTaskManager<IExternalProvisioning>( message.getIntProperty("entityId"), Constants.PLUGGABLE_TASK_EXTERNAL_PROVISIONING); IExternalProvisioning task = taskManager.getNextClass(); while (task != null) { if (task.getId().equals(processor.getId())) { callProcessor(task, processor, fields, message.getStringProperty("id")); break; } task = taskManager.getNextClass(); } if (task == null) { throw new SessionInternalError("Couldn't find external " + "provisioining task with id: " + processor.getId()); } } } /** * Processes each request to the given external provisioning task * as specified by the processor configuration. */ private void callProcessor(IExternalProvisioning task, Processor processor, Map<String, String> fields, String id) throws JMSException { List<Request> requests = processor.getRequests(); Collections.sort(requests); // sort by order for (Request request : requests) { LOG.debug("Submit string pattern: " + request.getSubmit()); // insert fields into submit string StringBuilder submit = new StringBuilder(request.getSubmit()); boolean keepLooking = true; while (keepLooking) { int barStartIndex = submit.indexOf("|"); int barEndIndex = submit.indexOf("|", barStartIndex + 1); if (barStartIndex == -1) { keepLooking = false; } else if (barEndIndex == -1) { throw new SessionInternalError("Mismatched '|' in submit " + "string. Index: " + barStartIndex); } else { String fieldName = submit.substring(barStartIndex + 1, barEndIndex); String fieldValue = fields.get(fieldName); LOG.debug("Replacing field name '" + fieldName + "' with value '" + fieldValue + "'"); submit.replace(barStartIndex, barEndIndex + 1, fieldValue); } } String submitString = submit.toString(); LOG.debug("Command string: " + submitString); // call external provisioning processor task String error = null; Map<String, Object> result = null; try { result = task.sendRequest(id, submitString); } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.close(); LOG.error("External provisioning processor error: " + e.getMessage() + "\n" + sw.toString()); error = e.toString(); } // post message if required if (request.getPostResult()) { postResults(result, error); } // only continue with other requests if correct result String continueOnType = request.getContinueOnType(); if (continueOnType != null && (result == null || !result.get("result").equals(continueOnType))) { LOG.debug("Skipping other results."); break; } } } /** * Posts results of external provisioning processing tasks. */ private void postResults(final Map<String, Object> results, final String error) throws JMSException { JmsTemplate jmsTemplate = (JmsTemplate) Context.getBean( Context.Name.JMS_TEMPLATE); Destination destination = (Destination) Context.getBean( Context.Name.PROVISIONING_COMMANDS_REPLY_DESTINATION); jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { MapMessage replyMessage = session.createMapMessage(); // add the original properties (names prefixed with 'in_') Enumeration originalPropNames = message.getPropertyNames(); while (originalPropNames.hasMoreElements()) { String propName = (String) originalPropNames.nextElement(); Object propValue = message.getObjectProperty(propName); replyMessage.setObjectProperty("in_" + propName, propValue); } if (error == null) { // add the properties returned by the processor Set<Map.Entry<String, Object>> entrySet = results.entrySet(); for (Map.Entry<String, Object> entry : entrySet) { replyMessage.setObjectProperty("out_" + entry.getKey(), entry.getValue()); } } else { // there was an error replyMessage.setStringProperty("out_result", "unavailable"); replyMessage.setStringProperty("exception", error); } return replyMessage; } }); } }