quarta-feira, dezembro 29, 2004

Wiki SPAM + Blacklists

Recentemente, vários wikis tem sido pichados com links. Aparentemente a idéia dos pichadores é aumentar o rank page no Google com isso. A idéia já é estúpida pelo simples fato que vc poderia simplesmente pagar para colocar propaganda no Google, mas vá lá, quem faz isso não costuma fazer propaganda de nenhum site "legal" (no sentido jurídico da bagaça). O site do XisPê é uma das grandes vítimas. Acabei tendo a oportunidade de dar uma mexidinha nele e estou tentando colocar um "blacklist" de sites. O Osvaldo, da Objective, me deu um toque e apontou para um wiki (o MoinMoin) que já faz blacklist. Acho que o próximo passo é consultar o blacklist deles, e talvez de outros sites, e adicioná-los no blacklist do XisPê de tempos em tempos. Achei o desenvolvimento desse tipo de coisa bem simples e interessante. Soluções para evitar spam em wikis não tem implementação muito complicada, mas dá pra gente imaginar um bocado de brincadeiras e métodos pra fazer isso. ---- Sites com blacklists: Del.icio.us - blacklist sites

Pattern para transformação simples?

Faz um tempo que não escrevo nada. Esses dias estava mexendo com transformações entre dois objetos. O problema é que nem sempre as transformações são entre atributos simples, algumas vezes um atributo vira dois, outras vezes dois atributos viram um, outras vezes ainda uma transformação é baseada no resultado de uma outra... Acabei usando o princípio do KISS (Keep It Simple Stupid) e a solução ficou bastante boa. Vou compartilhar aqui pra não esquecer. Basicamente, eu crio vários transformadores, cada um recebe o objeto de origem e o objeto de destino. Dessa forma, o transformador pode acessar o que bem entender dos dois lados. Para transformar o objeto inteiro, eu crio uma coleção de transformadores e rodo eles todos em cima do mesmo objeto origem e objeto destino. O objeto destino funciona como um Collecting Parameter (isso é um pattern, viu!) e ao final de rodar todos os transformadores, ele está totalmente preenchido. Simples né? As vantagens: 1 - Se tiver transformações que dependam do resultado de outra, vc pode simplesmente colocar uma antes da outra. O transformador dependente vai consultar o resultado no objeto destino para decidir o que fazer, uma vez que a transformação anterior já preencheu ela. 2 - Todo transformador pode ser fatorado para trabalhar na base do um-pra-um. Exemplificando: imagine que tenho um transformador que pega o campo "número de registro" na origem e coloca os cinco primeiros dígitos em "número do departamento" e os cinco últimos em "número do funcionário". Posso quebrar ele em dois transformadores, um que usa "número de registro"->5 primeiros dígitos->"número do departamento" e outro que usa "número de registro"->5 últimos dígitos->"número do funcionário", ao invés de usar um transformador só que faz tudo. Da mesma forma, posso compor vários transformadores em um único, e se eu quiser fazer gracinha, posso implementar eles como se fossem um Composite, aí um transformador pode conter vários transformadores. Mas não vi muita vantagem em fazer isso. No geral, a abordagem ficou muito simples e muito extensível. Será que isso já está escrito como pattern em algum lugar?

quarta-feira, dezembro 15, 2004

Carregamento dinâmico de classes em Java

Tá aqui um truque java que vale a pena. Basicamente, tenho o source code vindo do limbo e quero criar uma classe "on the fly". A dureza é que o java não é nada amistoso pra fazer isso, mas dá pra fazer. ;) Peguei do seguinte fórum
package com.sun.tools.javac.v8;
 
import com.sun.tools.javac.v8.JavaCompiler;
import com.sun.tools.javac.v8.util.List;
import com.sun.tools.javac.v8.util.Context;
import com.sun.tools.javac.v8.comp.Gen;
import com.sun.tools.javac.v8.comp.Env;
import com.sun.tools.javac.v8.tree.Tree;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
 
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.Iterator;
 
public class Derik1
{
	static class Compiler
	{
		private ByteArrayOutputStream os;
 
		public byte[] compile(String name, final String source)
			throws Throwable
		{
			os= new ByteArrayOutputStream(1024);
 
			final Context context= new Context();
 
			JavaCompiler compiler= new JavaCompiler(context) {
				public InputStream openSource(String filename) {
					return new ByteArrayInputStream(source.getBytes());
				}
				void genCode(Env env, Tree.ClassDef classdef) 
					throws IOException
				{
					Gen.instance(context).genClass(env, classdef);
					writer.writeClassFile(os, classdef.sym);
				}
			};
	
			List list= new List();
			list= list.append(name +".java");
			compiler.compile(list);
 
			byte[] bytes= os.toByteArray();
			os.close();
			return bytes;
		}
	}
 
	static class Loader
		extends ClassLoader
	{
		private Hashtable mClasses= new Hashtable();
 
		public void add(String name, byte[] bytes) { mClasses.put(name, bytes); }
 
		public Class findClass(String name)
			throws ClassNotFoundException 
		{
			byte[] bytes= (byte[]) mClasses.get(name);
			if (bytes == null) 
				return super.findClass(name);
			return defineClass(name, bytes, 0, bytes.length);
		}
	}
 
	static class Attribute
	{
		public String name;
		public String type;
 
		public Attribute(String name, String type)
		{
			this.name= name.substring(0,1).toUpperCase() +
				name.substring(1).toLowerCase();
			this.type= type;
		}
 
		public String declaration() {
			return "private " +type +" m" +name +";";
		}
 
		public String getter() {
			return "public " +type +" get" +name +"() { return m" +name +"; }";
		}
 
		public String setter() {
			return "public void set" +name +
				"(" +type +" " +name.toLowerCase() +") { m" +
				name +"= " +name.toLowerCase() +"; }";
		}
	}
 
	public static void main(String[] argv)
		throws Throwable
	{
		BufferedReader reader= new BufferedReader(
			new InputStreamReader(System.in));
 
		System.out.print("Class: ");
		String cname= reader.readLine();
 
		ArrayList attributes= new ArrayList();
 
		while (true) {
			System.out.print("Attribute (return to finish): ");
			String name= reader.readLine();
			if (name.length() < 1) 
				break;
			System.out.print("Type for " +name +": ");
			String type= reader.readLine();
			attributes.add(new Attribute(name, type));
		}
 
		String src= "public class " +cname +" {";
 
		Iterator iter= attributes.iterator();
		while (iter.hasNext()) 
			src += "\n\t" + ((Attribute) iter.next()).declaration();
 
		iter= attributes.iterator();
		while (iter.hasNext()) {
			Attribute a= (Attribute) iter.next();
			src += "\n\t" + a.setter();
			src += "\n\t" + a.getter();
		}
 
		src += "\n\tpublic String toString() {";
		src += "\n\t\treturn \"";
		iter= attributes.iterator();
		boolean first= true;
		while (iter.hasNext()) {
			Attribute a= (Attribute) iter.next();
			if (!first) 
				src += "+\",\" +\n\t\t\t\"";
			src += a.name +"=\" +get" +a.name +"()";
			first= false;
		}
		src +=  ";\n\t}\n}";
 
		System.err.println("##### Source Code");
		System.err.println(src);
 
		System.err.println("##### Compiling...");
		byte[] bytes= new Compiler().compile(cname, src);
    
		System.err.println("##### Class Loading...");
		Loader loader= new Loader();
		loader.add(cname, bytes);
		Class clazz= loader.findClass(cname);
 
		System.err.println("##### Class: " +clazz.getName());
    
		Object instance= clazz.newInstance();
		System.err.println("##### Instance: " +instance);
	}
}

segunda-feira, dezembro 06, 2004

Desenvolver software = nova linguagem != construir casas

Recentemente eu estava lendo alguns artigos sobre LOP (Language Oriented Programming) e sobre DSL (Domain Specific Languages), eles acabaram me dando finalmente a metáfora que eu estava procurando para desenvolvimento de software "Desenvolver software é como criar uma linguagem" Normalmente, associamos desenvolvimento de software com construção civil: "Desenvolver software é como construir um prédio, vc primeiro faz o design da planta, blah, blah, blah". Sempre achei esse conceito perigosamente errado, mas ainda não tinha achado um conceito para substituir ele. Bom, talvez eu não tenha achado o conceito certo (será que existe conceito certo?), mas acho que pelo menos é mais adequado pra mim. A metáfora sobre linguagem é que quando criamos um objeto e seus métodos, estamos criando um "conceito", ou seja, uma palavra para ser usada em no programa. Podemos montar conceitos em cima de conceitos para expressar conceitos mais complexos. Isso é mais ou menos o conceito de "camada" mas não tão restrititivo qdo elas. Complicado, né? Bom, acho que o melhor é refletir um pouco mais sobre isso antes de escrever muito. Vou fazer uns protótipos aqui e ver no que dá, aí reporto o que aconteceu no blog. Que vc acha? Essa metáfora vira?