sábado, 28 de março de 2009

Ajax-enabled Web Services with ScriptManager

Clique aqui para ver este post em Português

In this post I'll show an incredible ASP.Net feature I've learned recently: Ajax-enabled Web Services.

With the ScriptManager component (that we've already seen in this post) we're able to consume a Web Service in our Web Site and use the results in JavaScript with little to none specific programming effort. With this component, the Web Service looks like it's a client script object.


Let's start with a new Web Site in Visual Studio (the Visual Web Developer express edition works fine, too). Choose a folder to save the Web Site (you don't need the IIS installed in order to test de project). Now we'll create the Web Service.

Right-click the Web Site's root directory and choose Add New Item. In the dialog box select Web Service. Name it AjaxWs.asmx and click Ok. The IDE should open the file App_Code/AjaxWs.cs. This is where we're gonna work.

Let's leave its implementation as it is for now. It should come with a sample method HelloWorld. The only thing we'll do is "enable" the client script calls. To do that find the class definition:

public class AjaxWs : System.Web.Services.WebService


In the line above there's a commented class decoration that we only need to remove the comment bars to enable this feature:

// [System.Web.Script.Services.ScriptService]


So, remove the comment and save the file and click the menu Build > Build Web Site.

The Web Service part is done (for now). Now we're gonna code the Web Form that calls the Web Service. Open the Default.aspx file and go to the visualization "Source" or "Split". Below the form tag

<form id="form1" runat="server">


We're gonna add a ScriptManager component. You can drag it from the toolbox or simply type manually the asp:ScriptManager tag. The VS makes the effort of type the tags manually very smooth, even if it may not seem that way. After done the tag looks like this:

<asp:ScriptManager runat="server" ID="ScriptMngr">
<Services>
<asp:ServiceReference Path="~/AjaxWs.asmx" />
</Services>
</asp:ScriptManager>


We'll now create a client script block in our Web Form. Find the head section and add a script block inside. Here's the code:

<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript">
function executarWs() {
AjaxWs.HelloWorld(
function(resultado) {
alert(resultado)
}
)
}
</script>
</head>


I'll give a brief explanation about this script:

AjaxWs - Our Web Service - was created by VS when we added the ScriptManager component and referenced the ~/AjaxWs.asmx Web Service. We're coding a method call to Hello World and pass a function as an argument - more precisely a callback. A callback is a function definition that is called when the function that took is as an argument needs. Think of it as some sort of event handler. But it's subject for another post.

Now we need to call this method. We've seen "new feature" part. Now we're gonna call our Web Service's methods as an ordinary JavaScript call. We'll create a HTML button to call the JavaScript function:

<button onclick="executarWs()">Clique aqui</button>


Here goes the complete WebForm's code:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript">
function executarWs() {
AjaxWs.HelloWorld(
function(resultado) {
alert(resultado)
}
)
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptMngr">
<Services>
<asp:ServiceReference Path="~/AjaxWs.asmx" />
</Services>
</asp:ScriptManager>
<div>
<button onclick="executarWs()">Clique aqui</button>
</div>
</form>
</body>
</html>


Now run the Web Site (F5) to see what we've done in action. When we click the button the message "Hello World" will pop up. This alone is very nice already; but, what if we could return objects with properties instead of pure strings and ints? Well, we can!

First, let's create a class in our Web Site. Right click in the App_Code folder below the root folder and choose Add New Item. In the dialog box choose Class. Name it Pessoa.cs and click Ok. Here's the complete code:

public class Pessoa {

public string Nome { get; set; }
public string Sobrenome { get; set; }

}


Now let's get back to our Web Service and create a new method that take two parameters and returns an instance of Pessoa. Open the AjaxWs.cs again and add the following code under the HelloWorld method:

[WebMethod]
public Pessoa ObterPessoa(string nome, string sobrenome) {
return new Pessoa { Nome = nome, Sobrenome = sobrenome };
}


Now get back to Default.aspx and modify the HelloWorld method call to ObetrPessoa, giving the nome and sobrenome arguments before the callback argument:

function executarWs() {
AjaxWs.ObterPessoa(
"Estação",
"ZN",
function(resultado) {
alert(resultado.Nome + " " + resultado.Sobrenome)
}
)
}


Now run the Web Site again and see the result. Awesome! We had nearly zero effort and the possibilities are many with this technology. Thank you and take care.

Artigo completo (View Full Post)

Web Services em Ajax com ScriptManager

Click here to see this post in English

Neste post eu mostro uma facilidade incrível do ASP.Net que eu aprendi: Acessar Web Services via Ajax.

Com o componente ScriptManager - é o mesmo daquele post sobre ASP.Net e Ajax - podemos incluir referência para um Web Service criado no nosso WebSite, executá-lo e recuperar o resultado com um esforço mínimo de código. Com este componente parece que o Web Service é um objeto do próprio Script cliente da nossa página.


Bem, vamos começar criando projeto WebSite no Visual Studio (pode ser o Visual Web Developer Express Edition também). Escolham uma pasta para abrigar o WebSite (lembrando que para testar o VS inicia um servidor Web virtual com o nosso site) e pronto. Agora vamos criar nosso Web Service.

Clique com o botão direito na raiz do WebSite e selecione Add New Item. Na janela de New Item escolha Web Service. Chame de AjaxWs.asmx e clique Ok. A IDE vai abrir o arquivo App_Code/AjaxWs.cs. É nele que vamos trabalhar

Vamos Deixar a implementação dele como está por enquanto. Ele já vem com um método de exemplo HelloWorld. A única coisa que vamos fazer é "habilitar" que ele seja chamado por Script cliente. Para isso procure a definição da classe:

public class AjaxWs : System.Web.Services.WebService


Na linha acima desta há uma decoração comentada que nós só precisamos descomentar para adicionar esta funcionalidade:

// [System.Web.Script.Services.ScriptService]


Portanto descomente esta linha e salve o arquivo e chame o menu Build > Build Web Site.

A parte do Web Service está pronte (por enquanto). Agora vamos trabalhar no nosso WebForm para chamar este Web Service. Abra o arquivo Default.aspx e vá para a visualização "Source" ou "Split". Abaixo da tag do form

<form id="form1" runat="server">


vamos colocar um componente ScriptManager. Você pode arrastar da toolbox ou simplesmente digitar a tag. O VS deixa o tabalhar de digitar os componentes muito rápido e produtivo, apesar de parecer o contrário. Enfim, a tag fica assim depois de pronta:

<asp:ScriptManager runat="server" ID="ScriptMngr">
<Services>
<asp:ServiceReference Path="~/AjaxWs.asmx" />
</Services>
</asp:ScriptManager>


Com ele aí vamos criar um bloco de script na nossa página. Procure a seção <header> do WebForm e crie um bloco de script dentro. Abaixo está o código:

<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript">
function executarWs() {
AjaxWs.HelloWorld(
function(resultado) {
alert(resultado)
}
)
}
</script>
</head>


Vou dar uma breve explicação sobre este script:

AjaxWs - que é o nome do nosso Web Service - foi definido automaticamente pelo VS quando nós colocamos o ScriptManager no WebForm. Estamos chamando o método HelloWorld do nosso Web Service e passando como argumento uma função - mais precisamente um callback. Um callback é uma definição de função que será chamado pela função que nós invocamos quando necessário. É um conceito semelhante ao de manipuladores de eventos. Em outro post eu me dedico somente a JavaScript.

Agora precisamos executar esta função de alguma maneira. A novidade da tecnologia já passou; agora podemos fazer como uma chamada normal de alguma função JavaScript. Para este exemplo vou criar um botão HTML no WebForm:

<button onclick="executarWs()">Clique aqui</button>


Abaixo segue o WebForm completo:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript">
function executarWs() {
AjaxWs.HelloWorld(
function(resultado) {
alert(resultado)
}
)
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptMngr">
<Services>
<asp:ServiceReference Path="~/AjaxWs.asmx" />
</Services>
</asp:ScriptManager>
<div>
<button onclick="executarWs()">Clique aqui</button>
</div>
</form>
</body>
</html>


Agora execute o programa (F5) para ver o que nós fizemos em ação. Quando clicamos no botão a mensagem "Hello World" aparece. Isso por si só já é muito legal, mas imagine se, em vez de retornar somente uma string, pudéssemos retornar objetos com propriedades? Mas só pode!

Primeiro vamos criar uma classe no Web Site. Clique com o botão direito na pasta App_Code dentro da raiz do Web Site e clique Add New Item. Na janela selecione Class. chame o arquivo de Pessoa.cs e clique Ok. abaixo segue a classe pronta:

public class Pessoa {

public string Nome { get; set; }
public string Sobrenome { get; set; }

}


Agora vamos voltar no nosso Web Service e criar um novo método que pegue dois parâmetros e retorne uma instância de PEssoa. Abra o AjaxWs.cs outra vez e adicione o seguinte método abaixo do HelloWorld:

[WebMethod]
public Pessoa ObterPessoa(string nome, string sobrenome) {
return new Pessoa { Nome = nome, Sobrenome = sobrenome };
}


Agora volte no Default.aspx e modifique a chamada do método HelloWorld para ObterPessoa, passando agora os argumentos nome e sobrenome antes da função de callback:

function executarWs() {
AjaxWs.ObterPessoa(
"Estação",
"ZN",
function(resultado) {
alert(resultado.Nome + " " + resultado.Sobrenome)
}
)
}


Agora execute novamente e veja o resultado. Demais! Nosso esforço foi praticamente zero o as possibilidades são muitas com esta tecnologia. Um grande abraço a todos e até a próxima!

Artigo completo (View Full Post)

Ajax in a Simple way with ASP.NET

Clique aqui para ver este post em Português

Hello. In this post I'll show a very nice way to enable Ajax in our ASP.NET WebForms I learned.

When I (and most people) learned Ajax, the solution was to use a javascript object (XmlHttpRequest), create a separate script to process the request and write code to display the results that come in XML or plain text. I must confess that I always liked to work with Ajax, even with all that code effort xD

Another interesting point is the debugging and the error tracking.


In VS 2008 there are a couple of simple, but very useful components that allows us to work with Ajax in an easy and transparent way: ScriptManager and UpdatePanel.

To demonstrate the use of these components I'll create an ASP.NET WebForm that searches an object collection, and next add these componentes to enable Ajax. So, Let's start with an ASP.NET WebSite and edit the Default.aspx. Here goes the Markup source code:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ajax com ASP.NET</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<p>
Busca: <asp:TextBox runat="server" ID="BuscaTxt" Width="200" /> 
<asp:Button runat="server" ID="BuscaBtn" Text="Buscar"
onclick="BuscaBtn_Click" />
</p>

<div>
<asp:GridView runat="server"
ID="PessoasGridView"
AutoGenerateColumns="false"
CellPadding="4"
CellSpacing="0">
<Columns>
<asp:BoundField DataField="Nome"
HeaderText="Nome"
HeaderStyle-Width="200" />
<asp:BoundField DataField="Sobrenome"
HeaderText="Sobrenome"
HeaderStyle-Width="200" />
</Columns>
</asp:GridView>
</div>
</div>
</form>
</body>
</html>


Now the source code of Default.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;

public partial class _Default : System.Web.UI.Page {

class Pessoa {
public string Nome { get; set; }
public string Sobrenome { get; set; }
}

private List<Pessoa> Pessoas = new List<Pessoa>{
new Pessoa { Nome = "Felipe", Sobrenome = "Guerço" },
new Pessoa { Nome = "Gerson", Sobrenome = "Motta" },
new Pessoa { Nome = "Joaquim", Sobrenome = "José" },
new Pessoa { Nome = "João", Sobrenome = "Silva" },
new Pessoa { Nome = "Aristarco", Sobrenome = "Pederneiras" },
};

protected void Consultar() {
PessoasGridView.DataSource =
from p in Pessoas
where p.Nome.Contains(BuscaTxt.Text)
orderby p.Nome
select p;
PessoasGridView.DataBind();
}

protected void Page_Load(object sender, EventArgs e) {

}
protected void BuscaBtn_Click(object sender, EventArgs e) {
Consultar();
}
}


With that we can query the private member Pessoas according to the filter criteria in the textbox. But this way each request will produce a full round trip just to update the grid contents. Let's see how to avoid this with the Ajax components.

We'll add the ScriptManager component in our WebForm in order to enable Ajax. Next we'll add the UpdatePanel and put the grid "inside" it.

The UpdatePanel is a very interesting component. It represents a "piece" of the WebForm that is updated when certain events are fired up. But which events? Here comes the interesting part: We configure which component's events we want to "trigger" the update panel. In other words: When that action fires up the only part updated is the content inside the UpdatePanel. Let's see the markup here:

<asp:UpdatePanel runat="server" ID="GridUpdatePanel">
<Triggers>
<asp:AsyncPostBackTrigger
ControlID="BuscaBtn" EventName="Click" />
</Triggers>
<ContentTemplate>
<asp:GridView runat="server"
ID="PessoasGridView"
AutoGenerateColumns="false"
CellPadding="4"
CellSpacing="0">
<Columns>
<asp:BoundField DataField="Nome"
HeaderText="Nome"
HeaderStyle-Width="200" />
<asp:BoundField DataField="Sobrenome"
HeaderText="Sobrenome"
HeaderStyle-Width="200" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>


Now the entire WebForm markup:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ajax com ASP.NET</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="AjaxScriptManager" />
<div>
<p>
Busca: <asp:TextBox runat="server" ID="BuscaTxt" Width="200" /> 
<asp:Button runat="server" ID="BuscaBtn" Text="Buscar"
onclick="BuscaBtn_Click" />
</p>

<div>
<asp:UpdatePanel runat="server" ID="GridUpdatePanel">
<Triggers>
<asp:AsyncPostBackTrigger
ControlID="BuscaBtn" EventName="Click" />
</Triggers>
<ContentTemplate>
<asp:GridView runat="server"
ID="PessoasGridView"
AutoGenerateColumns="false"
CellPadding="4"
CellSpacing="0">
<Columns>
<asp:BoundField DataField="Nome"
HeaderText="Nome"
HeaderStyle-Width="200" />
<asp:BoundField DataField="Sobrenome"
HeaderText="Sobrenome"
HeaderStyle-Width="200" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</div>
</form>
</body>
</html>


Try to run the WebSite and notice that the page doesn't refresh anymore. And the best part is that we didn't need to write any javascript code and create any "support" files.

Thank you for the support and until next time.

Artigo completo (View Full Post)

Ajax de um jeito simples em ASP.NET

Click here to see this post in English

Olá a todos. Neste post vou mostrar uma maneira de implementar Ajax no ASP.NET muito legal que eu aprendi.

Quando eu (e a grande maioria) aprendi Ajax a maneira de se fazer era usar um objeto javascript (XmlHttpRequest), criar uma página separada para processar o request e montar o resultado em XML ou texto plano, e tratar este resultado no nosso script principal. Eu confesso que eu sempre achei muito legal programar com Ajax, apesar do trabalhão que dá xD.


Outra parte interessante é o trabalho que dá para depurar e corrigir errors.

No VS 2008 eu vi uns componentes simples, mas que nos permitem trabalhar com Ajax de maneira muito simples e transparente: ScriptManager e UpdatePanel.

Para mostrar o uso destes componentes vou criar uma pequena página ASP.NET que vai fazer uma consulta numa coleção de strings e depois adicionar funcionalidade Ajax a esta página. Para isso vou partir de um WebSite ASP.NET e editar a Default.aspx. Abaixo segue o código-fonte HTML:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ajax com ASP.NET</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<p>
Busca: <asp:TextBox runat="server" ID="BuscaTxt" Width="200" /> 
<asp:Button runat="server" ID="BuscaBtn" Text="Buscar"
onclick="BuscaBtn_Click" />
</p>

<div>
<asp:GridView runat="server"
ID="PessoasGridView"
AutoGenerateColumns="false"
CellPadding="4"
CellSpacing="0">
<Columns>
<asp:BoundField DataField="Nome"
HeaderText="Nome"
HeaderStyle-Width="200" />
<asp:BoundField DataField="Sobrenome"
HeaderText="Sobrenome"
HeaderStyle-Width="200" />
</Columns>
</asp:GridView>
</div>
</div>
</form>
</body>
</html>


Agora o código Default.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;

public partial class _Default : System.Web.UI.Page {

class Pessoa {
public string Nome { get; set; }
public string Sobrenome { get; set; }
}

private List<Pessoa> Pessoas = new List<Pessoa>{
new Pessoa { Nome = "Felipe", Sobrenome = "Guerço" },
new Pessoa { Nome = "Gerson", Sobrenome = "Motta" },
new Pessoa { Nome = "Joaquim", Sobrenome = "José" },
new Pessoa { Nome = "João", Sobrenome = "Silva" },
new Pessoa { Nome = "Aristarco", Sobrenome = "Pederneiras" },
};

protected void Consultar() {
PessoasGridView.DataSource =
from p in Pessoas
where p.Nome.Contains(BuscaTxt.Text)
orderby p.Nome
select p;
PessoasGridView.DataBind();
}

protected void Page_Load(object sender, EventArgs e) {

}
protected void BuscaBtn_Click(object sender, EventArgs e) {
Consultar();
}
}


Com isso nosso programa vai consultar a lista no membro privado Pessoas de acordo com o filtro. Mas dessa forma a página está fazendo uma viagem completa ao servidor só para atualizar o grid. Vamos ver como evitar isso com os componentes de Ajax do ASP.NET.

Na página vamos colocar o componente ScriptManager para habilitar o uso do componente que vai realizar o Ajax. Em seguida vamos colocar na página o componente UpdatePanel e colocar o grid dentro dele.

O componente UpdatePanel é muito interessante: Ele representa um "pedaço" do WebForm que vai ser atualizado quando determinada ação acontecer. Mas que ação? Agora vem a parte interessante: No UpdatePanel nós configuramos quais eventos de quais objetos vão disparar o refresh daquela área. Vou colocar só o trecho do grid aqui:

<asp:UpdatePanel runat="server" ID="GridUpdatePanel">
<Triggers>
<asp:AsyncPostBackTrigger
ControlID="BuscaBtn" EventName="Click" />
</Triggers>
<ContentTemplate>
<asp:GridView runat="server"
ID="PessoasGridView"
AutoGenerateColumns="false"
CellPadding="4"
CellSpacing="0">
<Columns>
<asp:BoundField DataField="Nome"
HeaderText="Nome"
HeaderStyle-Width="200" />
<asp:BoundField DataField="Sobrenome"
HeaderText="Sobrenome"
HeaderStyle-Width="200" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>


Segue abaixo a página modificada:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ajax com ASP.NET</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="AjaxScriptManager" />
<div>
<p>
Busca: <asp:TextBox runat="server" ID="BuscaTxt" Width="200" /> 
<asp:Button runat="server" ID="BuscaBtn" Text="Buscar"
onclick="BuscaBtn_Click" />
</p>

<div>
<asp:UpdatePanel runat="server" ID="GridUpdatePanel">
<Triggers>
<asp:AsyncPostBackTrigger
ControlID="BuscaBtn" EventName="Click" />
</Triggers>
<ContentTemplate>
<asp:GridView runat="server"
ID="PessoasGridView"
AutoGenerateColumns="false"
CellPadding="4"
CellSpacing="0">
<Columns>
<asp:BoundField DataField="Nome"
HeaderText="Nome"
HeaderStyle-Width="200" />
<asp:BoundField DataField="Sobrenome"
HeaderText="Sobrenome"
HeaderStyle-Width="200" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</div>
</form>
</body>
</html>


E o mais legal é que nós criamos zero de código javascript para habilitar Ajax no nosso WebForm.

Um grande abraço a todos e até a próxima.

Artigo completo (View Full Post)

quarta-feira, 11 de março de 2009

Rapidinha


Essa eu tenho que postar!


"Quanto mais desesperadora é a situação, mais otimista é o relatório de progresso"


daqui


Qualquer relação com a minha realidade é ... ah, vocês sabem.


[]s, Rodrigo.

Artigo completo (View Full Post)

quarta-feira, 25 de fevereiro de 2009

Delphi Intraweb - Gerar dinamicamente JavaScript

Continuação do artigo Delphi Intraweb – JavaScript II (Continua)

Antes de qualquer coisa, preciso registrar a alegria de podermos contar com mais um colaborador no nosso blog.
Prezados leitores, em nome de todos os colaboradores, quero manifestar a alegria em poder contar com mais um grande amigo no time do Estação ZN. Rodrigo Alencar, brother de longas datas tem contribuído com um material de extrema qualidade, pertinência e originalidade.
Rodrigão, seja bem vindo ao Estação ZN, é uma honra poder contar com o sua participação!

Dando continuidade ao tema ...

Para controlarmos, através de JavaScript, a ação de habilitar e desabilitar o IWCombo1 (O que carregamos com os nomes das bandas) vamos gerar dinamicamente o script que vai no browser fazer esse gerenciamento.
Adicione no IWForm mais um IWCheckBox, e codifique conforme ilustrado abaixo:


private
procedure LoadComboBandas;
procedure DesabilitaControles;
procedure BuildScriptControlEnabled;
public

Na seção “private” declarei o método que vai construir dinamicamente o JavaScript, “BuildScriptControlEnabled”.

procedure TZnIWForm.BuildScriptControlEnabled;
const
FunctionJsBodyStatment = 'function %s{%s}';
AQuebraLinhaJs = #13;
AStatementFnc =
' var ZnComboLnd = FindElem("%s");' + AQuebraLinhaJs +
' alert(ZnComboLnd[ZnComboLnd.selectedIndex].innerHTML);' + AQuebraLinhaJs +
' var ZnChkBox = FindElem("%s");' + AQuebraLinhaJs +
' alert(ZnChkBox.innerHTML);' + AQuebraLinhaJs +
' ZnChkBox.checked = !ZnChkBox.checked;' + AQuebraLinhaJs +
' ZnComboLnd.disabled = !ZnChkBox.checked;' + AQuebraLinhaJs +
' if (!ZnComboLnd.disabled){ ' + AQuebraLinhaJs +
' alert(ZnComboLnd.name + " está Habilitado"); ' + AQuebraLinhaJs +
' ZnComboLnd.style.backgroundColor = "#FFFFFF"; ' + AQuebraLinhaJs +
' ZnComboLnd.focus(); ' + AQuebraLinhaJs +
' return true; ' + AQuebraLinhaJs +
' } ' + AQuebraLinhaJs +
' else{ '+ AQuebraLinhaJs +
' ZnComboLnd.style.backgroundColor ="#E7E7E7"; '+ AQuebraLinhaJs +
' return false; ' + AQuebraLinhaJs +
' };'+ AQuebraLinhaJs;
var
StrStatement: String;
begin
StrStatement := AQuebraLinhaJs;
StrStatement := StrStatement + Format(AStatementFnc,
[UpperCase(IWComboBox1.Name), UpperCase(ChckBoxBandas.Name)]);

StrStatement := Format(FunctionJsBodyStatment,
['ZnHabilitaCmbBanda()', StrStatement]);
Self.JavaScript.Add(StrStatement);
ChckBoxBandas.ScriptEvents.Add('onClick').EventCode.Add('ZnHabilitaCmbBanda()');
end;


Observe que temos problemas na hora de trabalharmos com o objeto do tipo “CheckBox”. Veja que, para montar a string que será adicionada a propriedade “JavaScript” do IWForm, na linha 8, recuperamos o objeto com função “FindElem()”.Do contrário, teríamos que usar o nome completo do componente (definido em JavaScript na página web). Ou seja, teríamos que concatenar com o nome do controle (IWCheckBox) no Delphi como bendito sufixo “_CHECKBOX”.
Na linha 9, somos obrigados a simular o comportamento “Click” do objeto. Que absurdo! Do contrário ele não efetiva o “Click”. Pelo menos, essa foi a solução que encontrei, visto que o click do componente não rolava.
Na linha 10, somente então, efetivamos a atribuição de valor a propriedade “disabled” do objeto recuperado na linha 7 (o “IWComboBox1”).

No trecho de código abaixo, podemos ver como ficou no browser a função que dinamicamente montamos no Delphi:

function ZnHabilitaCmbBanda(){
var ZnCombo = document.forms[0].IWCOMBOBOX1;
var ZnChkBox = FindElem("CHCKBOXBANDAS");
ZnChkBox.checked = !ZnChkBox.checked;
ZnCombo.disabled = !ZnChkBox.checked;
if (!ZnCombo.disabled){
alert(ZnCombo.name + " está Habilitado");
ZnCombo.style.backgroundColor = "#FFFFFF";
ZnCombo.focus();
return true;
};
else{
ZnCombo.style.backgroundColor ="#E7E7E7";
};
}


Isso acontece especialmente no Intraweb 8 porque ele trata o comportamento de todo “CheckBox” com uma tag “span”. Como se não bastasse isso, para complicar ainda mais, ele agrega, arbitráriamente, um sufixo ao nome original do componente, “_CHECKBOX”. Isso certamente deve ter complicado a vida de quem tentou trabalhar com JavaScript no Intraweb 8. Veja um trecho de código gerado no browser pelo Intraweb 8 na definição do “CHCKBOXBANDAS”:


<input tabindex="27" type="CHECKBOX" name="CHCKBOXBANDAS_CHECKBOX">
<span onclick="FindElem('CHCKBOXBANDAS_CHECKBOX').checked =
!FindElem('CHCKBOXBANDAS_CHECKBOX').checked;">Habilitar Bandas</span>
</span>


No Intraweb 5 isso não acontece. Primeiro, não existe o sufixo na definição JavaScript do componente. Segundo, embora exista a tag span, ela presta somente para exibir o conteúdo do caption do IWCheckBox, o comportamento do CheckBox não está programado nela. Portanto, não precisaríamos simular o click em código conforme eu fiz na linha 04 da “function ZnHabilitaCmbBanda()”. Alias, se alguém tiver algo a acrescentar a minha análise, por favor, fique a vontade. Em outras, palavras, foi a solução que encontrei.

Outro ponto importante, quero destacar nesse exemplo a atribuição dinâmica da propriedade “ScriptEvent” do TIWCheckBox, “ChkBoxBandas”. Isso está sendo feito na linha 28 do procedimento “BuildScriptControlEnabled”.

Artigo completo (View Full Post)

segunda-feira, 9 de fevereiro de 2009

Sistema de tarifação, HSQLDB e Java - PARTE 2.

Continuando o post anterior...

O problema é interceptar determinados eventos em um servidor de download para tarifação em tempo real em um servidor de tarifação. O servidor de download permite a instalação de plug-ins escritos em Java para interceptar os eventos de download.

No post anterior vimos a implementação do servidor de download e um plugin de exemplo. Neste post vamos ver a outra ponta: o sistema de tarifação. O sistema de tarifação é um sistema implementado em um servidor de banco de dados com tabelas e stored procedures.

O sistema que vamos ver é um sistema simulado, composto de uma tabela e três procedures que consultam e atualizam o sistema.

As procedures são as seguintes e todas possuem dois parâmetros, o primeiro é o id do usuário e o segundo o servico que ele esta tentando acessar.

boolean queryBalance(int, string) - consulta se o usuário possui saldo para acessar o serviço.
void charge(int, string) - tarifa o usuário no serviço especificado.
void credit(int, string) - estorna o usuário no serviço especificado.

O sistema é implementado no HSQLDB, um gerenciador de banco de dados escrito em Java. O HSQLDB possui um instalação simples e rápida. O HSQLDB possui suporte a stored procedures escritas em Java.

Para a instalação basta fazer o download e descompactar o arquivo no diretório de sua preferência.

Após descompactar no diretório de destino você vai ter uma pasta chamada hsqldb. No prompt de comando dentro dessa pasta execute o camando abaixo para executar o HSQLDB no modo servidor.

java -classpath ./lib/hsqldb.jar org.hsqldb.Server

Para executar a ferramenta administrativa do HSQLDB execute o comando abaixo.

java -cp ./lib/hsqldb.jar org.hsqldb.util.DatabaseManager

Para a criação das stored procedures utilizei o Netbeans. Uma stored procedure no HSQLDB é um método stático de uma classe Java. E a classe Java deve estar no classpath do servidor. Esse método acessa o ambiente do servidor através de um parâmetro Connection declarado na assinatura do método.

Nesse sistema criei uma classe chamada hsqldbSP_Billing no pacote hsqldbSP_Billing. A classe é a seguinte.

package hsqldbSP_Billing;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class hsqldbSP_Billing {

public static double DOWNLOAD = 10;

public static boolean query(Connection conn, Integer id, String service) throws SQLException{
boolean hasBalance = false;
Statement st = null;
ResultSet rs = null;
st = conn.createStatement();
String sql = "select amount from balance where id = " + id;
rs = st.executeQuery(sql);

if(rs.next()) {
double amount = rs.getDouble("amount");
if(amount > 0){
hasBalance = true;
}
}

st.close();
return hasBalance;
}

public static void charge(Connection conn, Integer id, String service) throws SQLException{
String sql = "update balance set amount = amount - ? where id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setDouble(1, DOWNLOAD);
pstmt.setInt(2, id);

pstmt.executeUpdate();

pstmt.close();
}

public static void credit(Connection conn, Integer id, String service) throws SQLException{
String sql = "update balance set amount = amount + ? where id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setDouble(1, DOWNLOAD);
pstmt.setInt(2, id);

pstmt.executeUpdate();

pstmt.close();
}
}


No HSQLDB podemos criar aliases para as SPs através do seguinte comando.
CREATE ALIAS QUERYBALANCE FOR "hsqldbSP_Billing.hsqldbSP_Billing.query"

No HSQLDB a assinatura da procedure possui somente do dois últimos parâmetros pois a Connection será passada pelo servidor. Parar executar as stores procedures a sintaxe é a seguinte.
call queryBalance(1, 'DOWNLOAD')
call charge(1, 'DOWNLOAD')
call credit(1, 'DOWNLOAD')

Executando o código.

Faça o download do arquivo hsqldb.zip e copie seu conteúdo para a pasta do HSQLDB. Este arquivo possui dois arquivos para auxiliar na execução do HSQLDB para este exemplo, o run.bat que executa o servidor HSQLDB com as SPs no classpath e o arquivo manager.bat que executa a ferramente administrativa do HSQLDB. Além disso esse arquivo possui a configuração inicial das tabelas e aliases necessárias para o sistema.

No arquivo hsqldbSP_Billing.zip está o projeto Netbeans com o código das SPs criadas.

No próximo post vamos criar a infra necessária para acessar o sistema de tarifação através de um sistema BPEL.

Até o próximo post!
Rodrigo Alencar.

Artigo completo (View Full Post)

sexta-feira, 30 de janeiro de 2009

Inicializando o IWForm com alguns controles desabilitados

Continuação do artigo Delphi Intraweb – JavaScript II (Continua)

Eu tenho no IWForm dois IWComboBoxes (CMBTIPOEMAIL e IWComboBox1) e um IWEdit (EDTEMAIL).
Podemos definir na propriedade “ExtraTagParams” valores específicos para cada tag original de um controle Intraweb. Por exemplo:


procedure TZnIWForm.DesabilitaControles;
begin
IWComboBox1.ExtraTagParams.Add('disabled="true"');
IWComboBox1.ExtraTagParams.Add('style=background:#E7E7E7');

EDTEMAIL.ExtraTagParams.Add('disabled="true"');
EDTEMAIL.BgColor := RGB(231, 231, 231);
CMBTIPOEMAIL.ExtraTagParams.Add('disabled="true"');
CMBTIPOEMAIL.ExtraTagParams.Add('style=background:#E7E7E7');
end;

No trecho de código acima, na linha 3, definimos a propriedade “disabled” do objeto, select, que será criado na página HTML, referente ao controle VCL “IWComboBox1”.

Na linha 4, definimos a propriedade “style” do objeto, select, para que o “background” seja cinza.
.
Na linha 5, assim como fizemos na linha 3, definimos a propriedade “disabled” do objeto, input text, que será criado na página HTML, referente ao controle VCL “EdtMail”.
Na linha 6, definimos a cor do input através da propriedade “BgColor”. A função RGB, permite que possamos definir as cores usando números inteiros de 0 a 255 representando as 256 variações de intensidade no o padrão RGB (Red, Green, Blue). OBS: A função “RGB” está definida na unit “Windows”, portanto declare essa unit na seção “uses”.
OBS: Por falar em RGB achei esse artigo sobre converter, em Delphi, RGB para CMYK (Cyan, Magenta, Yellow, Black - Key Plate), se é isso que vc procura veja em(http://delphi.about.com/od/delphitips2007/qt/rgb_cmyk.htm).


Agora, mais uma vez no evento OnCreate do IWForm faça a chamada a este novo método:

procedure TZnIWForm.IWAppFormCreate(Sender: TObject);
begin
LoadCmbTipoEmail;
DesabilitaControles;

IWComboBox



Adicione ao IWForm um IWComboBox (Palheta IWStandard). Em seguida codificaremos um método que irá preenchê-lo. Vou declarar uma seção “private” na definição da classe do IWForm. Nesta seção vou codificar o cabeçalho do método “LoadComboBandas”:

procedure LoadComboBandas;


Codificando o corpo do procedimento ..

procedure TZnIWForm.LoadComboBandas;
begin
IWComboBox1.Items.Add('Police');
IWComboBox1.Items.Add('Man At Work');
IWComboBox1.Items.Add('Pealr Jam');
IWComboBox1.Items.Add('The Smith');
IWComboBox1.Items.Add('U2');
IWComboBox1.Items.Add('Dire Straits');
IWComboBox1.Items.Add('Siouxsie & the Banshees');
end;


No evento OnCreate do IWForm você pode fazer chamada a este método.

procedure TZnIWForm.IWAppFormCreate(Sender: TObject);
begin
LoadCmbTipoEmail;
DesabilitaControles;
(*excuta a chamada ao método que carrega a IWCombo Bandas de Rock*)
LoadComboBandas;
end;

Continua em ...

Artigo completo (View Full Post)

Delphi Intraweb – JavaScript II (Continua)

Quem desenvolve para Web, precisa conhecer bem JavaScript. Quem desenvolve para Web, precisa usar muito JavaScript. Mesmo que ainda não saiba disso, quem desenvolve em Delphi para Web, também precisa ousar usar mais JavaScript.

JavaScript é o caminho o qual trilharemos neste artigo. Não temas, o Intraweb não é e não precisa ser tão feio quanto tem sido apresentado.


Intraweb (Atozed Software)

Será que você ficaria surpreso ao saber que existem pessoas desenvolvendo em Intraweb? Bem, pelo menos é o indica os números no fórum da Embarcadero Technologies. Bem interessante o “Embarcadero Discussion Forums” para quem esta pesquisando sobre o Intraweb recomendo dar uma passada por lá.
Será que a pergunta por que, apesar de tantos pontos fracos, o número de projetos Intraweb vem crescendo? Pelo que percebi, fora do Brasil também. Principalmente, diante do amadurecimento do “.Net” e do JSF, além da competitividade do Rails, será que o Intraweb ainda assim tem fôlego para competir com essas tecnologias? Pretendo dedicar um tempo e investigar em busca dessas repostas.

Neste artigo, vamos ver mais sobre “Delphi Intraweb” com JavaScript. Como conseqüência você poderá aprender mais e conhecer melhor o framework Intraweb.

Em forma geral, sobre Web e programação no lado cliente, percebo cada vez maior a pertinência desse tema. Acredito ser totalmente improvável o sucesso de um sistema Web, o qual não exista uma ênfase no balanceamento do que vai ser processado client-side e o que vai ser processado server-side. Temos discutido bastante sobre isso aqui no Estação Zn.
Portanto, vamos falar mais sobre.
Usaremos JavaScript no Intraeweb para tentar demonstrar como podemos obter um ganho em performance, robustez e elegância no desenvolvimento para Web.


Vamos começar com um exemplo simplório. Adicione no IWForm um IWEdit (Palheta IWStandard). Meu objetivo é mostrar como podemos acessar e manipular no lado cliente (browser) os objetos que adicionamos no IWForm. O desenvolvedor acostumado a pensar puramente em Delphi pode estar interessado em como interagir através do seu mundo “delphiano” com mundo dos navegadores.
Prosseguindo, após adicionar o IWEdit, codifique a função JavaScript exatamente conforme ilustrado abaixo. Muita atenção a caixa das letras digitadas. Não importa a caixa do nome do IWComponente que você definiu no Delphi. O Intraweb sempre vai criar um objeto HTML, em JavaScript na página que ele cria, referente ao IWForm em questão, em caixa ALTA.


function testaEdit(){
alert(document.forms[0].IWEDIT1.value);
document.forms[0].IWEDIT1.value = "";
document.forms[0].IWEDIT1.focus();
}

A função acima, simplesmente trata de exibir o conteúdo digitado no IWEdit1, lembrando que esse processamento é realizado no próprio browser. Ela deve ser adicionada na propriedade “JavaScript” do IWForm.

Para poder acessar o “IWEDIT1” em JavaScript usei a referencia “document.forms[0]” porque a forma mais comum seria usar “document.all. IWEDIT1”. O Problema é que dessa forma o script vai funcionar somente no browser da MS (Internet Explorer). Então, podemos concluir que ao usar “document.forms[0]” nosso JavaScript, pelo menos no que tange a referencias os objetos numa página HTML, estaria compatível com todos os navegadores? Sim, dessa forma nosso JavaScript está compatível. Todavia, isso não funciona no Intraewb 8. Ou melhor, ou talvez pior, ora funciona, ora não funciona. O Intraweb 8 criar várias tags “form” na página HTML. Provavelmente é por isso que volta e meia me deparo com aplicações Intraweb 8 que não estão usando nada de JavaScript.
Então, para garantir o comportamento adequado da função “testaEdit()” vamos modificar o código para:



function testaEdit(){
var ZnEdt = FindElem("IWEDIT1”);
alert(ZnEdt.value);
ZnEdt.value = "";
ZnEdt.focus();
}


Adicione ao IWForm um IWButton (TIWButton, palheta IWStandard). Altere a propriedade “Name” dele para “BtnTestaEdit”. Altere o “Caption” dele para “Testa IWEdit1 JavaScript”. Usaremos o “BtnTestaEdit” para chamarmos essa nova função. Digite na propriedade “ScriptEvent” do BtnTestaEdit, no “Event” OnClick, conforme ilustrado abaixo (adiante veremos como setar essa propriedade dinamicamente):




Server-Side X Client-Side – Delphi Intraweb

Podemos, para ilustrar nosso exemplo, implementar um procedimento em Delphi equivalente ao processamento realizado pela função JavaScript “testaEdit()”. Para isso, adicione mais um IWButton (TIWButton, palheta IWStandard). Altere a propriedade “Name” dele para “BtnTestaEditServerSide”. Altere a propriedade “Caption” para “Testa IWEdit1 Delphi”.



Vejamos, no evento “OnClick” do “BtnTestaEditServerSide” digite:

procedure TIWForm1.BtnTestaEditServerSideClick(Sender: TObject);
begin
Self.ActiveControl := IWEdit1;
WebApplication.ShowMessage(IWEdit1.Text);
IWEdit1.Text := ''
end;


Agora você pode experimentar o efeito da diferença entre processar Server-side frente ao processamento client-side. Para testar, pressione F9. Em seguida, na janela de diálogo do servidor standalone pressione F9 novamente.


VCL X HTML
Não é papo de bêbado, garanto. Conforme mencionei anteriormente, o framework Intraweb gera, a partir de um form Delphi (IWForm - VCL) e de controles (IWControls - VCL) associados a ele, uma página HTML na qual objetos são declarados (em JavaScript) referentes a cada um dos controles VCL dispostos no IWForm que a originou.
Para conhecer todos os objetos que compõem o objeto “form” de uma página HTML:

function percorreTudo(){
var ZnForms = document.getElementsByTagName("form");
alert("Oi");
for (i = 0; i< ZnForms.length;i++) {
alert("Bla!! O nome do form é: " + ZnForms[i].name);
for (j = 0; j< ZnForms[i].elements.length; j++) {
alert("Bla!! O nome do form é: " + ZnForms[i].name + " o elemento é:" + ZnForms[i].elements[j].name);
}
}
}


Essa função pode ser utilizada em qualquer página HTML. No Intrweb, podemos codificá-la na propriedade “JavaScript” do IWForm (TIWForm).



Para chamarmos a função “precorreTudo()” vou adicionar ao IWForm1 um IWButton (palheta IWStandard). Em seguida na propriedade “ScriptEvent”, no evento OnClick digite, conforme ilustrado na imagem abaixo:



Na propriedade “Caption” do IWButton1 vou digitar “PrecorreTudo”. Ok, caro leitor do Estação ZN, mais um teste já pode ser feito. Porem, antes adicione alguns controles no IWForm1. Por exemplo umas IWListBox, uns IWEdits (todos da palheta IWStandard).

Manipulando os objetos HTML via JavaScript no Intraweb

Na próxima função seremos mais ousados. Vamos percorrer todos os objetos do “form” num laço (“for”), atribuindo o nome, tanto do “form”, quanto dos objetos associados a ele, num IWListBox. Veja como o framework Intraweb cria e manipula os objetos, componentes visuais Delphi, numa página HTML.


function percorreTudoSetValue(){
var ZnForms = document.getElementsByTagName("form");
var ZnList = FindElem("IWLISTBOX1");
var ZnMemo = FindElem("IWMEMO1");
for (i = 0; i< ZnForms.length;i++) {
alert("Bla!! O nome do form é: " + ZnForms[i].name);
alert("Bla!! O ID do form é: " + ZnForms[i].id);
alert(ZnList.item(i).innerHTML);
idx = ZnList.options.length++;
ZnList.options[idx].value = ZnForms[i].name;
ZnList.options[idx].text = ZnForms[i].name;
ZnList.setAttribute("Selected","Selected");
document.SubmitForm.IWLISTBOX1.text = "
" + ZnForms[i].name;
ZnMemo.value += ZnForms[i].name;
for (j = 0; j< ZnForms[i].elements.length; j++) {
alert("Bla!! O nome do form é: " + ZnForms[i].name + " o elemento é: " + ZnForms[i].elements[j].name);
idx = ZnList.options.length++;
ZnList.options[idx].value = ZnForms[i].name + " | elemento: " + ZnForms[i].elements[j].name;
ZnList.options[idx].text = ZnForms[i].name + " | elemento: " + ZnForms[i].elements[j].name;
ZnList.setAttribute("Selected","Selected");
ZnMemo.value += "\n" + ZnForms[i].elements[j].name;
}
}
}

Observe que atribuir valor a um objeto “Select” usando JavaScript para isso não é um das coisas mais triviais. Observe também que sabendo fazer isso você pode ganhar bastante performance no seu aplicativo, isso caso você substitua processamento server-side, para montar os valores num “Select”, fazendo através de JavaScript, client-side.
Vamos testar então? O próximo passo: Você vai adicionar um IWListBox e manter a propriedade name dele como “IWLISTBOX1”. Adicione um IWMemo também “IWMEMO1”. O Memo é criado na página HTML como um objeto “TextArea”, ao passo que o IWListBox gera um “Select”.
Agora, adicionaremos mais um IWButton, para fazermos chamada a essa function. No IWButton3, altere a propriedade “Caption” para “Percorre Tudo II”. No evento “ScriptEvent”, semelhante ao que fizemos no IWButton1, no item OnClick, digite “percorreTudoSetValue()”.




OBS: O JavaScript é super, ultra, hiper, case sensitive. Portanto, muito cuidado com a digitação. Nas funções JavaScript que estamos criando, devemos ter sempre a preocupação de usar um JavaScript que seja compatível com todos os navegadores (ou no mínino com os mais conhecidos: IE e FireFox). Por isso, note que não estou usando “document.all.Obj” Esse comando não é suportado pelo Fire Fox.

Propriedade “Name” do Form
Não me refiro ao form VCL (IWForm), mas a tag “form” da página HTML.
A próxima função será para definirmos dinamicamente um nome para o objeto “form” na página HTML:
function NamedForm(){
alert("www.estacaozn.blogspot.com");
document.forms[0].name = "Landjah";
document.forms[0].id = "Landjah";
alert("Estação zn - O nome do form é: " + document.forms[0].name);
document.forms[0].IWEDIT1.value = "Landjah";
}



Vou adicionar outro IWButton ao IWForm1, para a propriedade “Caption” dele vou definir “Nome do Form”. Desta vez, vamos fazer a chamada da função “NamedForm()” em outra propriedade. Faremos isso na propriedade “ExtraTagParams”, do IWButton2:



Gora, se você clicar no IWButton2 antes de fazê-lo no IWBurron1, perceberá que o nome do “form” foi, na verdade pela primeira vez, definido.


Habilitando e Desabilitando controles através de JavaScript.

Pode ser de grande utilidade saber habilitar e desabilitar os controles, inputs de um form HTML, sem que para isso precise ir ao servidor. Veja a função abaixo:


function HabilitaControleEmail(){
var ZnEdtMail = FindElem("EDTEMAIL");
var ZnCmbTipoMail = FindElem("CMBTIPOEMAIL");
var ZnChkBoxMail = FindElem("IWCHECKBOX1");
ZnChkBoxMail.checked = !ZnChkBoxMail.checked;
ZnEdtMail.disabled = !ZnChkBoxMail.checked;
ZnCmbTipoMail.disabled = !ZnChkBoxMail.checked;

if (ZnEdtMail.disabled) {
ZnEdtMail.style.backgroundColor = "#E7E7E7";
ZnCmbTipoMail.style.backgroundColor = "#E7E7E7";
}
else {
ZnEdtMail.style.backgroundColor = "#FFFFFF";
ZnCmbTipoMail.style.backgroundColor = "#FFFFFF";
}
}

Na linha 2, recupero o objeto "EDTEMAIL".

Na linha 3, recupero o objeto Select o qual representa uma IWComboBox "CMBTIPOEMAIL ".

Na linha 4, recupero o objeto CheckBox o qual representa uma IWCheckBox "IWCHECKBOX1".

Na linha 5, simulo o comportamento do CheckBox.

Na linha 6 e 7, atribuímos a propriedade “disabled” dos controles “EDTMAIL” e “CMBTIPOEMAIL” a negação do valor do CheckBox.

A partir da linha 9, controlamos o efeito visual: atribuímos a cor cinza aos controles caso estejam desabilitados. Caso contrário, recebem a cor branca.


Precisamos, então, adicionar outro IWEdit, o qual alteraremos a propriedade “Name” para “EdtEMail”. Adicione um IWComboBox, altere sua propriedade “Name” para “CMBTIPOEMAIL”. Vamos atribuir dinamicamente valores a propridade “Items” do CMBTIPOEMAIL. Na seção “private” declare o procedimento “LoadCmbTipoEmail”. Implemente conforme ilustrado abaixo:


procedure TZnIWForm.LoadCmbTipoEmail;
begin
CMBTIPOEMAIL.Items.Add('Email Comercial');
CMBTIPOEMAIL.Items.Add('Email Residencial');
end;

No envento “OnCreate” do IWForm faça chamada ao método “LoadCmbTipoEmail”.

Adicione também ao IWForm um IWCheckBox. No Intraweb 8, não é possível acessar um CheckBox simplesmente pelo nome que ele esta definido no Delphi. Por exemplo, para você acessar o IWCheckBox1, além de ter usar caixa alta ainda temos que acrescentar “_CHECKBOX” com sufixo do nome do componente. O IWCheckBos1 deve ser referenciado em código como “IWCHECKBOX1_CHECKBOX” . Como alternativa podemos acessar o componente usando “document.all[IWCHECKBOX1]”. Esse comando é compatível tanto com o Mozila, quanto com o IE.
Na propriedade “ExtraTagParams” do IWCheckBox1 digite conforme ilustrado abaixo:



Observe que a propriedade “ExtraTagParams” habilita o “Code Editor” (Delphi 2006 – Intraweb 8).


Continua ....

Artigo completo (View Full Post)

segunda-feira, 26 de janeiro de 2009

Mais Threads, ClassLoader, Classpath, Arquitetura de Plug-ins, Interfaces e mais recursos Java - PARTE 1.

O problema é interceptar determinados eventos em um servidor de download para tarifação em tempo real em um servidor de tarifação. O servidor de download permite a instalação de plug-ins escritos em Java para interceptar os eventos de download.

Esses eventos são disparados no ciclo do download, e são os seguintes: solicitação de download, download iniciado, download terminado com sucesso, download cancelado.

A classe Java não deve sobrecarregar o servidor para não causar indisponibilidade do serviço. Se a tarifação não puder ser efetuada o download não pode ser interrompido.

O problema é conectar o sistema de tarifação ao sistema de download.

Interface Java que deve ser implementada para interceptar os eventos:

public interface MonitorPlugin {

public void downloadRequested(Download download);

public void downloadStarted(Download download);

public void downloadFinished(Download download);

public void downloadCanceled(Download download);
}


A classe Java deve estar dentro do pacote padrão (sem pacote) dentro de um JAR com o mesmo nome no diretório raiz da aplicação.


Exemplo:
import downloadserver.Download;
import downloadserver.MonitorPlugin;

public class MonitorPluginSOP implements MonitorPlugin {

public void downloadRequested(Download download) {
System.out.println("downloadRequested T:" + download.getThreadId() + " U:" + download.getUserId());
}

public void downloadStarted(Download download) {
System.out.println("downloadStarted T:" + download.getThreadId() + " U:" + download.getUserId());
}

public void downloadFinished(Download download) {
System.out.println("downloadFinished T:" + download.getThreadId() + " U:" + download.getUserId());
}

public void downloadCanceled(Download download) {
System.out.println("downloadCanceled T:" + download.getThreadId() + " U:" + download.getUserId());
}

}


A classe MonitorPluginSOP vai estar dentro do JAR MonitorPluginSOP.jar.

O sistema de tarifação possui os comandos de tarifação e estorno. Para tarifação e estorno devem ser informados o numero de identificação do cliente, o comando e o serviço a ser tarifado.

Vamos discutir mais sobre o sistema de tarifação e o plug-in que fará a conexão entre os servidores no próximo post.

Por enquanto vamos ver o servidor de download que é uma simulação de um servidor real.

A classe que representa um download é a seguinte:
package downloadserver;

import java.util.ArrayList;
import java.util.List;

/*
* A classe Download implementa Runnable para possuir o comportamento de uma
* Thread. Para dar a possibilidade de parar a execução a classe possui
* um atributo running, quando running igual a false o objeto para a sua
* execução. O atributo running é alterado através do método cancel.
*/
public class Download implements Runnable {

static private long threadCount = 0;
static public final long DOWNLOAD_SIZE = 16;
private long threadId;
private long bytesSent = 0;
private int userId;
private boolean running = true;
private List monitors = new ArrayList();

public Download() {
threadId = threadCount++;
}

public Download(int userId) {
this.userId = userId;
threadId = threadCount++;
}

public void setUserId(int userId) {
this.userId = userId;
}

public int getUserId() {
return userId;
}

public long getThreadId() {
return threadId;
}

public long getBytesSent() {
return bytesSent;
}

public void request() {
downloadRequested();
}

public void cancel() {
running = false;
}

public void addMonitor(MonitorPlugin monitor) {
monitors.add(monitor);
}

public void addMonitor(List monitors) {
this.monitors.addAll(monitors);
}

private void downloadRequested() {
for (MonitorPlugin m : monitors) {
m.downloadRequested(this);
}
}

private void downloadStarted() {
for (MonitorPlugin m : monitors) {
m.downloadStarted(this);
}
}

private void downloadFinished() {
for (MonitorPlugin m : monitors) {
m.downloadFinished(this);
}
}

private void downloadCanceled() {
for (MonitorPlugin m : monitors) {
m.downloadCanceled(this);
}
}

public void run() {
downloadStarted();
while (running && bytesSent < DOWNLOAD_SIZE) {
bytesSent++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bytesSent == DOWNLOAD_SIZE) {
downloadFinished();
} else {
downloadCanceled();
}
}
}


E a classe que representa o Servidor é a seguinte, a classe está comentada nos seus principais recursos:
package downloadserver;

import java.io.File;
import java.io.FilenameFilter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;

public class Server extends javax.swing.JFrame {

private int usersCount = 0;
private List monitorPlugins = new ArrayList();

public Server() {
initComponents();
loadMonitorPlugin();
inicia();
}

@SuppressWarnings("unchecked")
//
private void initComponents() {

jButton2 = new javax.swing.JButton();

setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Server");
setBounds(new java.awt.Rectangle(0, 0, 100, 500));
getContentPane().setLayout(new java.awt.FlowLayout());

jButton2.setText("Novo");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
getContentPane().add(jButton2);
}//


private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
novo();
}

/*
* Lê os arquivos de plug-in sem a necessidade de estarem no classepath.
*/
public void loadMonitorPlugin() {

try {
File dir = new File(".");
String[] children;
FilenameFilter filter = new FilenameFilter() {

public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
};
children = dir.list(filter);
if (children != null) {
for (int i = 0; i < children.length; i++) {
String filename = children[i];
try {
String classname = filename.replaceAll("[.]jar", "");

java.io.File file = new java.io.File(filename);
java.net.URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};

ClassLoader loader = URLClassLoader.newInstance(urls, this.getClass().getClassLoader());

Class clazz = loader.loadClass(classname);
MonitorPlugin mp = (MonitorPlugin) clazz.newInstance();
monitorPlugins.add(mp);

System.out.println("Adicionada: " + classname);

} catch (Exception ex) {
ex.printStackTrace();
}
}
}

} catch (Exception ex) {
ex.printStackTrace();
}
}

/*
* Cria um novo download e exibe na tela como um botão. Ao clicar no botão o download é cancelado.
*/
private void novo() {
Download d = new Download(usersCount++);
d.addMonitor(monitorPlugins);
d.request();
print(d);
new Thread(d).start();
}


/*
* Executado no início cria 5 novos downloads.
*/
public void inicia() {
for (int i = 0; i < 5; i++) {
novo();
}
}

/*
* Exibe um download na tela como um botão, adiciona um ouvinte (monitor)
* ao download para atualizar a interface e adiciona ao evento de click
* a operação de cancelar o download.
*/
private void print(final Download d) {
final JButton elementoGrafico = new JButton();
elementoGrafico.setText("T:" + d.getThreadId() + " U:" + d.getUserId());
getContentPane().add(elementoGrafico);

elementoGrafico.addMouseListener(new java.awt.event.MouseAdapter() {

@Override
public void mouseClicked(java.awt.event.MouseEvent evt) {
d.cancel();
}
});

d.addMonitor(
new MonitorPlugin() {

public void downloadRequested(Download download) {
}

public void downloadStarted(Download download) {
}

public void downloadFinished(Download download) {
elementoGrafico.setBackground(new java.awt.Color(51, 204, 0));
}

public void downloadCanceled(Download download) {
elementoGrafico.setBackground(new java.awt.Color(255, 0, 51));
}
});
setVisible(true);
}

public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {

public void run() {
new Server().setVisible(true);
}
});
}

// Variables declaration - do not modify
private javax.swing.JButton jButton2;
// End of variables declaration
}


Bem, é isso. Vou tentar postar a continuação o mais breve possível.

O arquivo para download.

Abraços, Rodrigo Alencar.

Artigo completo (View Full Post)

 
BlogBlogs.Com.Br