| 2 items | |
| Frictionless data preloading in AngularJS http://t.co/QDwlEiQoDS | |
Frictionless data preloading in AngularJS http://t.co/QDwlEiQoDS | |
| KendoNg–Part 2–Data http://t.co/gSgoFVJeuZ | |
KendoNg–Part 2–Data http://t.co/gSgoFVJeuZ | |
lunes, junio 09, 2014
Resumen diario de links sobre Node.js AngularJS y MongoDB
lunes, febrero 17, 2014
Mean Flipboard Magazine
Obviamente hace mucho que no actualizo este blog, pero si os interesa el stack MEAN (MongoDB, Express, AngularJS y Node.js) no dejéis de visitar esta revista de Flipboard.
jueves, febrero 24, 2011
Links útiles sobre Spring
El SpringSource Team Blog no deja de postear diversos artículos sobre Spring 3.1 y de la serie Grean Beans (Spring en general). Aquí va una recopilación:
Green Beans
Putting the Spring in Your Step (and Application)
Getting Started with Spring MVC
Getting Started with Spring in your Service Tier
Getting Started with Maven and Spring
Getting Started with Enterprise Messaging and Spring
Getting Started with Spring Integration
Spring 3.1
Spring 3.1 M1: Introducing @Profile
Unified Property Management
Introducing FeatureSpecification support
MVC Namespace Enhancements and @Configuration
Cache Abstraction
domingo, abril 25, 2010
Seminario en la Semana Informática
El día 27 de Abril, mi colega Pedro Molina y yo daremos una charla sobre frameworks y MDD. La charla será dentro del evento Semana Informática 2010: Productividad y flexibilidad de desarrollo basada en frameworks y generación de código.
Habiendo contribuido en numerosas ocasiones al desarrollo de frameworks para instituciones de lo mas diversas, el reto está en saber transmitir la necesidad de tales frameworks, la complejidad que conlleva su desarrollo y los beneficios en productividad que se logran.
Pero tampoco hay que olvidar una parte fundamental de lo que se pretende lograr con un framework: la flexibilidad en los desarrollos. De nada nos sirve estandarizar la forma de construir aplicaciones, y el aporte de servicios de infraestructura muy robustos (¿eso es un framework de desarrollo, verdad?), si cada vez que cambia la tecnología tenemos que cambiar el framework.
Si ante cada cambio de tecnología nuestro framework va quedando obsoleto, la capacidad de adaptarnos a las necesidades del negocio van siendo cada vez menores, hasta tal punto de convertirnos en un obstáculo para el mismo. Y nuestro objetivo como informáticos es lograr que informática esté alineada siempre al 100% con el negocio, no ser el obstáculo del cambio.
En este punto es donde los beneficios de la generación de código se hacen patentes. Desde Spring Roo, hasta la generación mediante modelos, de lo que se trata es de "meta-programar".
Como es que después de mas de 30 años de ordenadores, sigamos por ejemplo dedicando jornadas enteras a desarrollar un simple mantenimiento de una tabla? Como es que no hemos logrado todavía decir "HAL: hazme un mantenimiento de la tabla Cliente"?
Realmente no tengo la respuesta a porqué, pero si sé que el 80% del código de una aplicación es repetible y se puede modelizar y generar, por lo que deberíamos dedicar mas esfuerzo a meta-programar y menos en mantenimientos.
Y también sé que me gustaría luego dedicarme tranquilamente al otro 20%.
jueves, abril 08, 2010
Verificar firma con OpenSAML
Suponiendo una llamada webservice que contenga una Assertion SAML firmada, como verificamos esa firma? CXF provee "interceptores" para incluir código antes y después de una invocación a webservice.
Primero debemos configurar (con Spring en este caso) la invocación:
<jaxws:client id="clientWS" serviceClass="es.mycompany.MyService" address="http://localhost:8080/app/services/myservice">
<jaxws:dataBinding>
<ref bean="aegisBean" />
</jaxws:dataBinding>
<jaxws:inInterceptors>
<ref bean="clientWS.InInterceptor" />
</jaxws:inInterceptors>
</jaxws:client>
<bean id="clientWS.InInterceptor" class="es.mycompany.MyInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature"/>
<entry key="passwordCallbackClass" value="es.mycompany.MyPasswordCallback"/>
<entry key="signaturePropFile" value="keystore.properties"/>
</map>
</constructor-arg>
</bean>Luego creamos nuestro fichero de propiedades del keystore ("keystore.properties"):
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.alias=MyAlias org.apache.ws.security.crypto.merlin.keystore.password=MyPassword org.apache.ws.security.crypto.merlin.file=keystore.jks
Y finalmente la clase encargada de dar las contraseñas de acceso a los certificados. Esta clase es claramente mejorable, pero sirve como ejemplo:
public class MyPasswordCallback implements CallbackHandler {
private Map passwords = new HashMap();
public STSPasswordCallback() {
passwords.put("MyAlias", "MyPassword");
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
}
}
}
}
Teniendo todo configurado, solo nos queda el código del interceptor, que a su vez se apoya en una clase de utilidades:
public class MyInterceptor extends WSS4JInInterceptor {
private String issuer = "http://www.mycompany.com";
public MyInterceptor() {
super();
}
public MyInterceptor(Map properties) {
super(properties);
}
protected void doReceiverAction(int doAction, RequestData reqData) throws WSSecurityException {
// Get SOAP Header
SOAPMessage message = ((org.apache.cxf.binding.soap.SoapMessage) reqData.getMsgContext()).getContent(javax.xml.soap.SOAPMessage.class);
SOAPHeader soapHeader = message.getSOAPHeader();
// Get Assertion XML
XPathUtils xu = new XPathUtils();
Element xmlAssertion = (Element) xu.getValueNode("//saml2:Assertion", soapHeader);
// Unmarshall
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(xmlAssertion);
Assertion assertion = (Assertion) unmarshaller.unmarshall(xmlAssertion);
// Get Handler properties
String sigPropFile = getString(WSHandlerConstants.SIG_PROP_FILE, reqData.getMsgContext());
String callback = getString(WSHandlerConstants.PW_CALLBACK_CLASS, reqData.getMsgContext());
// Verify
SAMLUtils.verifySignature(assertion, issuer, sigPropFile, callback);
super.doReceiverAction(doAction, reqData);
}
public void handleMessage(SoapMessage message) throws Fault {
super.handleMessage(message);
}
}
public class SAMLUtils {
public static void verifySignature(Assertion assertion, String issuer, String sigPropFile, String callback) throws WSSecurityException {
X509Credential cred = getX509Credential(sigPropFile, callback);
StaticCredentialResolver credResolver = new StaticCredentialResolver(cred);
KeyInfoCredentialResolver kiResolver = SecurityHelper.buildBasicInlineKeyInfoResolver();
ExplicitKeySignatureTrustEngine trustEngine = new ExplicitKeySignatureTrustEngine(credResolver, kiResolver);
CriteriaSet criteriaSet = new CriteriaSet();
criteriaSet.add(new EntityIDCriteria(issuer));
criteriaSet.add(new UsageCriteria(UsageType.SIGNING));
criteriaSet.add(new MetadataCriteria(IDPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS));
try {
trustEngine.validate(assertion.getSignature(), criteriaSet);
} catch (SecurityException e) {
throw new WSSecurityException(e.getMessage());
}
}
public static X509Credential getX509Credential(String sigPropFile, String callback) throws WSSecurityException {
PrivateKey privateKey = null;
X509Certificate[] certs = null;
Crypto crypto = crypto = CryptoFactory.getInstance(sigPropFile);
try {
CallbackHandler cbhandler = getPasswordCallBack(callback);
WSPasswordCallback cb = new WSPasswordCallback(crypto.getDefaultX509Alias(), WSPasswordCallback.SIGNATURE);
cbhandler.handle(new Callback[] { cb });
privateKey = crypto.getPrivateKey(crypto.getDefaultX509Alias(), cb.getPassword());
certs = crypto.getCertificates(crypto.getDefaultX509Alias());
} catch (Exception e) {
throw new WSSecurityException(e.getMessage());
}
if (certs.length != 1) {
throw new WSSecurityException("Couldn't get the (" + crypto.getDefaultX509Alias() + ") signature certificate.");
}
X509Credential cred = SecurityHelper.getSimpleCredential(certs[0], privateKey);
return cred;
}
public static CallbackHandler getPasswordCallBack(String callback) throws WSSecurityException {
CallbackHandler cbHandler = null;
try {
Class cbClass = Loader.loadClass(callback);
cbHandler = (CallbackHandler) cbClass.newInstance();
} catch (Exception e) {
throw new WSSecurityException("WSHandler: cannot create instance of password callback: " + callback, e);
}
return cbHandler;
}
} Como ven, todo el trabajo lo hace la línea trustEngine.validate(assertion.getSignature(), criteriaSet);, solo hay que llegar hasta ella con los datos adecuados.
lunes, octubre 26, 2009
Receta CXF, WSS4J y Spring Security
Como utilizar la seguridad de Spring Security para autenticar un usuario/contraseña de un servicio web implementado con CXF mediante WSS4J (Java WS-Security de Apache):
En el aplication context de spring, primero definimos los namespaces, importamos los xml de CXF y definimos el "Interceptor" que gestionará la seguridad:
<?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:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:cxf="http://cxf.apache.org/core"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd"
>
<!-- Load CXF modules from cxf.jar -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="WSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<property name="properties">
<map>
<entry key="action" value="UsernameToken Timestamp"/>
<entry key="passwordType" value="PasswordDigest"/>
<entry key="passwordCallbackRef">
<bean class="my.company.SecurityInPasswordHandler"/>
</entry>
</map>
</property>
</bean>
</beans>
Y luego agregamos el interceptor a los webservices que necesiten seguridad:
<jaxws:endpoint id="salaWebService" implementor="#salaService" address="/salas">
<jaxws:inInterceptors>
<ref bean="WSS4JInInterceptor"/>
</jaxws:inInterceptors>
</jaxws:endpoint>
Asumiendo que tenemos el siguiente servicio:
@WebService
@SOAPBinding
public interface SalaService {
@WebResult(name = "sala")
public abstract Sala get(@WebParam(name = "id") Long id);
}
@Service("salaService")
@WebService(serviceName = "SalaService", portName = "SalaPort", endpointInterface = "my.company.service.SalaService")
public class SalaServiceImpl implements SalaService {
public Sala get(Long id) {
return (Sala) executor.execute(SalaBusinessOperations.GET, new Long(id));
}
}
Y que CXF está configurado en el web.xml de la siguiente forma:
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
La seguridad de spring debería estar configurada, por ejemplo, con lo siguiente:
<http >
<intercept-url pattern="/services/**" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/**" access="ROLE_USER" />
<logout/>
<anonymous/>
</http>
Finalmente podemos dedicarnos a la clase que gestionará la seguridad:
public class SecurityInPasswordHandler implements CallbackHandler {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userService;
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException, AuthenticationException {
WSPasswordCallback pwdCallback = (WSPasswordCallback) callbacks[0];
int usage = pwdCallback.getUsage();
if ((usage == WSPasswordCallback.USERNAME_TOKEN) || (usage == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN)) {
String password = pwdCallback.getPassword();
if (usage == WSPasswordCallback.USERNAME_TOKEN) {
UserDetails userDetails = userService.loadUserByUsername(pwdCallback.getIdentifier());
password = userDetails.getPassword();
}
Authentication authentication = new UsernamePasswordAuthenticationToken(pwdCallback.getIdentifier(), password);
authentication = authenticationManager.authenticate(authentication); //throws AuthenticationException
SecurityContextHolder.getContext().setAuthentication(authentication);
// Return the password to the caller
pwdCallback.setPassword(password);
}
}
}
Con esto logramos gestionar passwords enviadas en plano (PasswordText) y encriptadas (PasswordDigest). En ambos casos creando un SecurityContext para que la petición tenga los roles del usuario y puedan utilizarse las anotaciones "@Secured" de Spring Security.
miércoles, agosto 26, 2009
OSGi/JTA
Existe un problema al utilizar JTA (Java Transaction API) en OSGi: la implementación JTA que ofrece el bundle "0" (el del runtime OSGi) es la del JDK y está incompleta.
Para solucionar esto hay seguir dos pasos:
1: Colocar un jar de JTA con el MANIFEST.MF de OSGi en la carpeta "lib/ext" del JRE. El Jar se puede obtener del repositorio de SpringSource: com.springsource.javax.transaction-1.1.0.jar
2: Modificar la propiedad "org.osgi.framework.system.packages" del runtime para agregar los packages de la versión "1.1.0": "javax.transaction, javax.transaction;version=1.1.0, javax.transaction.xa, javax.transaction.xa;version=1.1.0"
De hecho esta solución nos permite también ofrecer nuestros propios packages como parte del bundle "0", solo debemos agregarlos a la propiedad arriba mencionada.
Para solucionar esto hay seguir dos pasos:
1: Colocar un jar de JTA con el MANIFEST.MF de OSGi en la carpeta "lib/ext" del JRE. El Jar se puede obtener del repositorio de SpringSource: com.springsource.javax.transaction-1.1.0.jar
2: Modificar la propiedad "org.osgi.framework.system.packages" del runtime para agregar los packages de la versión "1.1.0": "javax.transaction, javax.transaction;version=1.1.0, javax.transaction.xa, javax.transaction.xa;version=1.1.0"
De hecho esta solución nos permite también ofrecer nuestros propios packages como parte del bundle "0", solo debemos agregarlos a la propiedad arriba mencionada.
viernes, marzo 13, 2009
Workast
Tanto tiempo sin escribir ha hecho de mi conciencia algo molesto, por lo que mi primer post será ni mas ni menos que una justificación!
Estos últimos meses he estado dedicado a un proyecto personal, Open Source (como no podía ser de otra manera), de microblogging.
El proyecto en cuestión es Workast y para explicarlo, nada mejor que una comparación: Twitter.
El "problema" con Twitter, aunque parezca una contradicción, es su naturaleza pública. Si bien eso lo ha llevado a donde hoy está, en las empresas no siempre es bien visto debido a que la información que allí se vuelca es visible para todos.
Así han surgido alternativas como laconi.ca con la que cualquier empresa puede instalar dentro de sus fronteras un servidor de microbloging para sus empleados. También hay servicios como Yammer, gratuitos hasta que se quieren administrar, momento en el cual cobran a la empresa por esa posibilidad.
Esto me ha llevado a plantearme la posibilidad de hacer un software yo mismo y devolver un poco de todo lo que tomamos cada día de la comunidad Open Source.
Por supuesto me ha servido también para probar diferentes alternativas a las problemáticas que van surgiendo, y aprender en el camino.
¿Que hay hecho hasta ahora?
5700+ líneas de Java mas algunas mas de javascript (en ohloh.net la cantidad de líneas javascript es desproporcionada, ya que el 99% es de bibliotecas de terceros).
¿Y que hacen esas 5700 líneas?

Mas pantallas aquí.
¿Como?
- Escribiendo lo que te pase por la cabeza en formato wiki
- Dejando eventos para formar un calendario
- Comentando lo que otros escriben
- Organizando lo que te parece útil mediante etiquetas
- Siguiendo a otra gente para que aparezca en tu vista de novedades
- Creando y escribiendo dentro de grupos, para organizar mejor las novedades
- Filtrando las novedades por etiqueta, texto o tipo de actividad
¿Y con qué tecnología está desarrollado?
- Spring framework
- Hibernate
- Hibernate Validator (JSR-303)
- RESTEasy (JAX-RS)
- jQuery
- MySQL/HSQL
- Joda Time
- info.bliki
Próximos pasos
- Investigar Spring 3.0, con su soporte para REST y Bean Validation
- Completar el roadmap
- Actualizar este blog con las novedades que vayan ocurriendo en el desarrollo
Luego de este resumen, queda por decir que ya hay una versión descargable aquí (alpha), y que todos los comentarios y sugerencias que me hagáis llegar son más que bienvenidos.
Estos últimos meses he estado dedicado a un proyecto personal, Open Source (como no podía ser de otra manera), de microblogging.
El proyecto en cuestión es Workast y para explicarlo, nada mejor que una comparación: Twitter.
El "problema" con Twitter, aunque parezca una contradicción, es su naturaleza pública. Si bien eso lo ha llevado a donde hoy está, en las empresas no siempre es bien visto debido a que la información que allí se vuelca es visible para todos.
Así han surgido alternativas como laconi.ca con la que cualquier empresa puede instalar dentro de sus fronteras un servidor de microbloging para sus empleados. También hay servicios como Yammer, gratuitos hasta que se quieren administrar, momento en el cual cobran a la empresa por esa posibilidad.
Esto me ha llevado a plantearme la posibilidad de hacer un software yo mismo y devolver un poco de todo lo que tomamos cada día de la comunidad Open Source.
Por supuesto me ha servido también para probar diferentes alternativas a las problemáticas que van surgiendo, y aprender en el camino.
¿Que hay hecho hasta ahora?
5700+ líneas de Java mas algunas mas de javascript (en ohloh.net la cantidad de líneas javascript es desproporcionada, ya que el 99% es de bibliotecas de terceros).
¿Y que hacen esas 5700 líneas?
Mas pantallas aquí.
¿Como?
- Escribiendo lo que te pase por la cabeza en formato wiki
- Dejando eventos para formar un calendario
- Comentando lo que otros escriben
- Organizando lo que te parece útil mediante etiquetas
- Siguiendo a otra gente para que aparezca en tu vista de novedades
- Creando y escribiendo dentro de grupos, para organizar mejor las novedades
- Filtrando las novedades por etiqueta, texto o tipo de actividad
¿Y con qué tecnología está desarrollado?
- Spring framework
- Hibernate
- Hibernate Validator (JSR-303)
- RESTEasy (JAX-RS)
- jQuery
- MySQL/HSQL
- Joda Time
- info.bliki
Próximos pasos
- Investigar Spring 3.0, con su soporte para REST y Bean Validation
- Completar el roadmap
- Actualizar este blog con las novedades que vayan ocurriendo en el desarrollo
Luego de este resumen, queda por decir que ya hay una versión descargable aquí (alpha), y que todos los comentarios y sugerencias que me hagáis llegar son más que bienvenidos.
viernes, junio 27, 2008
viernes, febrero 29, 2008
Spring Integration
El nuevo miembro de la famila, Spring Integration, nos permite utilizar los famosos patrones de integración dentro de nuestra aplicación, sin alejarnos del modelo de programación de Spring.
Con una configuración tan sencilla como esta se logra leer ficheros de un directorio y procesarlos:
----
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">
<message-bus/>
<channel id="fileInputChannel" />
<annotation-driven/>
<context:component-scan base-package="com.habuma.si.example" />
<file-source directory="/Users/wallsc/sucker"
poll-period="1000" channel="fileInputChannel"/>
</beans:beans>
package com.habuma.si.example;
import org.apache.log4j.Logger;
import org.springframework.integration.annotation.Handler;
import org.springframework.integration.annotation.MessageEndpoint;
@MessageEndpoint(input = "fileInputChannel")
public class FileSucker {
private static final Logger LOGGER = Logger.getLogger(FileSucker.class);
@Handler
public void suckAFile(String fileContents) {
LOGGER.debug(fileContents);
}
}
Pero todo no acaba allí. Los mensajes de entrada (los ficheros en el ejemplo anterior) se pueden enrutar hacia otras salidas: otros ficheros, colas JMS, servicios web o cualquier sistema del que dispongamos (o desarrollemos) adaptadores.
Del mismo modo, los mensajes de entrada pueden provenir de diversas fuentes.
El ejemplo anterior está amablemente copiado de Spring Integration: Meet the channel adapters. En el mismo blog de Craig Walls encontraremos mas información y ejemplos de utilización.
miércoles, agosto 01, 2007
Atlassian compra Cenqua
Quién sabe que puede surgir de esta unión: Atlassian compra Cenqua
Con aplicaciones como FishEye, Crucible y Clover, la gama de productos de Atlassian es impresionante.
jueves, julio 12, 2007
Usando Confluence wiki para documentar en forma agil
Gestionar los requisitos de un proyecto no es tarea fácil. ¿Pero no es fácil por la complejidad de obtenerlos o la de documentarlos? Son dos caras de una misma moneda y en definitiva la buena gestión de ambas tareas hará que el proyecto salga adelante con éxito o no.
Para obtenerlos hay muchas metodologías, tantas como gustos. Quizás RUP sea la mas conocida en proyectos grandes y las variantes de Agile para proyectos mas pequeños. Pero cualquiera sea la metodología elegida del proyecto, y por lo tanto de como gestionar los requisitos, las herramientas para documentar son un poco mas que penosas.
El rey de las herramientas de requisitos, RequisitePro es una arcaica mezcla de MS Word y aplicación propietaria, junto con una herramienta de reportes (SODA) en MS Word. Nada ágil por cierto. Quizás con el nuevo desarrollo Jazz todo mejore para Rational, pero para eso faltan no menos de 2 años. ¿Que nos queda entonces para documentar los requisitos? Quizás Enterprise Arquitect sea bueno para esto, pero sigue siendo un cliente pesado.
¡Bienvenidos a la web entonces! Quizás el actual sistema de documentación distribuído mas popular de estos días sea la wikipedia, por que no documentar entonces un proyecto en un wiki? El primer pensamiento en contra es por lo "desestructurada" que suele ser la información guardada en un wiki, pero alguas herramientas nos permiten agregar control sobre los formularios de entrada y edición de datos. XWiki es un ejemplo de ello, pero el ejemplo que nos ocupa hoy está desarrollado con Confluence y una serie de plugins disponibles para esta plataforma.
Paso 1. Crear plantilla de doucmento Cliente.
primero crearemos una plantilla para contener los datos de los clientes, de los cuales, por simplicidad, solo guardaremos el nombre. Para ello deberemos ir a "Browse Space/Advanced/Templates/Add New Space Template" y agregar el siguiente código bajo el nombre "Client":
| Nombre: | {page-info:title} |
{report-table}
{content-reporter:spaces=@self|type=page|scope=@self > children}
{text-sort:content:title|order=ascending}
{text-filter:data:FormName|include=Project}
{content-reporter}
{report-column:title=Nombre}{report-info:content:title|link=true}{report-column}
{report-column:title=Project Leader}{report-info:data:ProjectLeader|link=true}{report-column}
{report-column:title=Start Date}{report-info:data:StartDate|link=true}{report-column}
{report-column:title=End Date}{report-info:data:EndDate|link=true}{report-column}
{report-column:title=Status}{report-info:data:Status|link=true}{report-column}
{report-table}
(+) {add-page:template=Project|live=true}Agregar proyecto{add-page}
{set-data:FormName|hidden=true}Client{set-data}
En esta plantilla vemos una tabla con el título del documento, un reporte de proyectos y un link para agregar un nuevo proyecto. Por último el tag {set-data}, del plugin Scaffolding, nos permite identificar este documento con la propiedad "FormName=Client", lo que nos facilitará los reportes de clientes que querramos hacer.
Paso 2. Crear documento raíz.
Luego necesitamos una página en algún lugar de nuestro wiki, que contenga el listado de clientes. Para el listado utilizaremos el plugin Reporting:
{report-table}
{content-reporter:spaces=@self|type=page|scope=Clients > children}
{text-sort:content:title|order=ascending}
{content-reporter}
{report-column:title=Nombre}{report-info:content:title|link=true}{report-column}
{report-table}
(+) {add-page:template=Client|live=true}Agregar cliente{add-page}
Al final de la página se puede ver el tag {add-page} para crear documentos de tipo cliente mediante una plantilla previamente creada. Luego de salvar la página con el título "Clients", podemos agregar un cliente:
Paso 3. Agregar un cliente.
Lo que nos presentará un documento a crear en base a la plantilla "Client":
Que luego de salvarlo tendrá la siguiente apariencia:
Paso 4. Crear plantilla de proyecto
Análogamente, para crear un proyecto deberemos crear primero una plantilla, con título "Project":
| Nombre: | {page-info:title} |
| Cliente: | {report-link:content:parent > content:url}{report-info:key=content:parent}{report-link} |
| Codigo JIRA | {text-data:JiraCode|content=text|width=100px}{text-data} |
| Project Leader: | {list-data:ProjectLeader|required=true}
{user-options:groups=confluence-users}
{list-data} |
| Team Members: | {list-data:TeamMembers|type=check|multiple=true}
{user-options:confluence-users}
{list-data} |
| Start Date: | {date-data:StartDate|format=dd-MMM-yyyy}today{date-data} |
| End Date: | {date-data:EndDate|format=dd-MMM-yyyy}{date-data} |
| Status: | {list-data:Status}
{list-option}Unstarted{list-option}
{list-option}In progress{list-option}
{list-option}Awaiting approval{list-option}
{list-option}Completed{list-option}
{list-data} |
[Requerimientos|Requirements]
[Casos de Uso|UseCases]
[Iteraciones|Iterations]
[Actas de Reunión|MeetingMinutes]
[Informes de estado de proyecto|StatusReports]
{set-data:FormName|hidden=true}Project{set-data}
Esta plantilla tiene los datos propios del proyecto, incluyendo su project leader, participantes, fecha de inicio y fin y estado. Al final también hay links a los documentos que harán de padres de los requisitos, las iteraciones y todos los demás documentos que nuestra metodología requiera.
Paso 5. Crear un proyecto.
Luego de creada la plantilla de proyecto podremos crear proyectos, mediante el link en el formulario correspondiente al cliente:
Lo cual nos preguntará el título del proyecto, y luego los datos que especifica la plantilla correspondiente:
Que una vez salvada tendrá la siguiente apariencia:
Paso 6. El listado de requierimientos.
Queda como ejercicio resolver que cada proyecto contenga su propia lista de requerimientos, ya que como ven en la plantilla de proyecto el link a los requerimientos es a una página genérica, llamada "Requirements": [Requerimientos|Requirements]. La mejor posibilidad es utilizar un "space" para cada proyecto.
Pero vamos con el diseño de la plantilla de la citada página padre "Requirements":
h3.Requerimientos Funcionales
{report-table}
{content-reporter:type=page|scope=@self > children}
{text-sort:data:Date|order=ascending}
{text-filter:data:FormName|include=Requirement}
{text-filter:data:Type|include=Funcional}
{content-reporter}
{report-column:title=Título}{report-info:content:title|link=true}{report-column}
{report-column:title=Estado}{report-info:data:State}{report-column}
{report-column:title=Prioridad}{report-info:data:Priority}{report-column}
{report-column:title=Dueño}{report-info:data:Owner}{report-column}
{report-column:title=Creado}{report-info:content:creation date}{report-column}
{report-column:title=Creado por}{report-info:content:creator}{report-column}
{report-table}
h3.Requerimientos No Funcionales
{report-table}
{content-reporter:type=page|scope=@self > children}
{text-sort:data:Date|order=ascending}
{text-filter:data:FormName|include=Requirement}
{text-filter:data:Type|include=No Funcional}
{content-reporter}
{report-column:title=Título}{report-info:content:title|link=true}{report-column}
{report-column:title=Estado}{report-info:data:State}{report-column}
{report-column:title=Prioridad}{report-info:data:Priority}{report-column}
{report-column:title=Dueño}{report-info:data:Owner}{report-column}
{report-column:title=Creado}{report-info:content:creation date}{report-column}
{report-column:title=Creado por}{report-info:content:creator}{report-column}
{report-table}
(+) {add-page:template=Requirement|live=true}Agregar requerimiento{add-page}
Se ha dividido la página en requerimientos funcionales y no funcionales, pero eso es a gusto del consumidor. Lo que hay que saber es que esta página lista los documentos de tipo "Requirement", creados a su vez con la plantilla "Requirement":
Paso 7. El formulario de requirimientos:
{section}
{column:width=300px}
*Detalles*
{table:width=300px}
{table-row}
{table-cell}
Tipo:
{table-cell}
{table-cell}
{list-data:Type|required=true}
{list-option}Funcional{list-option}
{list-option}No Funcional{list-option}
{list-data}
{table-cell}
{table-row}
{table-row}
{table-cell:width=100px}
Prioridad:
{table-cell}
{table-cell:width=200px}
{list-data:Priority}
{list-option}Sin asignar{list-option}
{list-option}Alta{list-option}
{list-option}Media{list-option}
{list-option}Baja{list-option}
{list-data}
{table-cell}
{table-row}
{table-row}
{table-cell:width=100px}
Estado:
{table-cell}
{table-cell:width=200px}
{list-data:State}
{list-option}Propuesto{list-option}
{list-option}Rechazado{list-option}
{list-option}Aprobado{list-option}
{list-option}Diferido{list-option}
{list-data}
{table-cell}
{table-row}
{table-row}
{table-cell}
Dueño:
{table-cell}
{table-cell}
{list-data:Owner|required=true}{user-options:groups=confluence-users}{list-data}
{table-cell}
{table-row}
{table}
{column}
{column:width=90%}
*Descripción*
{text-data:Description|type=area|content=wiki|width=100%|height=500px}
{column}
{section}
{set-data:FormName|hidden=true}Requirement{set-data}
El resultado es un formulario:
Y un listado de todos requerimientos:
Finalmente, queda por decir que el plugin para Graphviz nos permite hacer diagramas sin salir del wiki (ver UML class diagrams in Confluence using Graphviz and DOT), lo que aumenta varios grados la agilidad de la documentación y permite gráficos como este:
martes, julio 03, 2007
Gestión de excepciones en Java
Acerca de Exception-Handling Antipatterns
Tim McCune nos propone en un "antiguo" artículo (del 2006) varias formas no hacer bien las cosas cuando de excepciones se trata. Sin copiar todo el artículo, aquí van algunos de mis antipatrones preferidos:
Log and Throw
(Duplicación de entradas de error en los logs.)
catch (NoSuchMethodException e) {
e.printStackTrace();
throw new MyServiceException("Blah", e);
}Throwing Exception
(Poco explícito)
public void foo() throws Exception
Catch and Ignore
(Se destruye la excepción y no se devuelve un error)
catch (NoSuchMethodException e) {
return null;
}Destructive Wrapping
(Se destruye la excepción original)
catch (NoSuchMethodException e) {
throw new MyServiceException("Blah: " +
e.getMessage());
}
martes, mayo 08, 2007
Semmle, al fin una idea original
Semmle es una herramienta para hacer consultas de nuestro código como si de una base de datos se tratara. La siguiente consulta
from Class cdevuelve el resultado:
where c.declaresMethod("equals")
and not(c.declaresMethod("hashCode"))
select c.getPackage(),c
para el working space "framework" de mi workspace de eclipse (Todas las clases que definen un método equals, pero no hashCode).
Basado en ".QL", un lenguaje de queries orientado a objetos, permite hacer nuestras propias clases para ampliar las funcionalidades de búsqueda.
Con una biblioteca de métricas de código tales como Lack of Cohesion of Methods y Afferent Coupling/Efferent Coupling, solo le falta una tarea ant para agregar esta herramienta al servidor de integración continua.
Al fin una idea original!
martes, diciembre 05, 2006
Nuevo tipo de configuración basado en anotaciones para Spring
Como ya saben, hay una nueva forma de configurar Spring. XML es el tipo de configuración mas utilizada para Spring, pero con este nuevo tipo, basado en anotaciones, podemos cambiar algo del "infierno xml". Dejaremos en paz la configuración mediante Groovy, al menos por ahora.
Los desarrolladores por ejemplo, pueden utilizar esto para alguna forma de configuración interna, dejando la configuración xml para infraestructura.
Pueden mirar en la web del proyecto las novedades, pero imaginen un xml como este:
<beans>
<bean id="myCompany" class="package.Company"/>
<bean id="employee" class="Employee" scope="prototype">
<property name="company" ref="myCompany"/>
</bean>
</beans>
podría hacerse así:
@Configuration
public class AppConfiguration {
@Bean
public Company myCompany() {
return new Company();
}
@Bean(scope = Scope.PROTOTYPE)
public Employee employee() {
Employee employee = new Employee();
employee.setCompany(myCompany());
return employee;
}
}
iniciando mas tarde un ApplicationContext con un bean ConfigurationPostProcessor, pudiendo incluso mezclar beans de ambos métodos:
<beans>
<bean class="package.AppConfiguration"/>
<bean class="org.springframework.beans.factory.java.ConfigurationPostProcessor"/>
<bean class="package.OtherClass">
<property name="employee" ref="employee"/>
</bean>
</beans>
Rod también nos muestra un nuevo application context (por Costin Leau):
ApplicationContext oneConfig =
new AnnotationApplicationContext(SimpleConfiguration.class.getName());
y
ApplicationContext aBunchOfConfigs =
new AnnotationApplicationContext("**/configuration/*Configuration.class");
Dejaremos a nuestra imaginación una forma de configuración mas dinámica todavía, mezclando loops y if/thens en el bean de configuración!
El código puede ser encontrado aquí. Que lo disfruten!
jueves, noviembre 30, 2006
Nuevo namespace en Spring 2.0
Una forma mas bien rara de configurar los beans de Spring parece ser la que Rod Johnson nos recuerda en una entrada de su blog.
Básicamente se puede utilizar un nuevo namespace "p" para utilizar los atributos de un XML como propiedades de los beans.
Lo que normalmente haríamos así:
<beans xmlns="http://www.springframework.org/schema/beans"Lo podríamos hacer así:
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.xsd">
<bean id="empleado" class="package.Empleado">
<property name="empresa" ref="empresa" />
<property name="horasSemanales" value="35" />
</bean>
<bean id="empresa" class="package.Empresa" />
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"Un pequeño cambio que podría hacer nuestros XMLs mas compactos, aunque no se si mas legibles.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="empleado" class="package.Empleado"
p:direccion-ref="empresa"
p:horasSemanales="35" />
<bean id="empresa" class="package.Empresa" />
</beans>
Mas ejemplos aquí.
lunes, noviembre 27, 2006
Compartir URLs
Aprovechando que es Lunes (quiero quejarme de algo) y que ya lo he leído por ahí, quiero apoyar una idea:
Sería hora que alguien pusiera orden al infierno de compartir URLs. Lo que aparece normalmente en los artículos (
miércoles, noviembre 22, 2006
Java web frameworks - Cuál elegir?
Si hay algo imposible en estos días (y desde hace tiempo) es elegir un framework web en Java. Es mas cuestión de gusto que de tecnología. Y eso molesta. Hace que la competencia (léase .NET) sean mucho mas fáciles de adoptar. Cuáles de ellos hay que seguir día a día para ver como avanzan? En mi opinión:
- Shale Basado en JavaServer Faces.
- Wicket Modelado web... en java.
- Stripes MVC clásico, pero con casi cero configuración.
- Seam Basado en EJB 3.0 y un poco pegado a productos JBOSS.
Update: Con el nuevo Seam 1.1 se pueden utilizar POJOs fuera de un servidor EJB 5.0. Incluso con Tomcat.
lunes, noviembre 20, 2006
Tecnologías con las que me gustaría trabajar (I)
Una de las tecnologías con las que me gustaría hacer algo productivo (léase: encontrar un cliente que me pague por hacer una aplicación) es JCR (Java Content Repository).
La JSR-170 especifica la forma en la que deberíamos comunicarnos con los repositorios de contenido. Los repositorios de contenido o "Content Management Systems" (CMS) como Documentum o Vignette nos permiten guardar información binaria o de texto sin tener que preocuparnos por dónde se hace, es decir si se utiliza una base de datos relacional o ficheros XML. Pero cada vendedor tiene su propio producto y su propia forma de acceder a la información, por lo que la JSR-170 trata de resolver el problema de la estandarización.
En el mundo open source, Apache JackRabbit es la implementación de referencia.
La información es organizada en nodos de un árbol, teniendo cada nodo "atributos" o "propiedades" donde guardar los datos. Por ejemplo, para guardar información podríamos (ejemplo tomado de aquí):
Session session = JackrabbitPlugin.getSession();
Node rootNode = session.getRootNode();
Node blogEntry = rootNode.addNode("blogEntry");
blogEntry.addMixin("mix:versionable");
blogEntry.setProperty(PROP_TITLE, blogEntryDTO.getTitle());
blogEntry.setProperty(PROP_BLOGCONTENT, blogEntryDTO.getBlogContent());
blogEntry.setProperty(PROP_CREATIONTIME, blogEntryDTO.getCreationTime());
blogEntry.setProperty(PROP_BLOGAUTHOR, blogEntryDTO.getUserName());
session.save();
Con lo que lograríamos crear un nodo, hacerlo versionable (uno de los servicios de la JSR), llenarlo con nuestra información y guardarlo.
Nunca se sabe, quizás en un futuro próximo convenza a alguien para utilizar Jackrabbit. Deséenme suerte!
domingo, noviembre 19, 2006
Pico y Spring
PicoContainer, alguien se acuerda de él? Es un contenedor de componentes que utiliza inyección de dependencias. Pero fue avasallado por el huracán Spring, aunque sigue teniendo sus adeptos incondicionales.
Y tan incondicionales que a veces (como otras tecnologías) se utiliza simplemente porque "es lo que sé", dejando de lado las "que no se" pero nos da mucha mas funcionalidad a nuestra arquitectura.
Pero como nada en esta vida es permanente (ni siquiera si utilizamos hibernate), y todo llega (incluso Java como open source), mi amigo Ale pudo finalmente "refactorizar" una aplicación para cambiar Pico por... Spring.
Suscribirse a:
Entradas (Atom)