Radar 2.0 is coming soon

The brand new version of the SonarQube plugin for Netbeans is almost here. Here is the list of the new features in this version.

SonarQube Runner

The main new feature is support for sonar-runner. So now is possible to run analysis locally and see the issues before committing/pushing your changes to the server.

When the sonar-runner is launched, an output tab is opened to see how the process is executing.

output-tab

 

At the end, the summary of issues is shown.

New layout

All the information is now shown in one panel with two tabs.

Summary

The summary is displayed in the first tab. The count of the issues is displayed by severity and by rule. From here, you can navigate to the list of issues.

summary

 

 

Also in this tab, you can see more information of a rule.

rule-dialog

Issues

The second tab is for the list of issues. This list has kept basically the same functionality with minor changes: the location of issue is shown in short format and, by default, a minimal number of columns is displayed. Of course, if you prefer, you can show some of the other columns.

column-menu

Additionally, now both tables have a popup menu to invoke common actions for a row.
popup-issue-list

Menu

The name of the items to invoke Radar have been renamed and grouped for better identification.

actions

Availability

All the features of this version are completed and the final release will be available after some minor fixes and some more testing.

Meanwhile, you can download the current build and give it a try. Bug reports are welcome in the page of the project: https://code.google.com/p/radar-netbeans/

Abstracción

Hablar de abstracción, no es solamente un principio que aplica dentro del Paradigma Orientado a Objetos. Cualquier sistema de software se basa en alguna forma de representación de la realidad. Se abstraen conceptos, se abstraen funciones, se abstraen algoritmos.

Hablando ahora sí de Orientación a Objetos, las abstracciones suelen ser diferentes de acuerdo al dominio del problema. Una clase Persona no tendrá el mismo detalle para una aplicación de medicina que para una aplicación web de correo. Al final estas diferencias hacen complicada la reutilización de clases.

Sería interesante un paradigma que permitiera modificar o alterar la abstracción de una clase en función del dominio en que se esté aplicando.

Modelo del dominio

Dentro del paradigma orientado a objetos, el modelo del dominio, expresado en un modelo conceptual, intenta capturar y expresar mediante conceptos (que pueden ser entendidos como clases) el contexto del problema, es decir, todo los objetos y sus relaciones que interactuan dentro de él.

Example of tag library using TLD Generator

In this post, I show an example of a tag library where TLD Generator is used. The example library contains one tag and one function, both for generation of  a random int. NetBeans is used to create the project.

Project structure

The package myrandom contains the tag handler and the function. The TLD generator jar and the JSP API are added to the libraries of the project.

Implementation of the tag

Every time the tag is invoked a random int is generated and sent to the output of its jspContext. The tag has an attribute to indicate the bound (exclusive) of the random int.

package myrandom;

import java.io.IOException;
import java.util.Random;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import tldgen.Tag;
import tldgen.TagAttribute;

@Tag
public class RandomNumberTag extends SimpleTagSupport{
    private int bound;

    @TagAttribute(required=true, runtimeValueAllowed=true)
    public void setBound(int bound) {
        this.bound = bound;
    }
    
    @Override
    public void doTag() throws JspException, IOException {
        Random r=new Random();
        getJspContext().getOut().print(r.nextInt(bound));
    }
    
}

The tag handler has two important annotations:

  • @Tag in the tag handler class. Because the tag doesn’t have an explicit name, the name will be the decapitalized name of the class with the suffix “Tag” removed. So in this example, the name of the tag in the descriptor will be randomNumber.
  • @TagAttribute is present in the setter of the bound attribute. The values of the annotations define that this attribute is required and that its value can be determined from an expression.

Function Implementation

The function is implemented like this:

package myrandom;

import java.util.Random;
import tldgen.Function;

public class RandomFunction {
    
    @Function
    public static int getRandomInt(){
        return new Random().nextInt();
    }
    
}

The method is annotated with @Function. The method has to be public and static for being visible from JSP pages. The name of the function is the name of the method, but other could be defined in the annotation.

Definition of the library

At this moment, the content of the project looks like this:

In order to group the tags and functions, we need to define a tag library. This is accomplished in a package-level annotation defined in the package-info.java file in myrandom (I used New Empty File wizard in NetBeans because the Class wizard doesn’t allow package-info name for a class).

And here is the content of this file:

@TagLibrary("http://random.org")
package myrandom;

import tldgen.TagLibrary;

In the above declaration, we define an URI for the library. The descriptor file will be taglib.tld by default. This may be changed in the annotation.

Compiling the project and generating the descriptor

Now, let’s compile the project in order to generate the descriptor with the Clean and Build option of the project.

To see the generated file, go to the Files window in NetBeans and navigate to the build/classes directory of the project.  The tld file will be in the subdirectory META-INF from where it’s read by the Servlet container at deployment time.

The content of the descriptor is:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib version="2.1" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <tlib-version>1.0</tlib-version>
    <uri>http://random.org</uri>
    <tag>
        <name>randomNumber</name>
        <tag-class>myrandom.RandomNumberTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>bound</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <type>java.lang.Integer</type>
        </attribute>
    </tag>
    <function>
        <name>getRandomInt</name>
        <function-class>myrandom.RandomFunction</function-class>
        <function-signature>int getRandomInt()</function-signature>
    </function>
</taglib>

Conclusion

In this example, I showed how to use TLD generator to produce automatically a Tag Library Descriptor. Most of the times, the descriptor is manually build with information already contained in the classes. With TLD generator I don’t have to write redundant information (DRY principle).

Resources

RandomNumbers NetBeans project in zip file

Reduciendo el archivo TLD con un listener en JAXB

Ahora con el soporte completo de la versión jsp 2.1 para archivos TLD, el descriptor generado puede ser bastante grande. Según la especificación del archivo TLD, si un elemento no se encuentra presente, toma un valor default. Tal es el caso de required para un atributo ya que por omisión es false.

Este es un fragmento para la descripción de un atributo:

       <attribute>
            <name>url</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <fragment>false</fragment>
        </attribute>       

Si se omitieran los elementos con un valor default, el xml podría quedar así:

        <attribute>
            <name>url</name>
            <type>java.lang.String</type>
        </attribute>

JAXB realiza el mapeo de todos los atributos cuyo valor sea diferente de null. Dentro del proyecto, los valores de estos atributos son copiados automaticamente de los valores de las anotaciones o en otros casos derivado del elemento que contiene la anotación. Debido a esta copia automática y que no creo que sea conveniente incluir condicionales para manejar valores default dentro del procesador, busqué y afortunadamente encontré una forma de limpiar estos atributos antes de ser transformados a XML.

Lo que hice fue utilizar un listener para el marshaller y antes de que el objeto sea convertido, explorar sus atributos y aquellos que tengan un valor que sea igual al default establecerlos en null.

Este es el código del listener:

package tldgen.processor;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;

/**
 *
 * @author Victor
 */
public class DefaultValueCleaner extends Marshaller.Listener{

    @Override
    public void beforeMarshal(Object source) {
        try {
            PropertyDescriptor[] descriptors = Introspector
                 .getBeanInfo(source.getClass()).getPropertyDescriptors();
            for(PropertyDescriptor descriptor:descriptors){
                if(descriptor.getReadMethod() != null){
                    Object currentValue=descriptor
                          .getReadMethod().invoke(source);
                    XmlElement xmlElement = descriptor
                          .getReadMethod().getAnnotation(XmlElement.class);
                    String defaultValue=null;
                    if(xmlElement != null){
                        defaultValue=xmlElement.defaultValue();
                    }
                    if(currentValue != null 
                        && currentValue.toString().equals(defaultValue) 
                        && descriptor.getWriteMethod() != null){
                            descriptor.getWriteMethod().invoke(source, new Object[]{null});
                    }
                }
            }
        } catch (IntrospectionException ex) {
            throw new RuntimeException(ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }
    }
    
}

Y así se establece en el marshaller:

   marshaller.setListener(new DefaultValueCleaner());

Sé que es ineficiente establecer el valor de un atributo para posteriormente quitarlo pero opté por esta opción por las siguientes razones:

  • Para que al procesador de anotaciones le sea transparente el uso o no de valores default.
  • Porque el proceso de generación del archivo se realiza solamente una vez durante la compilación y esto no afectará el performance de una aplicación en producción.

Generador de Tag Library Descriptor usando JAXB y Procesador de anotaciones

Estoy realizando modificaciones a mi proyecto de generación de archivos TLD (archivo XML para la definición de etiquetas para JSP).

Anteriormente el archivo TLD se generaba con Stax pero consideré que era prudente reducir el código para hacer más fácil las actividades de mantenimiento. El proyecto ya contenia clases bean cuyas propiedades eran directamente escritas a elementos o atributos XML con Stax. No había utilizado antes JAXB pero sabía que es un API para la traducción de XML/ Java (en forma bidireccional) por lo que realizé algunas pruebas preliminares y funcionaba de forma muy cercana a lo que yo esperaba.

Éste es un ejemplo de una clase en mi proyecto que contiene anotaciones de JAXB para poder ser traducida a XML.


package tldgen.processor;

import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name="taglib")
@XmlType(propOrder={"description", "displayName", "icon", "libraryVersion", "shortName", "uri", "validator", "webListeners", "tagFiles", "tagHandlers", "functions"})
public class TagLibraryInfo {
    private String uri;
    private String description;
    private String displayName;
    private String icon;
    private String libraryVersion;
    private String jspVersion;
    private String shortName;
    private List<TagInfo> tagHandlers=new LinkedList<TagInfo>();
    private List<FunctionInfo> functions=new LinkedList<FunctionInfo>();
    private ValidatorInfo validator;
    private List<WebListenerInfo> webListeners=new LinkedList<WebListenerInfo>();
    private List<TagFileInfo> tagFiles=new LinkedList<TagFileInfo>();

    protected TagLibraryInfo() {
    }
    
    public TagLibraryInfo(String uri) {
        this.uri = uri;
    }

    @XmlElement
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @XmlElement(name="display-name")
    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    @XmlElement
    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    @XmlElement(name="short-name")
    public String getShortName() {
        return shortName;
    }

    public void setShortName(String shortName) {
        this.shortName = shortName;
    }

    @XmlElement
    public String getUri() {
        return uri;
    }

    @XmlElement(name="tlib-version")
    public String getLibraryVersion() {
        return libraryVersion;
    }

    public void setLibraryVersion(String version) {
        this.libraryVersion = version;
    }

    @XmlAttribute(name="version")
    public String getJspVersion() {
        return jspVersion;
    }

    public void setJspVersion(String jspVersion) {
        this.jspVersion = jspVersion;
    }

    public ValidatorInfo getValidator() {
        return validator;
    }

    @XmlElement
    public void setValidator(ValidatorInfo validator) {
        this.validator = validator;
    }
    
    @XmlElement(name="tag")
    List<TagInfo> getTagHandlers() {
        return tagHandlers;
    }

    @XmlElement(name="function")
    List<FunctionInfo> getFunctions() {
        return functions;
    }

    @XmlElement(name="tag-file")
    List<TagFileInfo> getTagFiles() {
        return tagFiles;
    }

    @XmlElement(name="listener")
    List<WebListenerInfo> getWebListeners() {
        return webListeners;
    }
    
}

Para definir el namespace del archivo TLD, incluí el archivo package-info.java  (ver anotaciones en paquetes) con el siguiente contenido:

@XmlSchema(
namespace = "http://java.sun.com/xml/ns/javaee",
elementFormDefault = XmlNsForm.QUALIFIED)

package tldgen.processor;

import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

Aquí hay un ejemplo de una clase que invoca JAXB para realizar la conversión:


package tldgen.processor;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import tldgen.BodyContentType;

public class Main {
    
    public static void main(String[] args) throws JAXBException {
        TagLibraryInfo info=new TagLibraryInfo("http://simplehtml.net");
        info.setJspVersion("2.1");
        info.setLibraryVersion("1.0");
        info.setDescription("Tags for common HTML elements");
        info.getFunctions().add(
             new FunctionInfo("sum", "functions.Sum", "int sum(int a, int b)"));
        info.getFunctions().add(
             new FunctionInfo("sum2", "functions.Sum", "int sum(int a, int b)"));
        TagInfo tagInfo = new TagInfo("img", "my.ImageTag", BodyContentType.TAG_DEPENDENT);
        AttributeInfo attributeInfo = new AttributeInfo("src");
        attributeInfo.setType("java.lang.String");
        tagInfo.getAttributes().add(attributeInfo);
        info.getTagHandlers().add(tagInfo);
        JAXBContext context=JAXBContext.newInstance(TagLibraryInfo.class);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, 
          "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd");
        m.marshal(info, System.out);
    }
    
}

Y este es el XML generado:


run:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
    <description>Tags for common HTML elements</description>
    <tlib-version>1.0</tlib-version>
    <uri>http://simplehtml.net</uri>
    <tag>
        <name>img</name>
        <tag-class>my.ImageTag</tag-class>
        <body-content>tagdependent</body-content>
        <attribute>
            <name>src</name>
            <type>java.lang.String</type>
        </attribute>
    </tag>
    <function>
        <name>sum</name>
        <function-class>functions.Sum</function-class>
        <function-signature>int sum(int a, int b)</function-signature>
    </function>
    <function>
        <name>sum2</name>
        <function-class>functions.Sum</function-class>
        <function-signature>int sum(int a, int b)</function-signature>
    </function>
</taglib>


Ahora que estoy utilizando JAXB en el proyecto, creo que dispondré de tiempo que ocuparé en otros módulos del generador (como el procesador de anotaciones) o en otros proyectos.

El tutorial que utilice se encuentra en este enlace http://jaxb.java.net/tutorial/.