Projeto: Produção de Cachaça

cachaca-03_1

Para este projeto você irá implementar um sistema de controle de transações para produtores de cachaça que utilizam uma mistura de quatro diferentes variedades de cana de açúcar (Caiana, Rainha, Caninha e Prata)  para produzir uma bebida de sabor único.

O seu programa deverá gerenciar um único fornecedor de cana de açúcar, um número variável de alambiques e quatro atravessadores responsáveis por armazenar e entregar as diferentes variedades de cana de açúcar para os alambiques.

Cada atravessador mantém um estoque com quantidades variáveis das quatro diferentes variedades de cana de açúcar mas é especialista em apenas uma delas. De tempos em tempos o fornecedor entrega um suprimento de cana de açúcar de uma determinada variedade apenas para o atravessador especialista nesta variedade.

Por exemplo, o fornecedor pode entregar 30 fardos de cana caiana para o atravessador especialista em cana caiana e mais tarde entregar 20 fardos de cana rainha para o atravessador especialista nesta variedade.

Os alambiques periódicamente enviam pedidos de compra para os atravessadores. Cada pedido de compra especifica diferentes quantidades de cada uma das variedades de cana de açúcar.  Um pedido pode ser enviado para qualquer um dos quatro atravessadores e neste projeto nós vamos escolher o atravessador a receber o pedido de forma aleatória. Um atravessador tentará atender o pedido de um alambique utilizando o seu próprio estoque.

Se o especialista em uma determinada variedade de cana de açúcar C não consegue atender o pedido por não ter quantidade suficiente da variedade de cana C ele apenas espera até que o fornecedor entregue um novo carregamento dessa variedade de cana. Por outro lado. se ele estiver com estoque baixo de uma outra variedade de cana C’ , tentará obter uma maior quantidade de C’ negociando o atravessador especialista nessa variedade. Em ambos os casos, o atravessador não responde ao alambique até que possa atender o pedido por completo.

Para simplificar o projeto vamos assumir que todas as variedades de cana de açúcar possuem o mesmo valor, ou seja, três fardos de cana caiana podem ser trocados por três fardos de qualquer um das outras variedades disponíveis.

Suponha que um alambique tenha enviado um pedido ao atravessador especialista em cana caiana requisitando 4 fardos de cana caiana e 1 fardo de cana prata.

  • Se este atravessador possuir quantidades suficientes de cana caiana e cana prata para atender ao pedido ele fornecerá cana para o alambique direto do seu próprio estoque
  • Se ele tiver menos do que 4 fardos de cana caiana em seu estoque ele aguardará até que o fornecedor entregue mais cana caiana para poder atender ao pedido
  • Se ele tiver cana caiana suficiente mas não tiver cana prata ele contactará o atravessador especialista em cana prata e proporá uma troca de 1 fardo de cana caiana por 1 fardo de cana prata.

Um atravessador trata um pedido de um outro atravessador da mesma forma que trata um pedido de um alambique. Se ele pode atender imediamente, atenderá. Se não puder, uma vez que ele o pedido precisa ser relacionado apenas a variedade de cana de sua especialidade, ele aguardará por uma nova entrega de cana do fornecedor.

Implementando o sistema em Java

Você deve utilizar cincos classes Java já implementadas  (Projeto2, Fornecedor, Alambique, Pedido e GetOpts), uma enumeration (Cana) e uma interface (Atravessador).

O código fonte está disponível em um dos meus repositórios no GitHub. A forma mais simples de fazer download do código é fazendo um fork do repositório GitHub e sugiro fortemente que você o faça e que utilize o GitHub ao longo do seu projeto.

As classes Fornecedor e Alambique representam threads (implementam a interface Runnable). O seu trabalho neste projeto será fornecer uma implementação da interface Atravessador chamada AtravessadorImpl que funciona como um monitor, o que significa que o Atravessador não aje por si só, atuando apenas em resposta a chamadas do Fornecedor e dos Alambiques.

A classe GetOpt é uma implementação Java do comando GNU getopt e serve para manipular os argumentos da linha de comando.

A classe  Projeto2 é o programa principal. Ela cria um Fornecedor e um número variável (especificado na linha de comando) de Alambiques e inicia os threads que controlam suas execuções.

De tempos em tempos o thread do Fornecedor faz uma entrega de alguma variedade de cana de açúcar para o especialista dessa variedade invocando o seguinte método da classe Atravessador:

public void entrega(int quant);

De tempos em tempos um Alambique faz um pedido a um Atravessador invocando o método

public void get(Pedido pedido);

O pedido contem uma quantidade não-negativa de cada uma das variedades de cana, indicando quanto de cada uma dessas variedades está sendo solicitado. Por exemplo, para solicitar três fardos de cana caiana e um fardo de cana rainha ao Atravessador especialista em cana rainha um Alambique deve utilizar um código semelhante ao seguinte:

Pedido pedido = new Pedido();
pedido.set(Cana.CAIANA, 3);
pedido.set(Cana.RAINHA, 1);
Projeto2.especialista(Cana.RAINHA).get(pedido);

Como você poderá observar, a classe Projeto2 oferece uma série de métodos estáticos utilitários.

Um Atravessador que não possui quantidade suficiente de uma determinada variedade cana de açúcar para atender a um pedido pode tentar obter mais dessa variedade de cana do Atravessador especialista nessa variedade utilizando o método

public void troca(Cana variedade, int quant);

Este método indica que o Atravessador chamando o método deseja trocar quant fardos  de uma determinada variedade de cana por quant fardos da cana que é de especialidade do Atravessador invocado. Por exemplo, a chamada

Projeto2.especialista(Cana.CAIANA).troca(PRATA, 2);

indica que o chamador deseja trocar 2 fardos de cana prata por 2 fardos de cana caiana. Depois da chamada (chamadas a get e troca nunca falham, mas podem bloquear o chamador por um certo período de tempo), o chamador subtrai 2 de seu estoque de cana prata e adiciona 2 ao seu estoque de cana caiana.

Quando um Atravessador não pode satisfazer uma chamada de get ou de troca porque ele não possui uma quantidade suficiente de cana da sua variedade de especialidade ele bloqueia o chamador (utlizando o método wait) até que possa atender a solicitação. Um Atravessador bloqueado aguardando por algo deve continuar a aceitar solicitações de entrega do Fornecedor e pedidos dos Alambiques e de outros Atravessadores.

Sua solução deve garantir que não ocorrerão deadlocks mas não precisa ser justa. Em particular, você não precisa se preocupar em evitar starvation.

Dicas

Lembre-se da regra cardinal da programação concorrente:

Qualquer variável que pode ser acessada por mais de um processo e modificada por qualquer um deles só deve ser acessada dentro de uma sessão crítica.

No contexto deste projeto, isso significa que os atributos da classe AtravessadorImpl só devem ser alterados no corpo de métodos sincronizados. Por outro lado, existe uma outra regra que deve ser seguida para ajudar a evitar deadlocks:

Um método sincronizado nunca deve invocar um método sincronizado em um outro objeto.

Essa regra possui exceções mas elas são bem raras. No contexto deste projeto, isso significa que o método AtravessadorImpl.get não pode ser sincronizado pois ele pode precisar invocar o método AtravessadorImpl.troca em um outro Atravessador. Ao invés disso, defina seus próprios métodos privados sincronizados e os utilize para acessar e/ou atualizar os atributos da classe AtravessadorImpl.

O que deve ser entregue?

Você deve me enviar por e-mail o(s) arquivo(s) .java da sua implementação da classe AtravessadorImpl.

Critérios de avaliação

A corretude da implementação valerá 80% da nota e o estilo de codificação valerá 20%.