Spring dependencies

http://static.springsource.org/downloads/nightly/snapshot-download.php?project=SPR

http://static.springsource.org/downloads/nightly/release-download.php?project=SPR

Generating and Viewing PDF in AIR application


xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*"
creationComplete="init()">



import mx.core.UIComponent;
import mx.events.ResizeEvent;

import org.alivepdf.display.Display;
import org.alivepdf.fonts.CoreFont;
import org.alivepdf.fonts.FontFamily;
import org.alivepdf.layout.Layout;
import org.alivepdf.layout.Orientation;
import org.alivepdf.layout.Size;
import org.alivepdf.layout.Unit;
import org.alivepdf.pages.Page;
import org.alivepdf.pdf.PDF;
import org.alivepdf.saving.Method;

private var pdfViewer: HTMLLoader;

private function init(): void {

// Creamos un PDF con AlivePDF desde el mismo Flex
var pdf: PDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.A4);
pdf.setDisplayMode(Display.FULL_PAGE, Layout.SINGLE_PAGE);

// Añadimos una página
var page: Page = new Page(Orientation.PORTRAIT, Unit.MM, Size.A4);
pdf.addPage(page);

// Añadimos el contenido de la página
pdf.setFont(new CoreFont(FontFamily.ARIAL), 25);
pdf.writeText(23, "Hello World!");
pdf.drawRect(new Rectangle(100, 100, 100, 100));

// Almacenamos el PDF generado en la carpeta de almacenamiento de la aplicación
var folder: File = File.applicationStorageDirectory;
var file: File = new File(folder.url + "test.pdf");
var fs: FileStream = new FileStream();
fs.open(file, FileMode.WRITE);
fs.writeBytes(pdf.save(Method.LOCAL));
fs.close();

// Si el ordenador tiene capacidad para ver el PDF
if (HTMLLoader.pdfCapability == HTMLPDFCapability.STATUS_OK) {

// Cargamos el PDF generado en el visor de HTML desde local
var request: URLRequest = new URLRequest(folder.url + "test.pdf");
pdfViewer = new HTMLLoader();
pdfViewer.load(request);

//pdfViewer.loadString("h“);

// Añadimos el visor de PDF a la aplicación
var container: UIComponent = new UIComponent();
container.addChild(pdfViewer);
container.addEventListener(ResizeEvent.RESIZE, resizeHandler);
container.percentHeight = 100;
container.percentWidth = 100;
addElement(container);
}
}

private function resizeHandler(event: ResizeEvent): void {

// Ajustamos el tamaño del visor al tamaño disponible de la aplicación
pdfViewer.width = DisplayObject(event.target).width
pdfViewer.height = DisplayObject(event.target).height;
}
]]>

Kerberos, Spring, Flex

Supongamos tres ordenadores:

(Ojo con las mayúsculas y minúsculas, son importantes).

1. El controlador de dominio del dominio “LAB.COM” con Active Directory (DC-LAB.LAB.COM). Esta máquina tiene Windows 2003 Server.

2. El servidor donde reside nuestra aplicación (RS-LAB.LAB.COM). Este servidor también está en el dominio “LAB.COM”.

3. Una máquina que también está en el dominio “LAB.COM” desde donde se conectará un usuario con Internet Explorer a nuestra aplicación utilizando autenticación basada en Kerberos v5.

Pasos:

1. Configurar “C:\WINDOWS\krb5.ini” en la máquina donde reside nuestra aplicación:

[domain_realm]
.lab.com = LAB.COM
lab.com = LAB.COM

[libdefaults]
default_realm = LAB.COM

[realms]
LAB.COM = {
kdc = DC-LAB.LAB.COM
admin_server = DC-LAB.LAB.COM
default_domain = LAB.COM
}

2. Generamos un usuario en el controlador de dominio para nuestra aplicación. Se trata de un usuario normal. Se pone cualquier contraseña (no importa) y se marca para que no expire. Por ejemplo, creamos el usuario “http-rs-lab.lab.com”.

3. A continuación en el controlador de dominio, utilizando el comando ktpass, creamos el archivo cifrado con la clave privada que permitirá identificar a nuestra aplicación ante el servidor de Kerberos:

> ktpass -princ HTTP/rs-lab.lab.com@LAB.COM -mapuser http-rs-lab.lab.com@lab.com -pass * -out http-rs-lab.keytab

Targeting domain controller: DC-LAB.LAB.COM
Using legacy password setting method
Successfully mapped HTTP/rs-lab.lab.com to http-rs-lab.lab.com.
Type the password for HTTP/rs-lab.lab.com:
Type the password again to confirm:
WARNING: pType and account type do not match. This might cause problems.
Key created.
Output keytab to http-rs-lab.keytab:
Keytab version: 0×502
keysize 62 HTTP/rs-lab.lab.com@LAB.COM ptype 0 (KRB5_NT_UNKNOWN) vno 4 etype 0×1
7 (RC4-HMAC) keylength 16 (0x399f9bb3158d6b454313f297192705dd)

Aunque da un warning, no importa, funciona perfectamente.

Pienso que la clave está en hacer coincidir la parte de la url que hay después de “HTTP/” con la url de la aplicación (con minúsculas), ya que en mayúsculas no funciona.

4. A continuación copiamos el archivo “http-rs-lab.keytab” a la carpeta WEB-INF/classes de nuestra aplicación. Por lo visto es mejor tenerla fuera del classpath cuando esté en producción. Se puede referenciar también con el protocolo “file:” posteriormente en el XML de configuración de spring.

5. Configuramos spring:

<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=”http://www.springframework.org/schema/beans”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:sec=”http://www.springframework.org/schema/security”
xsi:schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd”>

<sec:http entry-point-ref=”spnegoEntryPoint”>
<sec:intercept-url pattern=”/secure/**” access=”IS_AUTHENTICATED_FULLY” />
<sec:custom-filter ref=”spnegoAuthenticationProcessingFilter”
position=”BASIC_PROCESSING_FILTER” />
</sec:http>

<bean id=”spnegoEntryPoint”
class=”org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint” />

<bean id=”spnegoAuthenticationProcessingFilter”
class=”org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter”>
<property name=”authenticationManager” ref=”authenticationManager” />
</bean>

<sec:authentication-manager alias=”authenticationManager”>
<sec:authentication-provider ref=”kerberosServiceAuthenticationProvider” />
</sec:authentication-manager>

<bean id=”kerberosServiceAuthenticationProvider”
class=”org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider”>
<property name=”ticketValidator”>
<bean
class=”org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator”>
<property name=”servicePrincipal” value=”HTTP/rs-lab.lab.com@LAB.COM” />
<property name=”keyTabLocation” value=”classpath:http-rs-lab.keytab” />
<property name=”debug” value=”true”/>
</bean>
</property>
<property name=”userDetailsService” ref=”dummyUserDetailsService” />
</bean>

<!– Just returns the User authenticated by Kerberos and gives him the ROLE_USER –>>
<bean id=”dummyUserDetailsService” class=”org.springframework.security.extensions.kerberos.sample.DummyUserDetailsService”/>
</beans>

Hasta aquí todo como lo explica estupendamente Mike en su blog (http://blog.springsource.com/2009/09/28/spring-security-kerberos/).

Cuando en el navegador se pidan los credenciales, el usuario se puede introducir de dos maneras:
Como “lab\paco” o como “paco@lab.com”. Ambas formas son compatibles.

Ahora vamos a ver cómo configurar Flex para conseguir que aquellos usuarios que estén en el dominio se puedan autenticar utilizando Kerberos y aquellos que no estén en el dominio puedan utilizar una autenticación basada en formularios de Flex.

…. DRAFT

nginx explicit page cache invalidation from Java

Example cache configuration:

proxy_cache_path /usr/local/nginx/cache/altaller levels=1:2 keys_zone=altaller:8m max_size=1000m inactive=600m;
proxy_temp_path /usr/local/nginx/cache/tmp;

server {
listen 80;
server_name admin.altaller.es;
index index.html;

auth_basic “altaller.es”;
auth_basic_user_file htpasswd;

location / {
proxy_pass http://localhost:8080;
proxy_cache altaller;
proxy_cache_key $uri$is_args$args;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;
}
}

This configuration specifies that there is a cache “altaller” stored in “/usr/local/nginx/cache/altaller”.

Each time that a new page is requested using http, a new file with a strange name is created inside previous folder. In this case, we request a “/admin/hello.html” page and a file “e74e6f246520c52df1b3e41fee0a5cdb” is created inside the cache structure that contains the following code:

z?1N????j?1N??0?NG
KEY: /admin/hello.html
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-US
Content-Length: 399
Date: Thu, 28 Jul 2011 21:53:46 GMT
Connection: close

Thu Jul 28 21:53:46 UTC 2011
5
23
hello

This is a message

hola
……………

As you can see, the key “/admin/hello.html” and the HTML content is contained inside this file.

If we delete this file, the page is invalidated from the nginx cache.

Looking inside the code of nginx_ngx_cache_purge plugin, it uses a nginx function “ngx_http_file_cache_create_key” that creates the name of the file using the key of the cached page.

And finally, the strange name is a MD5 digest of the key in hexadecimal.

To test this, I have developed in Java the equivalent code:

MessageDigest digest = MessageDigest.getInstance(“MD5″);
byte[] bytes = digest.digest(“/admin/hello.html”.getBytes(“utf-8″));
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0×100, 16).substring(1));
}
System.out.println(sb.toString());

And TACHÁN!!! The result is:
e74e6f246520c52df1b3e41fee0a5cdb

Finally, the only thing we must do to explicitly invalidate a page from the cache using Java is deleting the file whose name is a MD5 of the key in hexadecimal.

The file is created in two levels folder structure (“/cache/altaller/b/cd/e74e6f246520c52df1b3e41fee0a5cdb”).

First level is taken from the last letter in the file name “b”.
Second level is taken from the two letters before the last “cd”.

That’s all folks! ;-)

Configuring nginx reverse proxy cache

We needed a reverse proxy cache to manage the traffic to a Tomcat server. After evaluating several alternatives (G-WAN, Traffic Server, Varnish, Lighttpd, …), we decided to use nginx for its low memory usage.

This is the basic configuration to manage the traffic to the Tomcat server:

http {
  proxy_cache_path  /Users/paco/cache/altaller levels=1:2 keys_zone=altaller:8m max_size=1000m inactive=600m;
  proxy_temp_path /Users/paco/cache/tmp; 

  server {
    location / {
      proxy_pass http://localhost:8080;
      proxy_cache altaller;
      proxy_cache_valid  200 302  60m;
      proxy_cache_valid  404      1m;
    }
  }
}

That's all folks!

Processing a File with a Mule ESB Web Service

“MyWebService.java”

package com.hg;

import javax.jws.WebService;

@WebService
public interface MyWebService {
	String test(byte[] text);
}

"MyWebServiceImpl.java"

package com.hg;

import javax.jws.WebService;

@WebService(endpointInterface="com.hg.MyWebService", serviceName="MyWebService")
public class MyWebServiceImpl implements MyWebService {

	public String test(byte[] text) {
		return new String(text) + "\nOK";
	}
}

"mule-config.xml"

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:spring="http://www.springframework.org/schema/beans"
      xmlns:stdio="http://www.mulesoft.org/schema/mule/stdio"
      xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf"
      xmlns:http="http://www.mulesoft.org/schema/mule/http"
      xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.mulesoft.org/schema/mule/core

http://www.mulesoft.org/schema/mule/core/3.1/mule.xsd

http://www.mulesoft.org/schema/mule/stdio

http://www.mulesoft.org/schema/mule/stdio/3.1/mule-stdio.xsd

http://www.mulesoft.org/schema/mule/cxf

http://www.mulesoft.org/schema/mule/cxf/3.1/mule-cxf.xsd

http://www.mulesoft.org/schema/mule/http

			http://www.mulesoft.org/schema/mule/http/3.1/mule-http.xsd">

	<!-- Documentation URL: http://www.mulesoft.org/documentation/display/MULE3USER/Building+Web+Services+with+CXF -->
	<!-- REST: http://localhost:63081/rs/test/arg0/hola -->
    <flow name="MyWebServiceProducer">

    	<http:inbound-endpoint
    			address="http://localhost:63081/rs"
    			exchange-pattern="request-response">

	        <cxf:jaxws-service serviceClass="com.hg.MyWebService"/>
    	</http:inbound-endpoint>

    	<component class="com.hg.MyWebServiceImpl"/>
	</flow>

	<!-- Documentation URL: http://www.mulesoft.org/documentation/display/MULE3USER/Consuming+Web+Services+with+CXF -->
	<flow name="MyWebServiceConsumer">
		<inbound-endpoint address="file:///C:/in">
			<object-to-byte-array-transformer/>
		</inbound-endpoint>

  		<cxf:jaxws-client
  				serviceClass="com.hg.MyWebService"
         		operation="test"/>

  		<outbound-endpoint address="http://localhost:63081/rs" exchange-pattern="request-response"/>
		<outbound-endpoint address="file:///C:/out"/>
	</flow>
</mule>

Merging two XML documents in ActionScript

The other day I needed to merge two XML documents using ActionScript (Flex). I show here a simple example of a recursive function to do so.


<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" applicationComplete="example()"> <fx:Script> <![CDATA[ import mx.controls.Alert; private function example(): void { var xml1: XML = <fx:XMLList xmlns:fx="http://ns.adobe.com/mxml/2009"> <fx:menuitem label="menu-1"> <fx:menuitem label="menu-1-1"> <fx:menuitem label="option-1"/> <fx:menuitem label="option-2"/> </fx:menuitem> <fx:menuitem label="menu-1-2"> <fx:menuitem label="option-3"/> </fx:menuitem> </fx:menuitem> <fx:menuitem label="menu-2"> <fx:menuitem label="option-4"/> </fx:menuitem> </fx:XMLList> var xml2: XML = <fx:XMLList xmlns:fx="http://ns.adobe.com/mxml/2009"> <fx:menuitem label="menu-1"> <fx:menuitem label="menu-1-1"> <fx:menuitem label="option-2"/> <fx:menuitem label="option-6"/> </fx:menuitem> <fx:menuitem label="menu-1-2"> <fx:menuitem label="menu-1-2-1"> <fx:menuitem label="option-7"/> <fx:menuitem label="option-8"/> </fx:menuitem> </fx:menuitem> </fx:menuitem> <fx:menuitem label="menu-3"> <fx:menuitem label="option-5"/> </fx:menuitem> </fx:XMLList> merge(xml1, xml2); Alert.show(xml1.toXMLString()); } private function merge(destination: XML, source: XML): void { var t1: XML; var t2: XML; for each (t2 in source.*) { var exists: Boolean = false; for each (t1 in destination.*) { if (t1.@label == t2.@label) { exists = true; break; } } if (exists) { merge(t1, t2); } else { destination.appendChild(t2); } } } ]]> </fx:Script> </s:Application>

See you soon!

Running MWE (Modeling Workflow Engine) workflows outside Eclipse

Software development can be simplified by using MDD (Model Driven Development). I use MWE, Xpand, Xtend, (formerly known as oAW [openArchitectureWare]), …, to generate code from higher-level models.

Thus, it increases flexibility and agility in software development, shifting from an model focused on the implementation to a model focused on the design.

Sometimes I need to run the MWE workflows outside of Eclipse.

These are the necessary steps that have worked for me:

// Change the context class loader
ClassLoader before = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(
    GenerationServiceImpl.class.getClassLoader());

// Add binary folders to classpath to allow the workflow
//     to find the necessary custom classes (postprocessors, functions)
URLClassLoader loader = (URLClassLoader) Thread.currentThread()
    .getContextClassLoader();
Method method = loader.getClass().getDeclaredMethod(
    "addURL", new Class[] {URL.class});
method.setAccessible(true);
method.invoke(loader, new Object[] {
    new File(CARTRIDGE_BASE_FOLDER, "bin").toURI().toURL()});

// Instantiate Workflow runner
final WorkflowRunner runner = new WorkflowRunner();

// Change the resource loader to find the resources
//    needed by the workflow
ResourceLoader resourceLoader = new ResourceLoader(
    new File(CARTRIDGE_BASE_FOLDER, "bin"));
ResourceLoaderFactory.setCurrentThreadResourceLoader(
    resourceLoader);

// Overwrite the parameters of the workflow
Map params = new HashMap();
File modelFolder = new File(workingFolder, "model");
params.put("applicationModel", new File(modelFolder, "model.mm"));
params.put("projectName", "result");
params.put("projectFolder", workingFolder.getPath());

// Specify the workflow file
String wfFile = "D:/.../generator.mwe";

runner.run(wfFile,
		new NullProgressMonitor(),
		params,
		null);

// Restore the previous context class loader
ResourceLoaderFactory.setCurrentThreadResourceLoader(null);
Thread.currentThread().setContextClassLoader(before);

And this is an example of custom resource loader that works for me:

import java.io.File;
import java.net.URL;

import org.eclipse.emf.mwe.core.resources.ResourceLoaderDefaultImpl;

public class ResourceLoader extends ResourceLoaderDefaultImpl {

	private File baseFolder;

	public ResourceLoader(File baseFolder) {
		this.baseFolder = baseFolder;
	}

	@Override
	protected URL loadFromContextClassLoader(String path) {
		URL url = null;

		try {
			url = new URL(path);
		}
		catch (Exception e) {
			try {
				File file = new File(path);
				if (!file.isAbsolute())
				  file = new File(baseFolder, path);

				url = file.toURI().toURL();
			}
			catch (Exception e2) {
				e2.printStackTrace();
			}
		}
		return url;
	}
}

Finally, this is a piece of the workflow file:

<?xml version="1.0" encoding="utf-8"?>
<workflow>
  <property file="workflow/generator.properties"/>

  <!-- Load internal Model - Begin -->
  <component class="org.eclipse.emf.mwe.utils.Reader">
    <uri value="${applicationModel}"/>
    <modelSlot value="model"/>
  </component>
  <!-- Load internal Model - End -->

  <!-- Generate static resources - Begin -->
  <component class="com.hernandezgomez.CopyResources">
    <projectFolder value="${projectFolder}"/>
    <projectName value="${projectName}"/>
    <resourcesFolder value="${cartridgeResources}"/>
  </component>
  <!-- Generate static resources - End -->

  <!-- Generate code - Begin -->

  <!-- Root folder - Start -->
  <component class="org.eclipse.xpand2.Generator">
    <metaModel id="ecore"
        class="org.eclipse.xtend.typesystem.emf.EmfMetaModel"
        metaModelPackage="org.eclipse.emf.ecore.EcorePackage"/>

    <metaModel id="mm"
        class="org.eclipse.xtend.typesystem.emf.EmfMetaModel"
        metaModelPackage="mm.MmPackage">

      <metaModelFile value="metamodel/mm.ecore"/>
    </metaModel>

    <fileEncoding value="utf-8"/>
    <globalVarDef name="projectName" value="'${projectName}'"/>
    <outlet path="${projectFolder}/${projectName}">
      <postprocessor class="com.hernandezgomez.SkipWrittingSameFile"/>
    </outlet>
    <expand value="templates::project::main FOR model"/>
  </component>

  <component class="org.eclipse.xpand2.Generator">
    <metaModel idRef="mm"/>
    <fileEncoding value="utf-8"/>
    <outlet path="${projectFolder}/${projectName}">
      <postprocessor class="com.hernandezgomez.SkipWrittingSameFile"/>
    </outlet>
    <expand value="templates::build::main FOR model"/>
  </component>
  ...
</workflow>

That’s all! ;-)

Ubuntu 10.10 XWindows Putty

C:\>xming :0 -clipboard -multiwindow

In putty.exe -> SSH -> X11 -> Check “Enable X11 forwarding” and introduce localhost:0 in “X display location”.

From Putty console:

# firefox (or whatever you want)

Managing Spring dependencies using Apache Ivy

This is a simple example of configuring Apache Ivy to manage/retrieve the Spring dependencies.

First, to install Ivy in Ant, ivy-x.x.x.jar must be copied to ANT_HOME/lib.

These are the necessary files:

“build.xml”

<project name="test" default="run"
        xmlns:ivy="antlib:org.apache.ivy.ant">
    <property name="lib.dir" value="lib"/>
    <property name="build.dir" value="build"/>
    <property name="src.dir" value="src"/>
    <property name="res.dir" value="resources"/>

    <path id="lib.path.id">
        <fileset dir="${lib.dir}" includes="*.jar"/>
    </path>

	<path id="run.path.id">
        <path refid="lib.path.id"/>
        <path location="${build.dir}"/>
        <path location="${res.dir}" />
    </path>

	<ivy:settings file="ivysettings.xml"/>

    <target name="resolve">
        <ivy:retrieve/>
    </target>    

    <target name="report" depends="resolve">
        <ivy:report todir="${build.dir}"/>
    </target>

    <target name="run" depends="resolve">
        <mkdir dir="${build.dir}" />
        <javac srcdir="${src.dir}" destdir="${build.dir}"
                classpathref="lib.path.id" />
        <java classpathref="run.path.id" classname="test.Main"/>
    </target>

	<target name="clean-cache">
		<ivy:cleancache />
	</target>
</project>

"ivy.xml"

<ivy-module version="2.0">
	<info organisation="com.hernandezgomez" module="server"/>
	<dependencies>
		<dependency org="org.springframework"
                        name="org.springframework.core"
                        rev="3.0.5.RELEASE"/>
		<dependency org="org.springframework"
                        name="org.springframework.beans"
                        rev="3.0.5.RELEASE"/>
		<dependency org="org.springframework"
                        name="org.springframework.context"
                        rev="3.0.5.RELEASE"/>
	</dependencies>
</ivy-module>

"ivy.settings"

<ivysettings>
  <settings defaultResolver="spring.chain"/>
    <resolvers>
      <chain name="spring.chain">
        <url name="com.springsource.repository.bundles.release">
        <ivy pattern="http://repository.springsource.com/
            ivy/bundles/release/[organisation]/
            [module]/[revision]/[artifact]-[revision].[ext]" />
        <artifact pattern="http://repository.springsource.com/
            ivy/bundles/release/[organisation]/
            [module]/[revision]/[artifact]-[revision].[ext]" />
      </url>
      <url name="com.springsource.repository.bundles.external">
        <ivy pattern="http://repository.springsource.com/
            ivy/bundles/external/[organisation]/
            [module]/[revision]/[artifact]-[revision].[ext]" />
        <artifact pattern="http://repository.springsource.com/
            ivy/bundles/external/[organisation]/
            [module]/[revision]/[artifact]-[revision].[ext]" />
      </url>
    </chain>
  </resolvers>
</ivysettings>

"Main.java"

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
	public static void main(String[] args) throws Exception {
		ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"beans/*.xml"});
		context.getBean("server");
	}
}

"Server.java"

package test;

public class Server {

	public Server() {
		System.out.println("Server instantiated");
	}
}

"beans/server.xml"

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="server" class="test.Server"/>

</beans>

"log4j.properties"

log4j.rootCategory=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.SimpleLayout

Finally, execute ant:

# ant

See you soon!