Fluxo dinâmicos usando Spring Aplication
A capacidade de controlar a forma como mula cria e gerencia contexto de aplicação
Primavera é um recurso muito útil. Em um compromisso com o cliente recente, eu tinha que
realizar uma operação de integração idêntico, mas a partir de um número variável de fontes
(diferentes para cada ambiente).
Em mula, isto pode ser realizado por:
Adicionando fluxos específicos do sistema duplicado em tempo de compilação, ou
Criando um fluxo modelo, parametrização de configuração e criar uma instância contexto de
aplicação para cada configuração.
Eu prefiro a opção 2, uma vez que é seco e permite controlar facilmente o número de fluxos
de configuração não código.
Como um caso de uso, vamos supor que temos parceiros de negócios que colocam arquivos
em baldes S3, onde buscá-las e movê-los para um local S3 separado. Vamos agora dizer que
nós temos um número variável de parceiros e, portanto, um número variável de origem
baldes S3.
On-boarding novos parceiros significaria que origem e de destino baldes seria diferente e,
provavelmente, frequência de consulta tão bem, mas resto deve permanecer o mesmo. Isto
pode ser alcançado através de:
a adição de um parceiro de configuração específica, e, em seguida
programaticamente criando um contexto de aplicação de um fluxo de template
Construir o nosso aplicativo
Um fluxo modelo é necessário para que possamos parametrizar-lo e mudar endpoints.
Vamos usar seguinte pasta de destino de fluxo e fonte de mudança / baldes de destino e
frequência de consulta.
<flow>
<poll doc:name="Poll">
<fixed-frequency-scheduler frequency="20" timeUnit="SECONDS"/>
<logger message="Flow Start: " level="INFO" doc:name="Log Start"/>
</poll>
<s3:list-objects config-ref="Amazon_S3_GlobalConnector" bucketName="test-partner-1" d
oc:name="List objects"/>
<foreach collection="#[payload]" doc:name="For Each">
<enricher doc:name="Message Enricher" target="#[flowVars['copyObjectResult']]">
<s3:copy-object config-ref="Amazon_S3_GlobalConnector" destinationBucketName="tes
t-system-1" destinationKey="#[payload.getKey()]" sourceBucketName="#[payload.getBuck
etName()]" sourceKey="#[payload.getKey()]" doc:name="Copy object"/>
</enricher>
<s3:delete-object config-ref="Amazon_S3_GlobalConnector" bucketName="test-partner-1
" key="#[payload.getKey()]" doc:name="Delete object"/>
</foreach>
<logger message="Flow End" level="INFO" doc:name="Log Start"/>
</flow>
Bater em mula fases do ciclo de vida de aplicação nos daria a oppurtunity para ler um
arquivo de propriedades e começar um contexto de aplicação com os detalhes específicos de
parceiros. Uma classe Java que implementa org.mule.api.lifecycle.Initialisable e
org.mule.api.lifecycle.Disposable nos deixaria fazer isso.
Durante a fase de Inicializar, mudamos parceiro específico detalhes em nosso fluxo de
modelo e iniciar um contexto de aplicação.
Em fase de descarte, paramos e dispor nosso contexto (s) pedido
public void initialise() throws InitialisationException {
// read template flow
// replace endpoints with properties from configuration file
// create and start an application context
}
public void dispose() {
// stop and dispose application context(s)
}
E, finalmente, uma falha principal, que nos dá acesso às propriedades do arquivo, e chutar
iniciar nossa aplicação.
<context:property-placeholder location="mule-app.properties" />
<flow>
<component doc:name="RouteFactory">
<singleton-object class="demo.RouteFactory">
<property key="partnerCount" value="${partner-count}"/>
</singleton-object>
</component>
</flow>
Dependendo do número de parâmetros, arquivo de propriedades pode acabar olhar como
seguir
partner-count=2
partner-1.flow-name = partner-one-flow
partner-1.polling-frequency = 10
partner-1.source-bucket = test-partner-1
partner-1.destination-folder = partner1/
partner-1.destination-bucket = test-system-1
partner-2.flow-name = partner-two-flow
partner-2.polling-frequency = 20
partner-2.source-bucket = test-partner-2
partner-2.destination-folder =
partner-2.destination-bucket = test-system-2
RouteFactory.java
package demo;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.mule.api.MuleContext;
import org.mule.api.MuleException;
import org.mule.api.config.ConfigurationBuilder;
import org.mule.api.lifecycle.Disposable;
import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.config.ConfigResource;
import org.mule.config.spring.SpringXmlConfigurationBuilder;
import org.mule.context.DefaultMuleContextFactory;
public class RouteFactory implements Initialisable, Disposable{
private HashMap<String,MuleContext> initialisedContexts;
private String s3Namespace = "http://www.mulesoft.org/schema/mule/s3";
private int partnerCount ;
public RouteFactory() {
initialisedContexts = new HashMap<String,MuleContext>();
}
@Override
public void initialise() throws InitialisationException {
System.out.println(this.getClass().getName() + ".initialise(). Total partner count :
int i = 0;
while(i < partnerCount){
i++;
try {
ArrayList<String> config = new ArrayList<String>();
String flowName = System.getProperty("partner-"+i+".flow-name");
String sourceBucket = System.getProperty("partner-"+i+".source-buc
String destinationBucket = System.getProperty("partner-"+i+".destina
String destinationFolder = System.getProperty("partner-"+i+".destina
DocumentBuilderFactory docFactory = DocumentBuilderFactory.new
docFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document flowTemplate = docBuilder.parse(this.getClass().getClassL
S3-object-flow.xml"));
NodeList flow = flowTemplate.getElementsByTagName("flow");
NodeList scheduler = flowTemplate.getElementsByTagName("fixed-
NodeList listObject = flowTemplate.getElementsByTagNameNS(s3N
NodeList copyObject = flowTemplate.getElementsByTagNameNS(s3
NodeList deleteObject = flowTemplate.getElementsByTagNameNS(s
NodeList logger = flowTemplate.getElementsByTagName("logger");
// change flow name
flow.item(0).getAttributes().getNamedItem("name").setNodeValue( f
// change logger message
logger.item(0).getAttributes().getNamedItem("message").setNodeVal
// change frequency
scheduler.item(0).getAttributes().getNamedItem("frequency").setNod
// change source bucket
listObject.item(0).getAttributes().getNamedItem("bucketName").setN
// change destinationBucket and destinationKey(append folder name t
copyObject.item(0).getAttributes().getNamedItem("destinationBucke
copyObject.item(0).getAttributes().getNamedItem("destinationKey")
"payload.getKey()]");
// delete an object from a specific bucket
deleteObject.item(0).getAttributes().getNamedItem("bucketName").s
config.add(getStringFromDoc(flowTemplate));
addFlow(config, flowName);
}catch(Exception e) {
e.printStackTrace(System.out);
}
}// end of while
}
@Override
public void dispose() {
System.out.println(this.getClass().getName() + ".dispose(). Total partner count : "
Iterator<String> iterator = initialisedContexts.keySet().iterator();
while ( iterator.hasNext() ) {
MuleContext muleContext = initialisedContexts.get(iterator.next());
System.out.println(this.getClass().getName() + "Stopping context with flow
try {
muleContext.stop();
muleContext.dispose();
} catch (MuleException e) {
System.out.println(this.getClass().getName() + "Cannot stop context
e.printStackTrace(System.out);
}
}
}
private String getStringFromDoc(Document doc) throws TransformerException {
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
writer.flush();
return writer.toString();
}
private boolean addFlow(ArrayList<String> configs, String flowName) {
boolean flag = false;
try {
// add to mule context
MuleContext muleContext = new DefaultMuleContextFactory().createMule
ConfigResource configResource[] = new ConfigResource[1];
int i = 0 ;
for (String config : configs) {
configResource[i] = new ConfigResource(flowName, new ByteArray
i++;
}
ConfigurationBuilder builder = new SpringXmlConfigurationBuilder(config
builder.configure(muleContext);
muleContext.start();
initialisedContexts.put(flowName, muleContext);
flag = true;
} catch (MuleException me) {
me.printStackTrace(System.out);
}
return flag;
}
public void setPartnerCount(int partnerCount) {
this.partnerCount = partnerCount;
}
}
Conclusão
O exemplo que acabamos de considerar é bastante simples. Para a ousadia entre nós,
poderíamos até mesmo experimentar com a adição de uma API HTTP que cria e destrói os
fluxos para nós sem a necessidade de reiniciar a nossa aplicação.

Fluxo dinâmicos usando spring aplication

  • 1.
    Fluxo dinâmicos usandoSpring Aplication A capacidade de controlar a forma como mula cria e gerencia contexto de aplicação Primavera é um recurso muito útil. Em um compromisso com o cliente recente, eu tinha que realizar uma operação de integração idêntico, mas a partir de um número variável de fontes (diferentes para cada ambiente). Em mula, isto pode ser realizado por: Adicionando fluxos específicos do sistema duplicado em tempo de compilação, ou Criando um fluxo modelo, parametrização de configuração e criar uma instância contexto de aplicação para cada configuração. Eu prefiro a opção 2, uma vez que é seco e permite controlar facilmente o número de fluxos de configuração não código. Como um caso de uso, vamos supor que temos parceiros de negócios que colocam arquivos em baldes S3, onde buscá-las e movê-los para um local S3 separado. Vamos agora dizer que nós temos um número variável de parceiros e, portanto, um número variável de origem baldes S3. On-boarding novos parceiros significaria que origem e de destino baldes seria diferente e, provavelmente, frequência de consulta tão bem, mas resto deve permanecer o mesmo. Isto pode ser alcançado através de: a adição de um parceiro de configuração específica, e, em seguida programaticamente criando um contexto de aplicação de um fluxo de template Construir o nosso aplicativo Um fluxo modelo é necessário para que possamos parametrizar-lo e mudar endpoints. Vamos usar seguinte pasta de destino de fluxo e fonte de mudança / baldes de destino e frequência de consulta. <flow> <poll doc:name="Poll">
  • 2.
    <fixed-frequency-scheduler frequency="20" timeUnit="SECONDS"/> <loggermessage="Flow Start: " level="INFO" doc:name="Log Start"/> </poll> <s3:list-objects config-ref="Amazon_S3_GlobalConnector" bucketName="test-partner-1" d oc:name="List objects"/> <foreach collection="#[payload]" doc:name="For Each"> <enricher doc:name="Message Enricher" target="#[flowVars['copyObjectResult']]"> <s3:copy-object config-ref="Amazon_S3_GlobalConnector" destinationBucketName="tes t-system-1" destinationKey="#[payload.getKey()]" sourceBucketName="#[payload.getBuck etName()]" sourceKey="#[payload.getKey()]" doc:name="Copy object"/> </enricher> <s3:delete-object config-ref="Amazon_S3_GlobalConnector" bucketName="test-partner-1 " key="#[payload.getKey()]" doc:name="Delete object"/> </foreach> <logger message="Flow End" level="INFO" doc:name="Log Start"/> </flow> Bater em mula fases do ciclo de vida de aplicação nos daria a oppurtunity para ler um arquivo de propriedades e começar um contexto de aplicação com os detalhes específicos de parceiros. Uma classe Java que implementa org.mule.api.lifecycle.Initialisable e org.mule.api.lifecycle.Disposable nos deixaria fazer isso. Durante a fase de Inicializar, mudamos parceiro específico detalhes em nosso fluxo de modelo e iniciar um contexto de aplicação. Em fase de descarte, paramos e dispor nosso contexto (s) pedido public void initialise() throws InitialisationException { // read template flow // replace endpoints with properties from configuration file // create and start an application context } public void dispose() { // stop and dispose application context(s)
  • 3.
    } E, finalmente, umafalha principal, que nos dá acesso às propriedades do arquivo, e chutar iniciar nossa aplicação. <context:property-placeholder location="mule-app.properties" /> <flow> <component doc:name="RouteFactory"> <singleton-object class="demo.RouteFactory"> <property key="partnerCount" value="${partner-count}"/> </singleton-object> </component> </flow> Dependendo do número de parâmetros, arquivo de propriedades pode acabar olhar como seguir partner-count=2 partner-1.flow-name = partner-one-flow partner-1.polling-frequency = 10 partner-1.source-bucket = test-partner-1 partner-1.destination-folder = partner1/ partner-1.destination-bucket = test-system-1 partner-2.flow-name = partner-two-flow partner-2.polling-frequency = 20 partner-2.source-bucket = test-partner-2 partner-2.destination-folder = partner-2.destination-bucket = test-system-2 RouteFactory.java
  • 4.
    package demo; import java.io.ByteArrayInputStream; importjava.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.mule.api.MuleContext; import org.mule.api.MuleException; import org.mule.api.config.ConfigurationBuilder; import org.mule.api.lifecycle.Disposable; import org.mule.api.lifecycle.Initialisable; import org.mule.api.lifecycle.InitialisationException; import org.mule.config.ConfigResource; import org.mule.config.spring.SpringXmlConfigurationBuilder; import org.mule.context.DefaultMuleContextFactory; public class RouteFactory implements Initialisable, Disposable{ private HashMap<String,MuleContext> initialisedContexts; private String s3Namespace = "http://www.mulesoft.org/schema/mule/s3"; private int partnerCount ; public RouteFactory() { initialisedContexts = new HashMap<String,MuleContext>(); }
  • 5.
    @Override public void initialise()throws InitialisationException { System.out.println(this.getClass().getName() + ".initialise(). Total partner count : int i = 0; while(i < partnerCount){ i++; try { ArrayList<String> config = new ArrayList<String>(); String flowName = System.getProperty("partner-"+i+".flow-name"); String sourceBucket = System.getProperty("partner-"+i+".source-buc String destinationBucket = System.getProperty("partner-"+i+".destina String destinationFolder = System.getProperty("partner-"+i+".destina DocumentBuilderFactory docFactory = DocumentBuilderFactory.new docFactory.setNamespaceAware(true); DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); Document flowTemplate = docBuilder.parse(this.getClass().getClassL S3-object-flow.xml")); NodeList flow = flowTemplate.getElementsByTagName("flow"); NodeList scheduler = flowTemplate.getElementsByTagName("fixed- NodeList listObject = flowTemplate.getElementsByTagNameNS(s3N NodeList copyObject = flowTemplate.getElementsByTagNameNS(s3 NodeList deleteObject = flowTemplate.getElementsByTagNameNS(s NodeList logger = flowTemplate.getElementsByTagName("logger"); // change flow name flow.item(0).getAttributes().getNamedItem("name").setNodeValue( f // change logger message logger.item(0).getAttributes().getNamedItem("message").setNodeVal // change frequency scheduler.item(0).getAttributes().getNamedItem("frequency").setNod // change source bucket
  • 6.
    listObject.item(0).getAttributes().getNamedItem("bucketName").setN // change destinationBucketand destinationKey(append folder name t copyObject.item(0).getAttributes().getNamedItem("destinationBucke copyObject.item(0).getAttributes().getNamedItem("destinationKey") "payload.getKey()]"); // delete an object from a specific bucket deleteObject.item(0).getAttributes().getNamedItem("bucketName").s config.add(getStringFromDoc(flowTemplate)); addFlow(config, flowName); }catch(Exception e) { e.printStackTrace(System.out); } }// end of while } @Override public void dispose() { System.out.println(this.getClass().getName() + ".dispose(). Total partner count : " Iterator<String> iterator = initialisedContexts.keySet().iterator(); while ( iterator.hasNext() ) { MuleContext muleContext = initialisedContexts.get(iterator.next()); System.out.println(this.getClass().getName() + "Stopping context with flow try { muleContext.stop(); muleContext.dispose(); } catch (MuleException e) { System.out.println(this.getClass().getName() + "Cannot stop context e.printStackTrace(System.out); } } }
  • 7.
    private String getStringFromDoc(Documentdoc) throws TransformerException { DOMSource domSource = new DOMSource(doc); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.transform(domSource, result); writer.flush(); return writer.toString(); } private boolean addFlow(ArrayList<String> configs, String flowName) { boolean flag = false; try { // add to mule context MuleContext muleContext = new DefaultMuleContextFactory().createMule ConfigResource configResource[] = new ConfigResource[1]; int i = 0 ; for (String config : configs) { configResource[i] = new ConfigResource(flowName, new ByteArray i++; } ConfigurationBuilder builder = new SpringXmlConfigurationBuilder(config builder.configure(muleContext); muleContext.start(); initialisedContexts.put(flowName, muleContext); flag = true; } catch (MuleException me) { me.printStackTrace(System.out); } return flag; }
  • 8.
    public void setPartnerCount(intpartnerCount) { this.partnerCount = partnerCount; } } Conclusão O exemplo que acabamos de considerar é bastante simples. Para a ousadia entre nós, poderíamos até mesmo experimentar com a adição de uma API HTTP que cria e destrói os fluxos para nós sem a necessidade de reiniciar a nossa aplicação.