Tag Archive: Java


Ayer se publicó un exploit para la vulnerabilidad etiquetada cómo CVE-2017-5638 de Apache Struts2. Bastante crítica ya que, gracias a este exploit, podemos pasar a ejecutar comandos en la máquina donde resida la aplicación, sin importar lo que haya en medio. Así que hay que intentar parchearlo lo antes posible. Las acciones a tomar serían actualizar las librerias de Struts  y si disponemos de algún WAF, IDS o algun sistema de protección en medio, poner las firmas necesarias que paren el ataque.

En caso de no disponer de ninguna de estas y no poder actualizar las librerias de forma rápida, existe una solución rápida que podemos utilizar hasta que implantemos la definitiva.

Para ello, si tenemos una arquitectura en la que haya un apache delante del servidor de aplicaciones, podemos poner en este un filtrado que modifique la cabecera de las peticiones (donde reside el payload del exploit) y sustituya partes fundamentales de este código maligno.  Para ello, con el uso del mod_header y la siguiente línea:


RequestHeader edit Content-Type "ognl" "text/html;charset=UTF-8"

podemos parar inmediatamente el ataque sin afectar a peticiones legitimas.

Fuente: http://www.hackplayers.com/2017/03/exploit-rce-para-apache-struts-cve-2017-5638.html

Este es el primero de una serie de tutoriales que pretendo publicar acerca de ir creando aplicaciones utilizando JSF 2.1.Intentaré centrarme en cómo se hacen ciertas tareas e ir creando material nuevo que o bien sea difícil de encontrar en nuestro idioma o no se haya publicado nada al respecto en ningún otro idioma. Por ello, pondré enlaces de recomendada lectura que vean que pueden aclarar conceptos, ya que es una tontería volver a publicar algo que esté bien explicado en otro lado.

Cómo primer objetivo, será entender el ciclo de vida y el proceso de creación de una aplicación sencilla en JSF, pero antes de nada, os recomiendo este enlace de adictos al trabajo  en el que se explican todos los conceptos de JSF sin entrar en mucho detalle.

Vamos a comenzar por crear un proyecto, para esta serie de tutoriales usaremos Netbeans 7.2 que ya viene incluido con soporte para JSF 2.1, un contenedor Tomcat 7.0.22 y soporte para Java EE6. Así que creamos un proyecto del tipo web application

Después hacemos las configuraciones del proyecto y pulsando Siguiente, hasta llegar a la elección de que frameworks queremos utilizar, entonces seleccionamos Java Server Faces

Ahora que tenemos el proyecto creado, vamos a crear un pequeño formulario para crear las estadísticas de un personaje de D&D. Necesitaremos  un backend bean, su diseño en facelet (XHTML) y una clase POJO para representar al personaje. Al final del articulo, encontrareis el proyecto que tiene la siguiente estructura:

Podréis observar las impresiones en consola que se hacen en los diferentes métodos, para que veamos y comprendamos mejor el ciclo de vida JSF aplicado al funcionamiento de una aplicación.

Así que vamos a echarle un ojo a la siguiente imagen del ciclo de vida:

ciclo de vida

Cómo podemos ver, tenemos diferentes fases, que en el enlace de adictos al trabajo se explica muy bien:

  1. Restaurar los conponentes de la vista (restore view). En esta etapa el controlador construye en memoria la estructura de componentes de la página.
  2. Aplicar los valores de la petición.. (apply request values). En esta etapa se recuperan los valores de la request y se asignan a los beans de la página.
  3. Procesamiento de las validaciones (process validations). Se verifican los parámetros de entrada según un conjunto de reglas definidas en un fichero de configuración.
  4. Actualizar los valores del modelo (update model values). Los valores leídos y validados son cargados en los beans.
  5. Invocacion a la aplicación (invoke application). Se ejecutan las acciones y eventos solicitados para la página. Si es necesario se realiza la navegación.
  6. Generación de la página (render response). En esta fase se genera la página que será enviada al usuario con todos sus elementos y valores actualizados.

Entonces, cómo afecta esto a nuestro proyecto?  Al  llamar por primera vez a la página, sólo se ejecutan las dos primeras fases, debido a que no hay información de la petición para relllenar el modelo.  Ahora el usuario rellena los datos y pueden ocurrir dos cosas, que haya algún error de validación, en cuyo caso no pasará del paso de validación y no se rellenará el modelo (nunca se ejecuta el setter) ni se ejecutará ninguna acción (no se ejecuta el método save() ). O bien que todo esté correcto, entonces ejecutará el siguiente paso de actualizar los valores del modelo y si no hay ningún error en este paso, se pasarían a ejecutar las invocaciones de los eventos, en este caso, se ejecutaría el método save().

También se puede ver, que el getter en el backend bean, se llama una vez por cada propiedad de la clase POJO, ya que a través de este getter obtenemos la clase POJO, por lo que si lo estuviéramos obteniendo con una consulta a una base de datos o algún otro método costoso, habrá que guardarlo en una variable del bean.

Por supuesto, esto se puede complicar más, porque habrá que tener en cuenta que ocurre cuando hay listeners  de por medio. Pero eso lo dejamos para otra entrada.

El proyecto entero, podeis descargarlo desde aquí.

Antes de nada, Feliz año a mis escasos lectores. Últimamente ando enfangado de nuevo con Java y por supuesto, con JasperReport para sacar informes. Con la aplicación que estoy desarrollando actualmente, estoy utilizando Icefaces 3 para JSF, que da muchas facilidades para crear aplicaciones rápidamente y atractivas, tanto visual como funcionalmente. Una de las cualidades de esta versión del framework, esque todo va por AJAX. Esto, al llegar al momento de presentar al usuario los informes creados con JasperReport, supone un problema. Ya que normalmente, nos inmiscuimos en el ciclo de vida de JSF para capturar la respuesta e imprimir nuestro informe, y esto no le sienta muy bien al framework.  Así que tras unas cuantas peleas y cabezazos contra el teclado, he conseguido llegar a una solución. Dicha solución consiste en sacar el informe en otra ventana, fuera del flujo de Icefaces. Y para presentar el informe, utilizo directamente un Servlet para generar la respuesta. Paso a explicar paso a paso.

Lo primero, tenemos en nuestro archivo .xhtml lo siguiente:

<h:commandButton id="pushButtonId12"  actionListener="#{informes.pdfEdificios}"  />

Utilizamos actionListener, para que no haga ningún refresco del contenido. Ahora, el código del método encargado de manejar dicho evento es el siguiente:


public String pdfEdificios(ActionEvent event) throws IOException{
try {
Connection con = edificiosDao.getConnection();
HashMap parameters = new HashMap();
String reportsDirectory = "c:/Users/Usuario/Documents/NetBeansProjects/Proyecto/build/web/";
String jasperName = "report1.jasper";
JasperPrint jasperPrint = UtilReports.generateReport(con, reportsDirectory, jasperName, parameters);
getSessionMap().put("report",jasperPrint);
JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), "window.open('Report', 'myWindow');");
} catch (NamingException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (JRException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}

En este método, simplemente generamos el informe mediante UtilReports.generateReport(con, reportsDirectory, jasperName, parameters), una vez hecho, guardamosel objeto JasperPrint a la sesión, para poder recuperarlo después en el Servlet.
Hasta este punto, ya tenemos el informe creado y guardado en la sesión, ahora necesitamos abrir el Servlet automáticamente para llevar el informe al usuario. Para ello, Icefaces nos proporciona JavascriptContext, desde el cual podemos añadir código javascript enviado desde el servidor.
Así que mediante JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), “window.open(‘Report’, ‘myWindow’);”); enviamos el código para que se abra una ventana nueva que lleva al servlet Report. Que habremos creado previamente con el siguiente código para manejar la respuesta:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
JasperPrint report =(JasperPrint) request.getSession().getAttribute("report");
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + "report" + ".pdf;");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, report);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, response.getOutputStream());
exporter.exportReport();
} catch (JRException ex) {
Logger.getLogger(Report.class.getName()).log(Level.SEVERE, null, ex);
}finally{
request.getSession().removeAttribute("report");
}

}

Aquí, recuperamos de la sesión el objeto JasperPrint de nuestro informes y preparamos las cabeceras de la respuesta para indicar el tipo, y utilizamos un exporter para exportar el informe en el formato deseado (normalmente pdf). Por último, es importante poner en el bloque finally, la liberación del objeto de la sesión, para no sobrecargar la memoria del servidor.
Esta es la única forma que he encontrado, de momento, para poder mostrar los informes sin entrar en conflicto con Icefaces. Si alguién tiene una solución mejor, que no dude en comentarlo.

Después del parón de los exámenes volvemos a la carga. En este caso, voy a publicar la práctica de una asignatura de la carrera. El enunciado de la práctica consiste en resolver el problema del Trimino, que se trata de un tablero de X*X tamaño, siendo X una potencia de 2, y teniendo una casilla marcada. Entonces debemos recubrir el tablero utilizando triminos (un angulo de tres casillas). Una descripción más detallada del problema con su solución teórica la podemos encontrar en el siguiente enlace : http://www.dma.fi.upm.es/docencia/primerciclo/matrecreativa/juegos/poliominos/triminos/triminos.htm

Para la solución del problema se puede utilizar uno de los esquemas algorítmicos básicos, en este caso el esquema de Divide y Venceras. Que comentado por encima, consiste en coger un problema y dividirlo en partes iguales y más sencillas, hasta llegar a una solución trivial. Una vez llegada a esta solución, se va juntando las soluciones parciales para llegar a una solución completa.
La adaptación de mi esquema para este problema es el siguiente:


public static void divide(Rectangulo r){
boolean b=buscarLleno(r);
if(b) // comprobacion de si es el caso trivial
return;
// Division en subproblemas
ArrayList<Rectangulo> rects = r.dividirEnCuatro();
for(Rectangulo r1  : rects){
divide(r1); // Llamada recursiva para resolver el subproblema
}
 }

Gracias a la mayoría de las funciones auxiliares, el algoritmo  resultante es muy reducido. Pinchando aquí os podeis descargar la práctica resuelta. Podesi descargarlo de github: https://github.com/smaug1985/Trimino

Esta mañana, hablando con un viejo amigo me ha comentado algo sobre App Inventor for Android. Resulta que es una interfaz web para crear pequeñas aplicaciones para Android utilizando Drag&Drop. Cuando me lo ha comentado me he quedado bastante sorprendido, ya que es una gran iniciativa por parte de Google el acercar el acceso de desarrollo simple a más gente. Tras estar un rato toqueteandolo, os puedo decir qué me ha gustado y que no.
Vamos a empezar por lo bueno,

  1. Crear aplicaciones simples es bastante sencillo, como ya he dicho,  Drag & Drop
  2. Viene con soporte para incluir multimedia, acceso a base de datos, lanzar otras activitys, etc.
  3. Simplifca la creación de interfaces gráficas comparado con el desarrollo en eclipse.
  4. No es necesario saber ningún lenguaje de programación para crear la lógica de aplicación. Basta saber un poco de flujo, variables, etc.
  5. Puedes probarlo en tiempo real en un terminal, cada vez que guardas, se actualiza la aplicación en el terminal.

Como se puede ver, son bastantes las ventajas que tiene este sistema, pero le he encontrado las pegas siguientes:

  1. La creación de la interfaz gráfica sigue siendo demasiado simple,(despues de haber probado la de Apple, todas me lo parecen).
  2. De momento no he visto nada para poder recoger datos de un source externo a través de web (un xml, json,etc)
  3. Una vez terminada, solo puedes descargarte un APK
  4. No genera ningun tipo de fuentes con el que puedas añadir más funcionalidades.
  5. Por lo visto, aún hay problemas para subir al market.

Son pegas, que para mi son muy importantes, aunque de momento es solo una Beta, y no se sabe como nos sorprenderan estos chicos de Google de aquí a un año si siguen apostando por este camino para sus terminales.

Tras unos días jugueteando con OpenXava, he visto las posibilidades que tiene, para crear aplicaciones muy rapidamente. Así que he decidido compartir unos pequeños tips, para crear configurar un entorno que nos permita empezar a crear rápidamente aplicaciones.

Paso 1: Descargar OpenXava

El primer paso y más obvio, es descargar OpenXava, actualmente en su versión 4m2. Podeis hacerlo directamente desde su página o pulsando en el enlace que os ofrezco.

Paso 2: Descargar Eclipse

Bueno, este paso no siempre es necesario si ya teneis un Eclipse descargado, solo aseguraos de que es la versión con soporte para JEE, para poder tener las vistas y librerías necesarias. Podeis descargarlos Aqui

Paso 3: Leeros la documentación oficial

Antes de seguir paso a paso, recomiendo que leais la guía de referencia que viene en la wiki de OpenXava, sobre como crear el primer proyecto. Aunque los pasos que daremos aqui, serán algo diferentes.

Paso 4: Pasar a un workspace más quirurgico.

Si seguimos los pasos de la guía de referencia, esta nos indica que cambiemos el workspace al que viene dentro de la carpeta que descargamos, pero esto no siempre es deseable, ya sea por prefrencias personales, para mantener un orden, o porque después realmente, los ejemplos de openxava no nos interesa tenerlos por ahi pululando.
Así que vamos a personalizar nuestro entorno para empezar a crear aplicaciones OpenXava.

Del arbol de directorios de la carpeta de OpenXava que nos descargamos, dentro del workspace que viene, solo son necesarias dos carpetas de proyectos, que son la de OpenXava y OpenXavaPlantilla.
OpenXava es el proyecto base, sobre el que se apoyaran todas nuestras aplicaciones, y OpenXavaPlantilla, como su nombre indica, es la plantilla para crear nuevos proyectos.
Lo que tenemos que hacer, es copiar esas dos carpetas al workspace que queramos, ya sea el que utilizamos habitualmente, o en uno nuevo y vacio, para dedicarlo solo a las aplicaciones que desarrollemos con este framework.

Una vez tenemos esto, solo tenemos que arrancar Eclipse, y señalar a este nuevo workspace creado.  En caso de que no reconozca los dos proyectos, tendremos que crear un nuevo proyecto desde los directorios copiados.
File > New > Project… > Java Project
Y marcamos la opción de Create Project from existing source, y buscamos la carpeta, le asignamos un nombre al proyecto, preferiblemente el mismo que la carpeta contenedora (vamos, OpenXava y OpenXavaPlantilla)
Con esto, ya tenemos nuestro entorno listo.

Paso 5: Asociar el servidor tomcat a Eclipse

El último paso para tener nuestro entorno listo, es asociar el servidor tomcat a eclipse. Haciendo esto, nos ahorramos tener que estar lanzando los scripts para lanzar/parar el servidor, ya que podemos hacerlo directamente desde Eclipse, y también podremos utilizar el depurador. Lo primero, es poner la carpeta de Tomcat que viene con OpenXava en un lugar que nos resulte apropiado, eso a conveniencia de cada uno.  Ahora solo nos queda asociar el servidor, para ello abrimos la pestaña de servidores ( Window > Show view > Others… > Servers), dentro de la pestañita, pulsamos con el botón derecho y New > Server , ahora seleccionamos Apache Tomcat 6.0 y le damos a siguiente, nos pedirá configurar un RuntimeTime Enviorment para ese server, así que le indicamos un nombre y buscamos la carpeta del tomcat que copiamos, y le damos a finalizar.

Con estos pasos, ya tenemos nuestro entorno listo para crear una nueva aplicación con OpenXava y poder ejecutarla en nuestro servidor. Próximamente, publicaré como crear la primera aplicación OpenXava utilizando MySQL como gestor de base de datos.

Enlaces:

Buscando en la red herramientas de desarrollo rápido, para que nos endendamos, un tipo de herramienta que nos genere código y nos ayude a crear aplicaciones lo más rápido posible. Según la wikipedia, netbeans sería una herramienta de desarrollo rápido, y la verdad que este famoso IDE nos ayuda muchisimo a crear aplicaciones con unos pocos clicks y unas cuantas lineas de código que escribamos nosotros.

La herramienta que he encontrado, se llama OpenXava y nos permite crear aplicaciones web de gestión en escasos minutos. Nos podemos olvidar de tener que generar las tablas para la base dedatos, así como todo el tema de inserción modificación y eliminación de la base de datos. OpenXava se encarga de estas gestiones mediante JPA, aparte, también nos crea la parte de vista y controlador. Practicamente nos hace todo el trabajo, nosotros solo tenemos que escribir unas clases POJO y añadirles unas anotaciones para la persistencia, y en pocos segundos, obtendremos una aplicación funcional, ahorrandonos muchas horas y muchos quebraderos de cabeza.

Os recomiendo que le echeis un vistazo a sus ejemplos, para que veais su potencial:

http://www.openxava.org/web/guest/demos

Bueno, después de unas cuantas semanas sin postear, volvemos con las pilas recargadas. Vamos a empezar con un pequeño tutorial, para introducir informes creados con Jasper Reports en nuestras aplicaciones webs usando el framework Struts 2.

Para este tutorial, vamos a presuponer que se saben crear informes con jasper report (mejor, si utilizamos la herramienta iReport) y que tenemos ya montada una aplicación con Struts 2.

Lo primero que tenemos que hacer, es importar tantos las librerías de Jasper repots como el plugin para struts 2. El jar del pluggin lo podreís encontrar, con las librerias de struts 2, se llama struts2-jasperreports-plugin-2.1.x.jar , como veis, es muy facil identificarla.  Para ver que librerias necesitais de jasper report, podeis remitiros a la documentación oficial.

Una vez importadas las librerias, lo siguiente, sería abrir nuestro fichero de configuración struts.xml y hacer que el package en el que está la acción que nos mostrará el report, incluya el soporte para interpretar los informes:


<package name="default" namespace="/" extends="struts-default,json-default,jasperreports-default">

 <action name="Certificados">
 <result name="success" type="jasper">
 <param name="location">informes/report3.jasper</param>
 <param name="dataSource">resultados</param>
 <param name="format">PDF</param>
 <param name="reportParameters">params</param>
 </result>

 <result name="informe2" type="jasper">
 <param name="location">informes/peticion_justificacion.jasper</param>
 <param name="connection">conexion</param>
 <param name="format">PDF</param>
 <param name="reportParameters">params</param>
 </result>
 </action>
 </package>

Como podemos ver, el paquete, extiende la clase jasperreports-default , con lo que las acciones podrán devolver resultados del tipo jasper, para poder imprimir nuestros informes.  Después de eso, podeis ver dos acciones, y es de notar que el tipo es  jasper, con lo cual, ya se entiende, que es un informe.  Las acciones,  tienen que llevar los siguientes parámetros de forma obligatoria:

  • location: es la localización del informe compilado que queremos que se ejecute como resultado.
  • datasource : podemos especificar el nombre de una propiedade de la acción, que deberá devolver una colección de beans, de la cual queremos extraer las propiedades.
  • connection: pondremos el nombre de una propiedade de la acción que devuelva una conexión a la base de datos, que usará el report para conectarse y ejecutar la sentencia que lleve este implicita.

Entre datasource y connection, solo hace falta especificar una de las dos. En el código de ejemplo, hay dos parámetros más:

  • format: Será el formato de salida del informe, podrá ser PDF o cualquier otro que permita Jasper Report, (ver la documentación oficial)
  • reportParameters: Será un mapa, con una lista de parámetros que se utilizará dentro del report, por ejemplo para parametrizar la consulta del report.

Ahora solo nos queda ver, el código de una acción, que como veremos a continuación, puede llegar a ser muy simple:


public class CertificadosAction extends ActionSupport implements ApplicationAware{
private Collection resultados;
private Map<String,Object> app;
private Map<String,Object> params;

@Override
public String execute(){
ProxyBD proxy = (ProxyBD)app.get("db"); // Un objeto que envuelve la conexión
resultados = proxy.getDatos();
params = new HashMap<String,Object>();

params.put("param1","dato 1");
params.put("fecha",new Date());

return SUCCESS;
}

public Connection getConexion(){
ProxyBD proxy = (ProxyBD)app.get("db");
return  proxy.getConnection();
}

public Map getParams(){
return params;
}
public Collection getResultados(){
return resultados;
}

public void setApplication(Map<String, Object> arg0) {
app = arg0;
}
}

Como se puede ver, para un informe sencillo, el código de la acción es realmente reducido, incluso para informes más complejos, no es necesario complicar mucho más la acción. Se puede ver, que tiene varios atributos,  uno los resultados, que sería el datasource para el informe, otro son los parámetros adicionales que se desean enviar al report y por último, la conexión, de forma que esta misma acción, se puede reutilizar para diferentes report, dependiendo del resultado de la acción.

De momento, esto es todo por ahora,  en próximas entregas veremos como tratar con subreports sin perder la cabeza y algún que otro truquillo más.

Enlaces:

Y aún estoy de exámenes, hasta el viernes, que estaré por fin libre de este gran peso, y esperemos, que con resultados satisfactorios.

Mientras tanto, para no tener abandonado el blog, ahi va mi pequeña aportación semanal.
Normalmente, en toda base de datos de una aplicación que se precie, suele tener algún campo del tipo Date. Pues Mysql, por norma general, si el campo no puede ser NULL, le asigna como valor por defecto ‘0000-00-00’, un valor que puede parecer inocente en un principio, pero si ya tenemos una aplicación Java funcionando o de una nueva, al intentar recuperarla del ResultSet, obtendremos una bonita excepción del tipo :


java.sql.SQLException: Value '0000-00-00' can not be represented as java.sql.Date

Aunque la primera solución que se nos puede pasar por la cabeza, después de las ganas de querer apalear la máquina virtual de Java, sería modificar la base de datos y cambiar todos los registros cuyo valor sea ‘0000-00-00’ por un NULL o si no podemos, por la fecha más antigua posible (Esa de 1970 que bla bla bla).  Ahora bién, esto puede ser bastante engorroso, aparte de que posiblemente nos pueda causar problemas posteriores con otras zonas de la aplicación. Pero por suerte, tenemos una solución bastante más asequible, rápida y escalable. Es tan sencillo, como pasarle como parámentro al conector de Mysql para java lo siguiente : “zeroDateTimeBehavior=convertToNull”, quedando por ejemplo, la línea de la conexión algo así:

"jdbc:mysql://localhost:3306/pagos?user=root&password=root&zeroDateTimeBehavior=convertToNull";

De esta forma, automaticamente, el conector, cada vez que recupere un registro del tipo fecha que sea cero, lo que hará será darle al ResultSet un null, solucionando así gran parte de nuestros problemas.

Si quereis aprender más sobre toda la funcionalidad del conector de Mysql, siempre podeis dirigiros a la documentación oficial del conector.

Cuando estamos tratando con consultas a bases de datos en Java, sobre todo en un proyecto Enterprise, podemos llegar a hablar de consultas kilométricas que acceden a varias tablas con una cantidad ingente de campos. Y si pretendemos hacer las cosas bien, utilizando los PreparedStatement, por temas de seguridad , legibilidad y reutilización, seguramente, si tenemos que hacer algún cambio en una sentencia, añadiendo o eliminando algún parametro, nos podemos encontrar con que deberemos cambiar bastantes líneas de código, en vez de la que debemos añadir/quitar solamente.

Vamos a poner un ejemplo práctico, si tenemos la siguiente sentencia de consulta a una base de datos:


SELECT usuarios.*, roles.descripcion, posts.entrada, posts.titulo FROM usuarios,descripcion,posts
WHERE usuarios.rolId=roles.id AND posts.userId=usuarios.id AND posts.fecha BETWEEN ? AND ?
AND usuarios.userId = ? AND posts.id = ? AND posts.isVisible = ? AND posts.visitas > ?

Esta consulta no es muy extensa, pero nos valdrá para ilustrar el supuesto práctico.  Ahora tendríamos el siguiente código Java para realizar la consulta:


String consulta = //la consulta de arriba;
PreparedStatement stmt = conexion.prepareStatement(consulta);
stmt.setDate(1,desde);
stmt.setDate(2,hasta);
stmt.setInt(3,userId);
stmt.setInt(4,postId);
stmt.setBoolean(5,isVisible);
stmt.setInt(6,numVisitas);

ResultSet result = stmt.executeQuery();

Esta sería la forma normal de trabajar, pero si en un futuro tenemos que realizar alguna modificación a la consulta,
por ejemplo, quitamos la necesidad de las dos fechas en la condición. Nos encontramos conque deberemos eliminar dos lineas de código
y modificar las otras 4, para que cuadre el número del parámetro. Esto no nos llevaría mucho tiempo en una sola consulta, pero si hubiera que modificar más consultas (porque se haya eliminado un campo que se utilizaba en todas las consultas como condición),  o si tratamos de una consulta con 20 parámetros por ejemplo, ya requeriría más tiempo empleado en el mantenimiento.

Así, que para ahorrar en tiempo de mantenimiento futuro, lo que podemos hacer, es insertar una variable que vaya aumentando con cada inserción de un nuevo parámetro, de esta forma,  no  tenemos que estar trabajando con el número de parámetro que se le pasa a los métodos set… y nos ahorraremos más de un dolor de cabeza. El código resultante, sería algo así:

int parameter=1;
String consulta = //la consulta de arriba;
PreparedStatement stmt = conexion.prepareStatement(consulta);
stmt.setDate(parameter++,desde);
stmt.setDate(parameter++,hasta);
stmt.setInt(parameter++,userId);
stmt.setInt(parameter++,postId);
stmt.setBoolean(parameter++,isVisible);
stmt.setInt(parameter++,numVisitas);

ResultSet result = stmt.executeQuery();

De esta forma, ya nos podemos despreocupar, de que el número de un parámetro coincida
con el de la consulta, y si hacemos alguna modificación en la consulta, no tendremos que realizar demasiado
cambio en el código y por último, nos facilita el copy & paste que tanto gusta a algunos.
Para finalizar, solo hay que recordar que el número de parametros, empieza con 1, no con 0 como los arrays, y que el número
de parametros que requiere la consulta, debe ser igual al número de parámetros que le pasamos, uno más o uno menos
resultará en una excepción.