A queue é uma estrutura de dados usada para armazenar mensagens aguardando processamento. Amazon Simple Queue Services (Amazon SQS) é uma fila de mensagens altamente disponível e escalável habilitada para serviços da Web. Os benefícios principais da Amazon SQS são:
- Solução baseada na nuvem. Gerenciado por Amazon, nenhum conhecimento de infraestrutura nas instalações ou de suporte é necessário.
- Baseado na Internet. O serviço pode ser acessado por meio de serviços da Web em qualquer cliente com acesso à Internet, dessa forma permitindo a integração business-to-business (B2B).
- Redundância. O serviço armazena todas as mensagens em vários servidores para fornecer alta disponibilidade e tolerância a falhas.
- Várias leituras/gravações simultâneas. Amazon SQS suporta vários processos de leitura e gravação em uma fila simultaneamente e bloqueia mensagens durante a janela de processamento para impedir que dois clientes processem uma mensagem simultaneamente.
- Configurável. Com o serviço Amazon SQS, é possível projetar a janela de bloqueio com base nos requisitos de processamento exclusivos das mensagens armazenadas na fila. A janela de bloqueio impede que dois leitores da fila processem o mesmo item da fila. Uma janela maior é necessária para itens da fila que levam mais tempo para serem processados. A janela de bloqueio é controlada pelo parâmetro de tempo limite de visibilidade, que é possível configurar fila por fila.
- API simples de usar. Fornece wrappers de API para linguagens comuns, incluindo as plataformas Java e Microsoft .NET, para permitir o rápido desenvolvimento e a integração perfeita aos aplicativos existentes.
- Solução de baixo custo. As empresas pagam apenas pela largura de banda que usam e pelas solicitações de HTTP; Amazon SQS não tem nenhuma estrutura de taxa mínima.
É útil conhecer algumas características do Amazon SQS antes de entrar no desenvolvimento do Amazon SQS. Sem conhecimento dessas características, é possível que você considere o trabalho com o Amazon SQS frustrante e confuso inicialmente.
Primeiro, Amazon não dá nenhuma garantia da ordem em que os itens da fila são processados. Isso significa que o processamento primeiro a entrar, primeiro a sair (FIFO) não é garantido, como é comum com muitas implementações de fila de mensagens. Amazon garante apenas que todas as mensagens serão entregues.
A segunda característica importante do Amazon SQS é o conceito de consistência eventual.
Os recursos principais de sistemas de banco de dados grandes são consistência, alta disponibilidade e escalabilidade.
Em vez de volta o foco para os três, Amazon optou por enfocar a alta disponibilidade e a escalabilidade e fornecer consistência eventual. Isso significa que o Amazon propaga todas as mensagens a vários servidores para alcançar alta disponibilidade e escalabilidade. Amazon garante que todas as mensagens serão entregues, mas não dá nenhuma garantia de quando elas serão entregues. A partir de uma perspectiva prática, isso significa que se você enviar três mensagens para uma fila, na próxima tentativa de receber as mensagens, não haverá nenhuma garantia de que as três mensagens serão recebidas. É possível receber as três mensagens em uma única
Leitura ou duas mensagens na primeira Leitura e a terceira mensagem em uma Leitura subsequente .
Se você pesquisar a fila continuamente, receberá as três mensagens consequentemente.
Para começar a trabalhar com o Amazon SQS, é preciso obter a biblioteca de API do Amazon SQS com base na linguagem sendo usada. Amazon fornece uma biblioteca para todas as linguagens comuns, como
Perl, Microsoft Visual Basic®.NET, C#, Java e PHP.
As bibliotecas são software livre e simples de usar. Consulte Recursos
para os links para fazer o download dessas bibliotecas.
Usando Amazon SQS com a linguagem Java
Inicialmente, você verá como criar uma fila, enviar uma mensagem e receber uma mensagem usando a linguagem Java. A primeira etapa é criar uma fila Amazon SQS. O código
na Listagem 1 mostra como criar o cliente HTTP para o Amazon
SQS, instanciar o objeto CreateQueueRequest e chamar a solicitação de criação de fila. O ID de chave de acesso é a chave (20 caracteres, sequência alfanumérica) necessária para solicitar autenticação ou ler itens da fila de mensagens. Para criar ou manipular itens de fila, é preciso ter a chave de acesso secreta (40 caracteres, sequência alfanumérica). Você recebe essas chaves ao se registrar no Amazon. (Consulte Recursos para obter um link para um artigo sobre segurança na nuvem que descreve essas chaves em mais detalhes.)
Listagem 1. Criar a fila Amazon SQS
String queueName = "TestQueue";
// create http client
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
// instantiate create queue request
CreateQueueRequest request = new CreateQueueRequest();
request.setQueueName(queueName);
request.setDefaultVisibilityTimeout(30);
// execute create queue operation and get the server response
System.out.print("Creating Queue: " + queueName);
CreateQueueResponse response = service.createQueue(request);
if (response.isSetCreateQueueResult()) {
System.out.print("Create Queue Result:");
CreateQueueResult createQueueResult = response.getCreateQueueResult();
if (createQueueResult.isSetQueueUrl()) {
System.out.print("Queue Url: " + createQueueResult.getQueueUrl());
}
}
|
A próxima etapa é enviar uma mensagem à fila recém-criada. O código na Listagem 2 mostra como criar o cliente HTTP para Amazon SQS e como enviar uma mensagem simples à fila.
Listagem 2. Enviar uma mensagem à fila
String queueName = "TestQueue";
// create http client
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
// instantiate send message request
SendMessageRequest request = new SendMessageRequest();
request.setQueueName(queueName);
request.setMessageBody("Test SQS Message");
// execute the send message operation and get the server response
SendMessageResponse response = service.sendMessage(request);
if (response.isSetSendMessageResult()) {
System.out.print("Send Message Result: ");
SendMessageResult sendMessageResult = response.getSendMessageResult();
if (sendMessageResult.isSetMessageId()) {
System.out.print("\tMessageId: " + sendMessageResult.getMessageId());
}
}
|
Agora, receba a mensagem da fila. A Listagem 3 mostra como criar o cliente HTTP para Amazon SQS e como receber uma mensagem da fila.
Message contém a mensagem da fila e expõe vários métodos principais:
getMessageId. Retorna o identificador exclusivo da mensagem. É possível usarisSetMessageIdpara determinar se o ID da mensagem foi definido.getReceiptHandle. Retorna o identificador para a mensagem. O identificador é usado para excluir a mensagem. É possível usarisSetReceiptHandlepara determinar se o identificador da mensagem foi definido.getBody. Retorna o corpo da mensagem em uma cadeia de caractere. A mensagem pode ser texto simples ou XML, e é possível usarisSetBodypara determinar se o corpo da mensagem foi definido.
Listagem 3. Receber uma mensagem da fila
String queueName = "TestQueue";
// create http client
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
// instantiate the receive message request
ReceiveMessageRequest request = new ReceiveMessageRequest();
request.setQueueName(queueName);
// the following two parameters are optional
request.setMaxNumberOfMessages(10); // set maximum number of messages to receive
request.setVisibilityTimeout(30); // set visibility window
// execute the receive messages operation and get server response
ReceiveMessageResponse response = service.receiveMessage(request);
System.out.print("Receive Message Response:");
if (response.isSetReceiveMessageResult()) {
ReceiveMessageResult receiveMessageResult = response.getReceiveMessageResult();
java.util.List<Message> messageList = receiveMessageResult.getMessage();
for (Message message : messageList) {
if (message.isSetMessageId()) {
System.out.print("MessageId: " + message.getMessageId());
}
if (message.isSetReceiptHandle()) {
System.out.print("ReceiptHandle: " + message.getReceiptHandle());
}
if (message.isSetBody()) {
System.out.print("Body: " + message.getBody());
}
}
|
Agora, você serializará um objeto para XML e o enviará como uma mensagem Amazon SQS usando C#.
A primeira etapa é criar um objeto de negócios que será serializado; a Listagem 4 mostra um objeto Product. As propriedades públicas são decoradas com os atributos para controlar a serialização XML.
Os atributos C#, que são similares às anotações Java, definem como as propriedades são mapeadas para elementos XML ou atributos XML. Além disso, a classe contém o método ToXml() , que serializa a instância do objeto para XML.
Listagem 4. Criar um objeto de negócios para serialização
namespace Stewart.Test
{
/// <summary>
/// Product
/// </summary>
[XmlRoot(ElementName="Product")]
public class Product
{
/// <summary>
/// Product Name
/// </summary>
[XmlElement("ProductName")]
public string ProductName;
/// <summary>
/// Product Price
/// </summary>
[XmlElement("ProductPrice")]
public decimal ProductPrice;
/// <summary>
/// Quantity in stock
/// </summary>
[XmlElement("InStock")]
public bool InStock;
/// <summary>
/// Product Id
/// </summary>
[XmlAttributeAttribute(AttributeName = "Id", DataType = "integer")]
public string Id;
/// <summary>
/// Initializes a new instance of the <see cref="Product"/> class.
/// </summary>
public Product()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Product"/> class.
/// </summary>
/// <param name="productName">Name of the product.</param>
/// <param name="productPrice">The product price.</param>
public Product(string productName, decimal productPrice)
{
this.ProductName = productName;
this.ProductPrice = productPrice;
}
/// <summary>
/// Converts to XML.
/// </summary>
/// <returns></returns>
public String ToXml()
{
StringBuilder output = new StringBuilder();
// no name space
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// settings to omit xml declaration
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
// finally serialize to string
XmlWriter writer = XmlTextWriter.Create(output, settings);
XmlSerializer serializer = new XmlSerializer(typeof(Product));
serializer.Serialize(writer, this, ns);
// return string containing XML document
return output.ToString();
}
}
|
Em seguida, envie a mensagem XML. A API Amazon C# para Amazon SQS é funcionalmente equivalente e similar a Java API. O código na Listagem 5
mostra como enviar a mensagem usando C#.
Listagem 5. Enviar a mensagem usando C#
Product prod = new Product("Widget", 1.5M);
string accessKeyId = ConfigurationSettings.AppSettings["AmazonAccessKeyID"];
string secretAccessKey = ConfigurationSettings.AppSettings["AmazonSecretAccessKey"];
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
SendMessageRequest request = new SendMessageRequest();
request.MessageBody = prod.ToXml();
request.QueueName = "TestQueue";
SendMessageResponse response = service.SendMessage(request);
if (response.IsSetSendMessageResult())
{
Console.WriteLine("Send Message Response: ");
SendMessageResult sendMessageResult = response.SendMessageResult;
if (sendMessageResult.IsSetMessageId())
{
Console.WriteLine(String.Format("MessageId {0}",
sendMessageResult.MessageId));
}
if (sendMessageResult.IsSetMD5OfMessageBody())
{
Console.WriteLine(String.Format("MD5OfMessageBody: {0}",
sendMessageResult.MD5OfMessageBody));
}
}
|
Figura 1 mostra a saída da Listagem 5.
Figura 1. Enviar saída da mensagem XML
A etapa final é receber a mensagem XML da fila e desserializar a instância. A Listagem 6 mostra o código para desserializar a mensagem XML em uma instância de Product .
Listagem 6. Desserializar a mensagem XML
Product prod = null;
string accessKeyId = ConfigurationSettings.AppSettings["AmazonAccessKeyID"];
string secretAccessKey = ConfigurationSettings.AppSettings["AmazonSecretAccessKey"];
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
ReceiveMessageRequest request = new ReceiveMessageRequest();
request.QueueName = "TestQueue";
ReceiveMessageResponse response = service.ReceiveMessage(request);
if (response.IsSetReceiveMessageResult())
{
Console.WriteLine("Receive Message Result:");
ReceiveMessageResult receiveMessageResult = response.ReceiveMessageResult;
List<Message> messageList = receiveMessageResult.Message;
foreach (Message message in messageList)
{
if (message.IsSetMessageId())
{
Console.WriteLine(String.Format("MessageId: {0}",
message.MessageId));
}
if (message.IsSetBody())
{
Console.WriteLine(string.Format("Body: {0}", message.Body));
String xml = message.Body;
StringReader sr = new StringReader(xml);
XmlSerializer serializer = new XmlSerializer(typeof(Product));
prod = (Product) serializer.Deserialize(sr);
Console.WriteLine(string.Format("Id: {0}", prod.Id));
Console.WriteLine(string.Format("Name: {0}", prod.ProductName));
Console.WriteLine(string.Format("Price: {0}", prod.ProductPrice));
}
}
}
|
Figura 2 mostra a saída da Listagem 6.
Figura 2. Receber saída da mensagem XML
Embora a amostra acima seja muito simples, ela é muito poderosa porque é possível serializar um objeto e enviar a mensagem a outro aplicativo cujos limites de aplicativo não possuem restrição para uma rede física local. Não há nenhuma restrição de firewall complexa ou consideração de segurança. Além disso, o remetente e o destinatário da mensagem não precisam ser escritos na mesma linguagem ou até mesmo usar a mesma plataforma.
A solução de demonstração consiste em um revendedor e um fabricante que precisam integrar seus processos de negócios. O revendedor compra widgets do fabricante e os vende aos cliente.
Quando um cliente quer widgets, o revendedor usa um cliente C# WinForm para enviar ao cliente uma ordem. O programa de envio de ordens armazena os detalhes da ordem em um banco de dados MySQL local. O cliente também permite que os usuários busquem inventários, visualizem ordens e visualizem as filas de mensagens do Amazon SQS.
Normalmente, o revendedor tem widgets suficientes no inventário para atender à ordem do cliente. Caso contrário, o revendedor envia uma ordem de compra ao fabricante quase em tempo real. O fabricante enviará de volta um resumo das ordens quando os itens tiverem sido enviados. Toda a comunicação é feita com o uso do Amazon SQS.
Os serviços Order Fulfillment e Inventory Management do revendedor, que são também criados com o uso de C#, pesquisa merchandises recebidos e ordens pendentes dos clientes. Se, durante o processamento de uma ordem do cliente, o inventário de widgets estiver menor do que o número pedido, uma nova ordem de compra será enviada ao fabricante que usa o Amazon SQS. O corpo da mensagem do item de fila é um documento XML que contém a ordem de compra.
O serviço Order Processing do fabricante, que é criado na plataforma Java, processa a fila de ordens de compra. Ele enviará uma mensagem de volta ao revendedor usando Amazon SQS quando os itens tiverem sido enviados. O corpo da mensagem é o documento que contém o resumo das ordens.
O diagrama da Figura 3 mostra os sistemas envolvidos.
Figura 3. Visão geral da solução
A primeira etapa é definir os esquemas XML das mensagens enviadas entre o revendedor e o fabricante. Dois esquemas são necessários: uma ordem de compra e um resumo das ordens.
Listagem 7 mostra o esquema da ordem de compra.
Listagem 7. Esquema da ordem de compra
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="PurchaseOrder">
<xs:complexType>
<xs:sequence>
<xs:element name="Id" type="xs:string" minOccurs="0" />
<xs:element name="OrderDate" type="xs:string" minOccurs="0" />
<xs:element name="Company" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="CompanyName" type="xs:string" minOccurs="0" />
<xs:element name="StreetAddress" type="xs:string" minOccurs="0" />
<xs:element name="City" type="xs:string" minOccurs="0" />
<xs:element name="State" type="xs:string" minOccurs="0" />
<xs:element name="ZipCode" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Vendor" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="CompanyName" type="xs:string" minOccurs="0" />
<xs:element name="StreetAddress" type="xs:string" minOccurs="0" />
<xs:element name="City" type="xs:string" minOccurs="0" />
<xs:element name="State" type="xs:string" minOccurs="0" />
<xs:element name="ZipCode" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Items" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Item" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="Id" type="xs:string" />
<xs:attribute name="Name" type="xs:string" />
<xs:attribute name="Quantity" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
|
O esquema XML de ordens de compra consiste nos seguintes elementos principais:
Tabela 1. Elementos principais no esquema de ordem de compra
| Elementos principais | Descrição |
|---|---|
Id | Cadeia de caractere que contém o identificador exclusivo da ordem de compra |
OrderDate | Cadeia de caractere que contém a data da ordem de compra |
Company | Contém as informações de endereço principais relacionadas ao revendedor, incluindo nome da empresa, endereço, cidade, estado e CEP |
Vendor | Contém as informações de endereço principais relacionadas ao fornecedor, incluindo nome da empresa, endereço, cidade, estado e CEP |
Items | Contém todos os itens solicitados, incluindo ID do item, nome do item e quantidade |
Listagem 8 mostra o esquema do resumo de ordens.
Listagem 8. Esquema do resumo de ordens
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="OrderSummary">
<xs:complexType>
<xs:sequence>
<xs:element name="OrderId" type="xs:string" minOccurs="0" />
<xs:element name="ReferenceId" type="xs:string" minOccurs="0" />
<xs:element name="OrderDate" type="xs:string" minOccurs="0" />
<xs:element name="CompanyAddress" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="CompanyName" type="xs:string" minOccurs="0" />
<xs:element name="StreetAddress" type="xs:string" minOccurs="0" />
<xs:element name="City" type="xs:string" minOccurs="0" />
<xs:element name="State" type="xs:string" minOccurs="0" />
<xs:element name="ZipCode" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="CustomerAddress" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="CompanyName" type="xs:string" minOccurs="0" />
<xs:element name="StreetAddress" type="xs:string" minOccurs="0" />
<xs:element name="City" type="xs:string" minOccurs="0" />
<xs:element name="State" type="xs:string" minOccurs="0" />
<xs:element name="ZipCode" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Items" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Item" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="ItemId" type="xs:string" />
<xs:attribute name="ItemName" type="xs:string" />
<xs:attribute name="Quantity" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
|
O esquema XML de resumo de ordens consiste nos seguintes componentes principais:
Tabela 2. Elementos principais no esquema de resumo de ordens de compra.
Id | Cadeia de caractere que contém o identificador exclusivo do resumo de ordens |
ReferenceId | Cadeia de caractere que contém o ID da ordem de compra original |
OrderDate | Cadeia de caractere que contém a data do resumo de ordens de compra |
CustomerAddress | Contém as informações de endereço principais relacionadas ao revendedor, incluindo nome da empresa, endereço, cidade, estado e CEP |
VendorAddress | Contém as informações de endereço principais relacionadas ao fornecedor, incluindo nome da empresa, endereço, cidade, estado e CEP |
Items | Contém todos os itens solicitados, incluindo ID do item, nome do item e quantidade |
Definir o modelo de entidade de banco de dados
Em seguida, defina o esquema do banco de dados. Figura 4 mostra o diagrama do modelo de entidade de banco de dados.
Figura 4. Modelo de entidade de banco de dados
As entidades de dados do revendedor incluem o seguinte:
Customercontém as informações de contato do cliente para pedidos.CustomerOrdercontém as informações da ordem para a ordem do cliente.CustomerOrderDetailcontém detalhes de item de ordem para a ordem do cliente.Inventorycontém o inventário para o revendedor.
A entidade de dados do fabricante é:
VendorOrdercontrola as ordens de compra processadas pelo serviço Order Processing do fabricante.
Os últimos componentes que precisam ser definidos são as filas de mensagens. Tabela 3 mostra as filas de mensagens dessa solução.
Tabela 3. Filas de mensagens de Amazon SMS
| Nome da fila | Tempo limite de visibilidade | Finalidade |
|---|---|---|
| POQueue | 30 segundos | Mensagens de ordem de compra enviadas do revendedor para o fabricante |
| OSQueue | 30 segundos | Mensagens de resumo de ordens enviadas do fabricante ao revendedor |
O aplicativo do revendedor consiste nas seguintes entidades na Tabela 4.
Tabela 4. Entidades de negócios no aplicativo do revendedor
| Classe | Descrição |
|---|---|
CompanyAddressEntity | Contém um cliente ou um endereço comercial |
CustomerEntity | Contém um cliente |
OrderEntity | Contém um pedido do cliente |
OrderDetailEntity | Contém o detalhe em nível de itens de uma ordem do cliente |
InventoryItemEntity | Contém um item de inventário |
PurchaseOrderEntity | Contém uma ordem de compra |
PurchaseOrderDetailEntity | Contém o detalhe em nível de itens de uma ordem de compra |
No lado do Microsoft .NET, a classe BaseSQSDataProvider
também fornece uma classe de utilitário para sistema de mensagens de Amazon SQS. O
BaseDbProvider fornece uma classe de utilitário para trabalhar com bancos de dados MySQL.
O sistema de gerenciamento de ordens do revendedor
O cliente de gerenciamento de ordens do revendedor não é o foco deste artigo, mas o cliente é bem direto. É escrito em C# e é um aplicativo de n camadas com todos os negócios e dados separados da camada de apresentação. Toda a ligação do formulário é feita com o uso dos recursos poderosos de ligação de objeto do Microsoft .NET. Todas as listas suspensas, visualizações em grade e campos de formulário estão vinculados aos objetos de negócios. O código que está por trás dos formulários Windows® é mínimo, visto que a lógica de negócios reside na camada de lógica de negócios.
Figura 5 mostra o cliente.
Figura 5. Cliente de gerenciamento de ordens do revendedor
O serviço de preenchimento de ordens do revendedor
A classe OrderFulfillmentService é responsável por processar ordens do cliente e faz isso com base no intervalo de pesquisa especificado no arquivo App.config. O serviço recupera uma lista de ordens ainda não processadas. Para cada ordem na lista, as seguintes ações ocorrem nos métodos correspondentes:
- Verifique se a ordem pode ser enviada—ou seja, existe inventário suficiente (método
ProcessPendingOrders). - Processe a ordem se ela puder ser enviada (método
CanShip). - Coloque a ordem em pedido em atraso se ela não puder ser enviada e envie a ordem de compra ao fabricante (método
ProcessBackorder).
Listagem 9 mostra o método ProcessPendingOrders() .
Listagem 9. O método ProcessPendingOrders()
public int ProcessPendingOrders()
{
int itemsProcessed = 0;
// get orders not yet shipped
CustomerOrderFactory factory = new CustomerOrderFactory();
IList<OrderEntity> orders = factory.GetOrdersNotYetShipped();
// iterate through all orders not processed
IEnumerator<OrderEntity> ordersEnum = orders.GetEnumerator();
while (ordersEnum.MoveNext()) {
// get next order
OrderEntity curOrder = ordersEnum.Current;
Console.WriteLine(string.Format("Processing Order '{0}'...",
curOrder.Id));
// check if merchandise is available to ship
if (this.CanShip(curOrder)) {
// process order
if (this.ProcessOrder(curOrder)) {
itemsProcessed++;
}
}// if can ship order
else if (!curOrder.IsBackordered){
// set order to backordered
if (this.ProcessBackorder(curOrder)) {
itemsProcessed++;
}
} // if can't ship order (not enough merchandise)
} // while more orders to process
return itemsProcessed;
}
|
Para determinar se a ordem pode ser processada, a quantidade do item solicitada será verificada em relação ao nível de inventário de cada item. Listagem 10 mostra o método CanShip()
.
Listagem 10. O método CanShip()
private bool CanShip(OrderEntity order)
{
bool hasMerchandise = true;
// get items
IEnumerator<OrderDetailEntity> detailEnum = order.GetOrderItems();
// iterate through all items
while (detailEnum.MoveNext())
{
// get current item
OrderDetailEntity detailEntry = detailEnum.Current;
InventoryItemEntity inventoryItem = detailEntry.Item;
if (detailEntry.Quantity > inventoryItem.Quantity)
{
Console.WriteLine(
string.Format("Order {0} - Insufficient Inventory: {1} ({2})",
order.Id, inventoryItem.Name, inventoryItem.Id));
hasMerchandise = false;
} // if inventory is sufficient
} // while more entries to process
Console.WriteLine(string.Format("Order {0} - Can Ship: {1}",
order.Id, hasMerchandise));
return hasMerchandise;
}
|
Se o CanShip() retornar False e a ordem não tiver ainda sido definida como pedido em atraso, o método ProcessBackorder() será chamado, e a propriedade IsBackordered da ordem será definida como True. Use MessageQueueFactory para criar o item de fila e enviar a mensagem de ordem de compra. Listagem 11 mostra esse processo.
Listagem 11. O método ProcessBackorder()
private bool ProcessBackorder(OrderEntity order)
{
// set to backordered
order.IsBackordered = true;
// update order
CustomerOrderFactory factory = new CustomerOrderFactory();
bool result = factory.UpdateOrder(order);
if (!result) return result;
// get purchase order xml
string poXml = this.GetPurchaseOrderAsXml(order);
// create message queue
MessageQueueFactory queueFactory = new MessageQueueFactory();
return queueFactory.CreatePOQueueItem(poXml);
}
|
O objeto
PurchaseOrderEntity é criado através do método
GetPurchaseOrder() . Um
OrderDetailEntity é adicionado à ordem para cada item com níveis de inventário insuficientes. Listagem 12 mostra esse processo.
Listagem 12. O método GetPurchaseOrder()
private PurchaseOrderEntity GetPurchaseOrder(OrderEntity order)
{
PurchaseOrderEntity po = new PurchaseOrderEntity();
po.PurchaseDate = DateTime.Now;
// set company address of the Purchase Order - the Reseller
po.CompanyAddress.CompanyName = "The Widget Factory";
po.CompanyAddress.StreetAddress = "100 Main Street";
po.CompanyAddress.CityName = "Las Vegas";
po.CompanyAddress.StateName = "NV";
po.CompanyAddress.ZipCode = "89044";
// set vendor address of the Purchase Order - the Manufacturer
po.VendorAddress.CompanyName = "Widget Supplies";
po.VendorAddress.StreetAddress = "100 Main Street";
po.VendorAddress.CityName = "Orlando";
po.VendorAddress.StateName = "FL";
po.VendorAddress.ZipCode = "32801";
// while more items to process
IEnumerator<OrderDetailEntity> orderEnum = order.GetOrderItems();
while (orderEnum.MoveNext())
{
OrderDetailEntity orderItem = orderEnum.Current;
InventoryItemEntity inventoryItem = orderItem.Item;
// if insufficient inventory
if (orderItem.Quantity > inventoryItem.Quantity)
{
// order the number needed plus 100
int quantityToOrder = (orderItem.Quantity - inventoryItem.Quantity) + 100;
PurchaseOrderDetailEntity poItem = new PurchaseOrderDetailEntity();
poItem.ItemName = inventoryItem.Name;
poItem.ItemId = inventoryItem.Id;
poItem.Quantity = quantityToOrder;
// add item to po
po.AddItem(poItem);
}
}
return po;
}
|
A próxima etapa é serializar PurchaseOrderEntity para XML. O Microsoft .NET Framework oferece recursos de serialização de XML poderosos.
Crie uma instância de XmlSerializerNamespaces para definir o namespace XML do documento de saída. Crie uma instância de XmlWriterSettings para controlar a saída de XML. Sua intenção é omitir a declaração de XML da saída porque ela está sendo integrada ao corpo da mensagem. Use a classe XmlTextWriter para serializar o objeto para um editor de texto XML que escreve sua saída em uma instância de StringBuilder. Finalmente, use o método Serialize() deXmlSerializer
para serializar a instância dePurchaseOrderEntity para XML.
Listagem 13 mostra esse processo.
Listagem 13. O método GetPurchaseOrderAsXml()
private string GetPurchaseOrderAsXml(OrderEntity order)
{
// get purchase order
PurchaseOrderEntity po = this.GetPurchaseOrder(order); ;
StringBuilder output = new StringBuilder();
// no name space
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// settings to omit
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
XmlWriter writer = XmlTextWriter.Create(output, settings);
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrderEntity));
serializer.Serialize(writer, po, ns);
Debug.WriteLine(output.ToString());
return output.ToString();}
|
Agora, envie uma mensagem contendo a ordem de compra para a fila Amazon SQS usando o código da Listagem 14.
Listagem 14. O método CreatePOQueueItem()
public bool CreatePOQueueItem(String poXml)
{
MessageQueueSQSDataProvider queueFactory = DW4DataContext.MessageQueueData;
MessageQueueEntity message = new MessageQueueEntity();
message.MessageBody = poXml;
message.MessageType = MessageTypes.PurchaseOrder;
return queueFactory.InsertQueueItem(message);
}
|
O método InsertQueueItem() de MessageQueueSQSDataProvider cria o item de fila na fila correta com base no tipo de mensagem. O método chama o método base SendMessage() para enviar a mensagem à fila Amazon SQS, como na Listagem 15.
Listagem 15. O método InsertQueueItem()
public bool InsertQueueItem(MessageQueueEntity message)
{
String queueName = null;
if (message.MessageType == MessageTypes.OrderSummary)
{
queueName = ConfigurationSettings.AppSettings["OrderSummaryQueue"];
return this.SendMessage(queueName, message.MessageBody);
}
else if (message.MessageType == MessageTypes.PurchaseOrder)
{
queueName = ConfigurationSettings.AppSettings["PurchaseOrderQueue"];
return this.SendMessage(queueName, message.MessageBody);
}
return false;
}
|
O método SendMessage() cria SendMessageRequest, que faz parte da API Amazon.
Os parâmetros obrigatórios são o nome da fila e o corpo da mensagem (um documento XML).
Se SendMessageResponse não for nulo, isso indica que a mensagem foi enviada com êxito. As próximas linhas mostram de maneira simples as informações de depuração principais para solucionar problemas. O método SendMessage()
completo está na Listagem 16.
Listagem 16. O método SendMessage()
public bool SendMessage(string queueName, string messageBody) {
bool result = true;
try {
String accessKeyId =
ConfigurationSettings.AppSettings["AmazonAccessKeyID"]; ;
String secretAccessKey =
ConfigurationSettings.AppSettings["AmazonSecretAccessKey"];
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
// build request
SendMessageRequest request = new SendMessageRequest();
request.QueueName = queueName;
request.MessageBody = messageBody;
// send message
SendMessageResponse response = service.SendMessage(request);
if (response.IsSetSendMessageResult()) {
Debug.WriteLine("Send Message Result:");
SendMessageResult sendMessageResult = response.SendMessageResult;
if (sendMessageResult.IsSetMessageId()) {
Debug.WriteLine(String.Format("\tMessageId: {0}",
sendMessageResult.MessageId));
}
if (sendMessageResult.IsSetMD5OfMessageBody()) {
Debug.WriteLine("\tMD5 Of Message Body: ",
sendMessageResult.MD5OfMessageBody);
}
}
if (response.IsSetResponseMetadata()) {
Debug.WriteLine("Response Metadata:");
ResponseMetadata responseMetadata = response.ResponseMetadata;
if (responseMetadata.IsSetRequestId()) {
Debug.WriteLine(String.Format("\tRequest Id: {0}",
responseMetadata.RequestId));
}
}
}
catch (AmazonSQSException ex) {
Debug.WriteLine("Caught Exception: " + ex.Message);
Debug.WriteLine("Response Status Code: " + ex.StatusCode);
Debug.WriteLine("Error Code: " + ex.ErrorCode);
Debug.WriteLine("Error Type: " + ex.ErrorType);
Debug.WriteLine("Request ID: " + ex.RequestId);
Debug.WriteLine("XML: " + ex.XML);
result = false;
}
return result;
}
|
O serviço de gerenciamento de inventário do revendedor
InventoryManagementService é responsável por gerenciar o inventário e processar resumos de ordens recebidas do fabricante. Igual a
OrderFulfillmentService, o serviço usa o intervalo de pesquisa especificado no arquivo App.config. As seguintes ações ocorrem:
- O método
ProcessIncomingMerchandise()primeiro recebe uma lista de todos os resumos de ordens (OrderSummaryEntity) recebidos, mas ainda não processados. - O método
InventoryManagementServicerecupera as mensagens do método Amazon SQSOSQueue. - O método
ProcessOrderReceipt()é chamado para cada envio não processado.
Listagem 17 mostra como o método ProcessIncomingMerchandise() recupera uma lista.
Listagem 17. O método ProcessIncomingMerchandise()
public int ProcessIncomingMerchandise()
{
int itemsProcessed = 0;
OrderSummaryFactory osFactory = new OrderSummaryFactory();
IList<OrderSummaryEntity> orders = osFactory.GetOrderSummariesToProcess();
// iterate through all order summaries
IEnumerator<OrderSummaryEntity> orderEnum = orders.GetEnumerator();
while (orderEnum.MoveNext())
{
// get current order summary
OrderSummaryEntity order = orderEnum.Current;
// process order summary received
if (this.ProcessOrderReceipt(order))
{
itemsProcessed++;
}
}
Debug.WriteLine(String.Format("Orders Processed: {0}", itemsProcessed));
return itemsProcessed;
}
|
O método ProcessOrderReceipt() itera através de cada item (OrderSummaryDetailEntity) no resumo de ordens e aumenta a quantidade de inventário de cada item recebido. Listagem 18 mostra esse método.
Listagem 18. O método ProcessOrderReceipt()
private bool ProcessOrderReceipt(OrderSummaryEntity order)
{
if (order == null) return false;
bool result = true;
// add to inventory
InventoryFactory inventoryFactory = new InventoryFactory();
// iterate through all items in order summary
IEnumerator<OrderSummaryDetailEntity> itemsEnum = order.Items.GetEnumerator();
while (itemsEnum.MoveNext())
{
// get current item
OrderSummaryDetailEntity orderItem = itemsEnum.Current;
// get item
int itemId = orderItem.ItemId;
InventoryItemEntity item = inventoryFactory.GetInventoryItem(itemId);
// increase inventory
item.Quantity = item.Quantity + orderItem.Quantity;
result = inventoryFactory.UpdateInventoryItem(item);
if (!result) break;
}
if (result)
{
MessageQueueFactory queueFactory = new MessageQueueFactory();
queueFactory.DeleteQueueItem(order.QueueItem);
}
return result;
}
|
A última etapa é excluir o item da fila do resumo de ordens da fila de mensagens se o envio de inventário tiver sido processado com êxito. Listagem 19 mostra como excluir uma mensagem de uma fila do Amazon SQS usando C#.
Listagem 19. O método DeleteMessage()
public bool DeleteMessage(string queueName, string messageHandle) {
bool result = false;
try {
String accessKeyId =
ConfigurationSettings.AppSettings["AmazonAccessKeyID"];
String secretAccessKey =
ConfigurationSettings.AppSettings["AmazonSecretAccessKey"];
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
// Create delete message request object
DeleteMessageRequest request = new DeleteMessageRequest();
// Set queue name containing item to be deleted
request.QueueName = queueName;
// Set handle of message to be deleted
request.ReceiptHandle = messageHandle;
// Delete message
DeleteMessageResponse response = service.DeleteMessage(request);
Debug.WriteLine("Service Response");
Debug.WriteLine("\tDelete Message Response");
// If message response
if (response.IsSetResponseMetadata())
{
Debug.WriteLine("\tResponse Metadata");
ResponseMetadata responseMetadata = response.ResponseMetadata;
if (responseMetadata.IsSetRequestId())
{
Debug.WriteLine(String.Format("\tRequest Id: {0}",
responseMetadata.RequestId));
}
}
}
catch (AmazonSQSException ex)
{
Debug.WriteLine("Caught Exception: " + ex.Message);
Debug.WriteLine("Response Status Code: " + ex.StatusCode);
Debug.WriteLine("Error Code: " + ex.ErrorCode);
Debug.WriteLine("Error Type: " + ex.ErrorType);
Debug.WriteLine("Request ID: " + ex.RequestId);
Debug.WriteLine("XML: " + ex.XML);
}
return result;
}
|
O último componente da solução é o serviço Order Processing do fabricante, que é o serviço baseado em Java que processa ordens do revendedor. O serviço Order Processing:
- Pesquisa a fila de ordens de compra, que contém ordens de compra enviadas pelo revendedor.
- Ao receber uma ordem de compra, ele cria um resumo de ordens e o envia ao revendedor usando uma fila do Amazon SQS diferente.
- Exclui a ordem de compra recebida pelo revendedor.
O aplicativo do fabricante consiste nas seguintes entidades de negócios na Tabela 5.
Tabela 5. Entidades de negócios no aplicativo do revendedor
| Classe | Descrição |
|---|---|
AddresssEntity | Contém um cliente ou um endereço comercial |
CustomerOrderEntity | Contém uma ordem do cliente (ordem de compra) |
CustomerOrderDetailEntity | Contém o detalhe em nível de item para uma ordem do cliente (ordem de compra) |
MessageQueueEntity | Contem um item de fila de mensagens (ou seja, uma ordem de compra ou um resumo de ordens) |
OrderSummaryEntity | Contém um resumo de envios de ordens |
OrderSummaryDetailEntity | Contém o detalhe em nível de itens de um resumo de envios de ordens |
Essa solução usa a classe auxiliar JdbcQuery para todas as operações de banco de dados. A solução também usa duas classes auxiliares adicionais: DateUtil para formatação de dados e
AppConfig para ler parâmetros de configuração. Além disso, a classe SqsDataProvider é a classe do utilitário que fornece funcionalidade básica do Amazon SQS.
O serviço de processamento de ordens do fabricante
A classe OrderProcessingService é responsável por processar ordens de compra e faz isso com base no intervalo de pesquisa especificado no arquivo AppConfig.properties. O serviço recupera uma lista de ordens de compra não enviadas ainda e as processa. O método getMessageQueueItems, na Listagem 20, mostra como recuperar uma lista de mensagens de uma fila do Amazon SQS especificada com o uso de código Java.
Listagem 20. O método getMessageQueueItems()
protected ArrayList<HashMap<String, String>> getMessageQueueItems(String queueName) {
ArrayList<HashMap<String, String>> results = new ArrayList<HashMap<String, String>>();
try {
String accessKeyId = AppConfig.getProperty("AmazonAccessKeyID");
String secretAccessKey = AppConfig.getProperty("AmazonSecretAccessKey");
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
// get messages from queue
ReceiveMessageRequest request = new ReceiveMessageRequest();
request.setQueueName(queueName);
request.setVisibilityTimeout(1);
ReceiveMessageResponse response = service.receiveMessage(request);
// if received response
if (response.isSetReceiveMessageResult()) {
System.out.println("\tReceive Message Result");
ReceiveMessageResult receiveMessageResult =
response.getReceiveMessageResult();
// get all messages and iterate through them
List<Message> messageList = receiveMessageResult.getMessage();
for (Message message : messageList) {
// build hashmap containing each message
HashMap<String, String> row = new HashMap<String, String>();
if (message.isSetMessageId()) {
row.put(COLUMN_ID, message.getMessageId());
System.out.println("\t\tMessageId: " +
message.getMessageId());
}
if (message.isSetReceiptHandle()) {
row.put(COLUMN_HANDLE, message.getReceiptHandle());
}
if (message.isSetBody()) {
row.put(COLUMN_MESSAGE, message.getBody());
}
// add row
results.add(row);
}
}
} catch (AmazonSQSException ex) {
System.out.println("Caught Exception: " + ex.getMessage());
System.out.println("Response Status Code: " + ex.getStatusCode());
System.out.println("Error Code: " + ex.getErrorCode());
System.out.println("Error Type: " + ex.getErrorType());
System.out.println("Request ID: " + ex.getRequestId());
System.out.println("XML: " + ex.getXML());
}
return results;
}
|
A classe PurchaseOrderSerializer é responsável por desserializar o conteúdo XML da mensagem para uma classe Java. Use JDOM para analisar o conteúdo XML, como na Listagem 21. (À medida que você seguir esse código, é útil consultar o esquema referente à ordem de compra na Listagem 7.)
Listagem 21. A classe PurchaseOrderSerializer
public class PurchaseOrderSerializer {
public CustomerOrderEntity deserializePO(MessageQueueEntity message) {
CustomerOrderEntity order = null;
String xml = message.getMessageBody();
System.out.println("Purchase Order:\n" + xml);
try {
// Create input source from string containing XML
InputSource xmlIS = new InputSource();
xmlIS.setCharacterStream(new StringReader(xml));
// Initialize SAX Builder object
SAXBuilder builder = new SAXBuilder();
// Build JDOM Document object from input source
Document doc = builder.build(xmlIS);
Element rootElem = doc.getRootElement();
// get order id
String id = rootElem .getChildText("Id");
// create customer order
order = new CustomerOrderEntity(id);
// set order date
String dateStr = rootElem.getChildText("OrderDate");
order.setOrderDate(DateUtil.getDate(dateStr));
// get company (customer) address element node and parse it
Element addrElem = rootElem.getChild("Company");
this.setCompanyAddress(order, addrElem);
// get vendor address element node and parse it
addrElem = rootElem.getChild("Vendor");
this.setVendorAddress(order, addrElem);
// get order items list and parse it
Element itemsElem = rootElem.getChild("Items");
this.setOrderItems(order, itemsElem);
System.out.println(order.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
}
return order;
}
private void setVendorAddress(CustomerOrderEntity order, Element addrElem) {
// get data from xml
String coName = addrElem.getChildText("CompanyName");
String streetAddress = addrElem.getChildText("StreetAddress");
String city = addrElem.getChildText("City");
String state = addrElem.getChildText("State");
String zipCode = addrElem.getChildText("ZipCode");
AddressEntity coAddress = order.getVendorAddress();
// build address entity object using data read from xml
AddressEntity coAddress = order.getCompanyAddress();
coAddress.setCity(city);
coAddress.setCompanyName(coName);
coAddress.setStreetAddress(streetAddress);
coAddress.setState(state);
coAddress.setZipCode(zipCode);
}
private void setCompanyAddress(CustomerOrderEntity order, Element addrElem) {
// get data from xml
String coName = addrElem.getChildText("CompanyName");
String streetAddress = addrElem.getChildText("StreetAddress");
String city = addrElem.getChildText("City");
String state = addrElem.getChildText("State");
String zipCode = addrElem.getChildText("ZipCode");
// build address entity object using data read from xml
AddressEntity coAddress = order.getCompanyAddress();
coAddress.setCity(city);
coAddress.setCompanyName(coName);
coAddress.setStreetAddress(streetAddress);
coAddress.setState(state);
coAddress.setZipCode(zipCode);
}
private void setOrderItems(CustomerOrderEntity order, Element itemsElem) {
List itemList = itemsElem.getChildren();
if (itemList == null) return;
// iterate through all items in collection
for (int index = 0; index < itemList.size(); index++) {
// get current element
Element curElem = (Element) itemList.get(index);
// get item values
String itemId = curElem.getAttributeValue("Id");
String itemName = curElem.getAttributeValue("Name");
String quantity = curElem.getAttributeValue("Quantity");
// create order item
CustomerOrderDetailEntity detail = new CustomerOrderDetailEntity();
detail.setItemId(itemId);
detail.setItemName(itemName);
detail.setQuantity(Integer.parseInt(quantity));
// add order item to order summary
order.addItem(detail);
}
}
}
|
Em seguida, crie o resumo de ordens da ordem de compra. Listagem 22 mostra o código.
Listagem 22. O método getOrderSummaryForCustomerOrder()
public OrderSummaryEntity getOrderSummaryForCustomerOrder(
CustomerOrderEntity customerOrder) {
if (customerOrder == null) return null;
// create order
OrderSummaryEntity orderSummary = new OrderSummaryEntity();
orderSummary.setOrderDate(new Date());
orderSummary.setReferenceId(customerOrder.getId());
// set addresses
orderSummary.setCompanyAddress(customerOrder.getVendorAddress());
orderSummary.setCustomerAddress(customerOrder.getCompanyAddress());
// add items
Iterator<CustomerOrderDetailEntity> itemIter = customerOrder.getItems();
while(itemIter.hasNext()) {
CustomerOrderDetailEntity item = itemIter.next();
OrderSummaryDetailEntity detail = new OrderSummaryDetailEntity();
detail.setItemId(item.getItemId());
detail.setItemName(item.getItemName());
detail.setQuantity(item.getQuantity());
orderSummary.addItem(detail);
}
return orderSummary;
}
|
Em seguida, é preciso serializar o OrderSummaryEntity para XML. Use JDOM para serializar a mensagem, como na Listagem 23.
(É útil consultar o esquema referente ao resumo de ordens na Listagem 8.)
Listagem 23. A classe OrderSummarySerializer
public class OrderSummarySerializer {
public String serializeOrder(OrderSummaryEntity order) {
Document doc = new Document();
Element rootElem = new Element("OrderSummary");
doc.addContent(rootElem);
// add id
Element idElem = new Element("OrderId");
idElem.setText(order.getOrderId());
rootElem.addContent(idElem);
// add reference id
Element referenceIdElem = new Element("ReferenceId");
referenceIdElem.setText(order.getReferenceId());
rootElem.addContent(referenceIdElem);
// add order date
Element dateElem = new Element("OrderDate");
String dateStr = DateUtil.getDateString(new Date());
dateElem.setText(dateStr);
rootElem.addContent(dateElem);
// set company address
Element addrElem = this.getElementForAddress("CompanyAddress",
order.getCompanyAddress());
rootElem.addContent(addrElem);
// set customer address
addrElem = this.getElementForAddress("CustomerAddress",
order.getCustomerAddress());
rootElem.addContent(addrElem);
// iterate through all items and add each item to order
Element itemsElem = new Element("Items");
Iterator<OrderSummaryDetailEntity> itemIter = order.getItems();
while(itemIter.hasNext()) {
OrderSummaryDetailEntity item = itemIter.next();
Element itemElem = new Element("Item");
itemElem.setAttribute("ItemId", item.getItemId());
itemElem.setAttribute("ItemName", item.getItemName());
String quantityStr = String.valueOf(item.getQuantity());
itemElem.setAttribute("Quantity", quantityStr);
itemsElem.addContent(itemElem);
}
rootElem.addContent(itemsElem);
// convert xml document to string
XMLOutputter outp = new XMLOutputter();
StringWriter strWriter = new StringWriter();
try {
outp.output(doc, strWriter);
} catch (IOException e) {
e.printStackTrace();
}
return strWriter.toString();
}
private Element getElementForAddress(String elemName, AddressEntity address) {
Element addrElem = new Element(elemName);
String coName = address.getCompanyName();
String streetAddress = address.getStreetAddress();
String cityName = address.getCity();
String stateName= address.getState();
String zipCode = address.getZipCode();
Element coElem = new Element("CompanyName");
coElem.setText(coName);
addrElem.addContent(coElem);
Element streetElem = new Element("StreetAddress");
streetElem.setText(streetAddress);
addrElem.addContent(streetElem);
Element cityElem = new Element("City");
cityElem.setText(cityName);
addrElem.addContent(cityElem);
Element stateElem = new Element("State");
stateElem.setText(stateName);
addrElem.addContent(stateElem);
Element zipCodeElem = new Element("ZipCode");
zipCodeElem.setText(zipCode);
addrElem.addContent(zipCodeElem);
return addrElem;
}
}
|
Envie a mensagem XML à fila Amazon SQS usando Amazon Java API. Listagem 24 mostra como enviar a mensagem.
Listagem 24. O método sendMessage()
protected boolean sendMessage(String queueName, String messageBody) {
boolean result = false;
try {
String accessKeyId = AppConfig.getProperty("AmazonAccessKeyID");
String secretAccessKey = AppConfig.getProperty("AmazonSecretAccessKey");
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
SendMessageRequest request = new SendMessageRequest();
request.setQueueName(queueName);
request.setMessageBody(messageBody);
SendMessageResponse response = service.sendMessage(request);
// Send Message Response
if (response.isSetSendMessageResult()) {
System.out.println("\tSend Message Result");
SendMessageResult sendMessageResult = response.getSendMessageResult();
if (sendMessageResult.isSetMessageId()) {
System.out.println("\t\tMessageId: " +
sendMessageResult.getMessageId());
result = true;
}
}
} catch (AmazonSQSException ex) {
System.out.println("Caught Exception: " + ex.getMessage());
System.out.println("Response Status Code: " + ex.getStatusCode());
System.out.println("Error Code: " + ex.getErrorCode());
System.out.println("Error Type: " + ex.getErrorType());
System.out.println("Request ID: " + ex.getRequestId());
System.out.println("XML: " + ex.getXML());
}
return result;
}
|
Para finalizar, é preciso excluir o item da fila de ordens de compra da fila Amazon SQS. Listagem 25 mostra como excluir a mensagem.
Listagem 25. O método deleteMessage()
protected boolean deleteMessage(String queueName, String handle) {
boolean result = false;
try {
String accessKeyId = AppConfig.getProperty("AmazonAccessKeyID");
String secretAccessKey = AppConfig.getProperty("AmazonSecretAccessKey");
AmazonSQS service = new AmazonSQSClient(accessKeyId, secretAccessKey);
DeleteMessageRequest request = new DeleteMessageRequest();
request.setQueueName(queueName);
request.setReceiptHandle(handle);
DeleteMessageResponse response = service.deleteMessage(request);
// Delete Message Response
if (response.isSetResponseMetadata()) {
// Response Metadata
ResponseMetadata responseMetadata = response.getResponseMetadata();
if (responseMetadata.isSetRequestId()) {
System.out.print("RequestId: " +
responseMetadata.getRequestId());
result = true;
}
}
} catch (AmazonSQSException ex) {
System.out.println("Caught Exception: " + ex.getMessage());
System.out.println("Response Status Code: " + ex.getStatusCode());
System.out.println("Error Code: " + ex.getErrorCode());
System.out.println("Error Type: " + ex.getErrorType());
System.out.println("Request ID: " + ex.getRequestId());
System.out.println("XML: " + ex.getXML());
}
return result;
}
|
Agora, você está pronto para testar a solução. Para testar a solução, envie uma ordem referente a um item para o qual não há inventário suficiente. Em seguida, é possível rastrear todo o processo:
- Enviando a ordem
- Revendedor que coloca a ordem como pedido em atraso e cria a ordem de compra
- Manufatura recebe a ordem e envia o resumo de ordens ao revendedor
- Revendedor recebe o resumo de ordens e processa o pedido em atraso
O cliente revendedor na Figura 6. mostra os níveis de inventário antes de uma nova ordem ser feita.
Figura 6. Navegar no inventário
Figura 7 mostra a janela de envio de ordem.
Figure 7. Enviar a ordem
Figura 8 mostra a janela Browse Orders .
Figura 8. Navegando em ordens
Após a nova ordem ser feita, a janela na Figura 9 mostrará a saída dos serviços de preenchimento de ordem quando a ordem for colocada como pedido em atraso.
Figura 9. O preenchimento da ordem pelo revendedor e serviço de gerenciamento de inventário
Após a nova ordem ser feita, a janela na Figura 10 mostrará o conteúdo da fila de mensagens POQueue .
Figura 10. Visualizar a fila de mensagens
O fabricante recebe a ordem de compra da fila de mensagens e a processa. Figura 11 mostra a saída do sistema de processamento de ordem do fabricante.
Figura 11. O serviço de gerenciamento de ordem do fabricante
Este artigo mostra como utilizar XML e o serviço da Web Amazon SQS para integrar aplicativos de negócios corporativos. Combinar a extensibilidade de XML com Amazon SQS resulta em um aplicativo altamente escalável e flexível. Os benefícios principais dessa solução são a escalabilidade, a extensibilidade e o esforço de integração mínimo. A integração não precisou de nenhuma consideração especial de firewall ou de segurança. O benefício mais recente desta solução— e um motivador principal para serviços da Web—é que a solução funciona entre plataformas e é agnóstica de tecnologia. O fabricante e o revendedor podem manter suas plataformas de tecnologia existentes enquanto integram seus processos de negócios para impulsionar a eficiência operacional e o retorno sobre investimento (ROI).
Esta solução tem poucas limitações. Primeiramente, o tamanho máximo de um corpo da mensagem de Amazon SQS é de 8 KB. É possível contornar essa situação com várias soluções possíveis:
- Use Amazon Simple Storage Service (Amazon S3) para armazenar o documento XML grande e colocar um identificador no item Amazon S3 na mensagem Amazon SQS.
- Use Amazon SimpleDB para armazenar os dados estruturados e inserir um identificador nos itens da mensagem Amazon SQS.
A segunda limitação é a abordagem de consistência eventual que Amazon usa com a fila. É uma coisa boa e ruim ao mesmo tempo, mas é evidente que Amazon acredita que as coisas boas superam de longe as ruins. A limitação mais recente da solução é que não existe nenhuma prioridade ou abordagem FIFO. Novamente, isso é intencional por parte do Amazon, já que fornece uma solução mais escalável, confiável e altamente disponível.
Se você usar XML para troca de dados no corpo da mensagem, as melhores práticas recomendarão que se execute a validação XML Schema (XSD) para assegurar que o remetente e o destinatário falem a mesma língua. Assim como com qualquer projeto de integração XML, o remetente e o destinatário precisam concordar com um esquema comum.
| Descrição | Nome | Tamanho | Método de download |
|---|---|---|---|
| C# projects for the reseller applications | NETSolution.zip | 223KB | HTTP |
| Java project for the manufacturer applications | JavaSolution.zip | 24KB | HTTP |
| Contains the configuration scripts | DbScripts.zip | 1KB | HTTP |
Informações sobre métodos de download
Aprender
- Documentação técnica de Amazon SQS: consulte as informações na biblioteca Amazon SQS.
- Documentação JDOM: obtenha informações sobre a biblioteca JDOM e essa solução baseada em Java para acessar, manipular e apresentar dados XML de código Java.
- Connecting to the cloud, Part 3: Cloud governance and security: Secure the HybridCloud application (Mark O'Neill, developerWorks, junho de 2009): encontre uma boa descrição das chaves usadas com Amazon SQS e explore como configurar políticas que protegem informações sensíveis e monitoram o acesso e o uso de seus aplicativos na nuvem. Na Parte 1 desta série, observe um exemplo híbrido com infraestrutura e serviços de computação em nuvem (Mark O'Neill, developerWorks, março de 2009). Na Parte 2, desenvolva esse aplicativo híbrido (Mark O'Neill, developerWorks, abril de 2009).
- Consistência eventual: consulte a entrada no blog de Werner Vogels (Amazon CTO) sobre consistência eventual para informações sobre a lógica de design do Amazon SQS.
- Simplifique a programação XML com JDOM (Wes Biggs e Harry Evans, developerWorks, maio de 2001): JDOM é similar a DOM, mas fornece uma Java API simples para analisar, manipular e apresentar XML. Saber como usar essa API de software livre torna a manipulação de documentos XML mais fácil para desenvolvedores Java.
- Certificação XML da IBM: Descubra como se tornar um Desenvolvedor Certificado pela IBM em XML e tecnologias relacionadas.
- Biblioteca técnica de XML: Consulte a zona de XML para obter uma ampla gama de artigos técnicos e dicas, tutoriais, padrões e Redbooks da IBM.
- eventos técnicos e webcasts do developerWorks
: Mantenha-se atualizado em relação à tecnologia nessas sessões.
- Podcasts do developerWorks
: Ouça entrevistas e discussões interessantes para desenvolvedores de software.
Obter produtos e tecnologias
- JDOM: faça o download da biblioteca JDOM Java API.
Biblioteca C# para Amazon SQS: faça o download da biblioteca AmazonC# API para Amazon SQS.- Biblioteca Java para Amazon SQS: faça o download da biblioteca Amazon Java API para Amazon SQS.
- Servidor MySQL versão 5.1: faça o download da plataforma de banco de dados de software livre popular.
- MySQL Connector/Net versão 5.2: faça o download do driver de banco de dados Microsoft .NET.
- MySQL Connector/J versão 5.1: faça o download do driver Java Database Connectivity (JDBC).
- versões de avaliação de produto IBM: Faça o download ou explore as versões de teste on-line no IBM SOA Sandbox e entre em contato com as ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2®, Lotus®, Rational®, Tivoli®e WebSphere®.
Discutir
- Fóruns de discussão da zona de XML: Participe de qualquer uma das várias discussões relacionadas à XML.
- Blogs do developerWorks: confira estes blogs e participe da comunidade do developerWorks.

Brian J. Stewart é atualmente o consultor principal da Aqua Data Technologies, uma empresa fundada com foco em gerenciamento de conteúdo, tecnologias XML e sistemas Web e cliente-servidor corporativos. Ele arquiteta e desenvolve soluções corporativas com base nas plataformas Java EE e Microsoft .NET. Brian mantém um blog em BrianJStewart.com.