Tips, Soluciones y Novedades en Tecnología

14/11/2016

Manejando Exception






En java las excepciones son muy importante, y si no la usas correctamente, pues no lo estas aprovechando al máximo.









Vamos a trabajar explicitamente sobre la Clase Exception



El uso basicamente depende del escenario y el grado de control que se requiere utilizar.



Para este tutorial vamos a usar nuestra exception que herede de la Clase Exception



Como recomendacion debemos crear una única clase de exepcion para nuestra aplicacion, en este caso crearemos STExeption



Esta clase debe heredar de Exeption como se indica a continuacion



public class STException extends Exception {

private String code;
private int status;

public STException() {
super();
}

/**
     *
     * @param message
     */
public STException(String message) {
super(message);
}

public STException(int status,String message) {
super(message);
this.status=status;
}

/**
     * Este metodo se inicia cuando se invoca desde las excepciones que lo
     * extienden
     *
     * @param status
     * @param code
     * @param message
     */
public STException(int status, String code, String message) {
super(message);
this.status = status;
this.code = code;
}

/**
     *
     * @param status
     * @param message
     * @param rootCause
     */
public STException(int status, String message, Throwable rootCause) {
super(message, rootCause);
this.status = status;
}

/**
     *
     * @param rootCause
     */
public STException(Throwable rootCause) {
super(rootCause);
}

public STException(String message, Throwable rootCause) {
super(message, rootCause);
}

public String getCode() {
return code;
}

public int getStatus() {
return status;
}

}






Como podemos ver la clase tiene 2 atributos Code y Status, estos atributos almacenan los valores que producen las excepciones Hijos, mas adelante veremos que valores y como utilizarlos.





STException debe ser nuestra clase padre de todas nuestras excepciones que queramos controlar. por ejemplo si queremos tener excepciones de validacion, conversion, base de datos, etc.  esta debe implementar de STException. a continuacion veremos 2 excepciones hijas que implementen de STException.





public class AccessDeniedException extends STException {

public static final int status = 401;

public AccessDeniedException() {
super();
}

/**
     *
     * @param message
     */
public AccessDeniedException(String message) {
super(status, message);
}

public AccessDeniedException(String code, String message) {
super(status, code, message);
}

/**
     *
     * @param message
     * @param rootCause
     */
public AccessDeniedException(String message, Throwable rootCause) {
super(status, message, rootCause);
}

/**
     *
     * @param rootCause
     */
public AccessDeniedException(Throwable rootCause) {
super(rootCause);
}
}



En este caso la excepcion es para validar la autenticación, podemos ver que el atributo status tiene un valor por defecto de 401, este codigo se estableció segun: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes



De esta forma cuando lanzamos la excepcion de Autenticación  ya no tendremos que agregar el codigo para respuesta al navegador.





Otro ejemplo de implementacion es:



public class DatabaseException extends STException {

public static final int status = 500;
public DatabaseException() {
super();
}

/**
     *
     * @param message
     */
public DatabaseException(String message) {
super(status,message);
}
public DatabaseException(String code,String message) {
super(status, code, message);
}

/**
     *
     * @param message
     * @param rootCause
     */
public DatabaseException(String message, Throwable rootCause) {
super(status, message, rootCause);
}

/**
     *
     * @param rootCause
     */
public DatabaseException(Throwable rootCause) {
super(rootCause);
}
}



En este caso se estableció un codigo de 500, para indicar que se ha producido un error en el servidor de base de datos o servidor de aplicaciones, los codigo pueden variar segun el grado de precición de la validación.



Ahora veremos como encapsular nuestra excepcion ya sea el tipo que sea todos tiene un solo tipo de salida que es STException.



 @Override
public void remove(String token, int id) throws STException {
log.debug("add ({},{})", token, id);
UserInfo info = null;
try {
info = SessionManager.getInstance().get(token);
TrainingDAO.getInstance().remove(info, id);
} catch (AccessDeniedException e) {
log.error("remove ({},{})", e.getLocalizedMessage(), e);
throw new AccessDeniedException ("Mensaje de objeto autenticacion");
} catch (HibernateException e) {
log.error("remove ({},{})", e.getLocalizedMessage(), e);
throw new DatabaseException("Mensaje de error en la base de dados");
}
log.debug("add void");
}



Compo podemos ver podemos controlar muchas excepciones pero como respuesta daremos como salida solo a STException, por que todas las demas excepciones heredan de esta.



De este modo nos evitamos colocar todas las excepciones en nuestras interfaces y lo simplificamos con una sola.





Ahora supongamos que debemos dar respuesta a la peticion y devolver en el request, solo tendremos que lanzar este control.



    @POST
@Path("/add/{id}")
public ResponseData add(TrainingPartaker item) throws STException {
try {
TrainingPartaker nwitem = STTrainingPartaker.getInstance().add(null, item);
return STMessage.success(nwitem);
} catch (STException e) {
throw new GenericException(e);
} catch (Exception e) {
throw new GenericException(e);
}
}



Ahora si queremos determinar especificamente que excepcion es la que há lanzado nuestra aplicación, solo base de colocarlo de esta forma y sabremos a que excepcion pertenece.



       @POST
@Path("/add/{id}")
public ResponseData add(TrainingPartaker item) throws STException {
try {
TrainingPartaker nwitem = STTrainingPartaker.getInstance().add(null, item);
return STMessage.success(nwitem);
} catch (AccessDeniedException e) {
throw new GenericException(e);
} catch (DatabaseException e) {
throw new GenericException(e);
} catch (ParseException e) {
throw new GenericException(e);
} catch (ConverterException e) {
throw new GenericException(e);
} catch (Exception e) {
throw new GenericException(e);
}
}







Como podemos ver, las excepciones es recomendable encapsularlas al momento de generarlas y desencapsularlas el momento cuando queremos usarlas, o no desencapsularlas si queremos manejar mensajes de proposito general.





Espero que los ayude a manejar mejor sus excepciones en sus aplicacion.








22/02/2016

Anotaciones en Java Reflection



Asi como las clases y palabras reservadas en java, existe un tipo especial para definir  como anotación, es allí donde uno puede crear sus propias anotaciones para usos específicos.











Las anotaciones es una especificación en cual busca simplificar el código usado en java, así mismo junto con Java Reflexion, nos brinda la potencia de poder tener control total de las anotaciones que implementemos.



Las anotaciones son amplia mente utilizadas por la mayorías de frameworks java, por ejemplo Spring Framework con sus @Autowired, Hibernate también hace uso de las anotaciones para simplificar el mapeo de los objetos, evitando escribir en archivos xml.



Vamos a crear nuestra propia anotación y vamos a ver también como podemos usarlo, un caso tipico seria en una validación.



No necesitamos dependencias ya que es una implementan nativa de java.








 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
*
* @author Reynaldo Claros
* @email reyiclaros@gmail.com
*/
@Documented
@Target(ElementType.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnotacion {

String mensahe() default "";

}







Esta anotación lo que va hacer es capturar el valor que tiene asignado el campo de la clase que definiremos.



La clase MyClase lo definimos.








 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

/**
*
* @author Reynaldo Claros
* @email reyiclaros@gmail.com
*/
public class MyClase {

@MyAnotacion(mensahe = "Campo name")
private String name;
@MyAnotacion(mensahe = "Campo dni")
private String dni;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDni() {
return dni;
}

public void setDni(String dni) {
this.dni = dni;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}





Después solo tenemos que instanciar la clase asignarle los parámetros y enviarlo a un método para su procesamiento.








 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

public static void main(String[] args) {
MyClase clase = new MyClase();
clase.setName("Java for Developers");
clase.setDni("19283282222");
clase.setAge(2);
/**
* Procesamos
*/
procesandoAnotacion(clase);
}

private static void procesandoAnotacion(Object obj) {
Class cl = obj.getClass();
try {
for (Field field : cl.getDeclaredFields()) {
field.setAccessible(true);
try {
for (Annotation anno : field.getDeclaredAnnotations()) {
if (anno.annotationType() == MyAnotacion.class) {
MyAnotacion myanotacion = (MyAnotacion) anno;
System.out.println("Mensaje de la anotacion : " + myanotacion.mensahe());
System.out.println("Nombre del campo : " + field.getName());
System.out.println("Valor del campo : " + field.get(obj).toString());
System.out.println("----------------------------");
}
}
} catch (IllegalAccessException | IllegalArgumentException | SecurityException e) {
System.out.println(e.getMessage());
}
}
} catch (SecurityException | IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}





El resultado es:








1
2
3
4
5
6
7
8

Mensaje de la anotacion : Campo name
Nombre del campo : name
Valor del campo : Java for Developers
----------------------------
Mensaje de la anotacion : Campo dni
Nombre del campo : dni
Valor del campo : 19283282222
----------------------------







Como podéis ver, las anotaciones son una poderosa herramienta, cuando queremos hacer nuestros propios validadores y otros cometidos.



Saludos cordiales.

Fechas con JodaTime



Una de las cosas con las que he tenido que trabajar son las fechas, hacer operaciones con la librería Date del API de java, digamos que no es del todo amigable y fácil de hacerlo si queremos encontrar diferencias exactas en manejo de fechas.







Vamos a ver una librería que nos facilita mucho ese trabajo, hagamos los escenarios para el cual nos puede servir:






  • Encontrar Nro de días entre fechas

  • Diferencia entre fecha mayor y menor

  • Encontrar nro de meses entre dos fechas

  • Determinar intervalos entre 2 fechas. etc






Los que hace la librería JodaTime es tomar como base una fecha de partida (1970), a aparir de allí, hacer la contabilización y puede hacer todo tipos de operaciones lo que respecta a manejo de fechas.








Para incluirlo en nuestro proyecto solo basta con declarar en nuestro pom.xml o bajar directamente el .JAR (Lib) y agregarlo manualmente a nuestra librería.








1
2
3
4
5
6
7

<!-- JODA TIME -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.2</version>
</dependency>
<!-- /JODA TIME-->





Hagamos un ejemplo para determinar los periodos entre 2 fechas, el código para dicha operación seria el siguiente.










 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

private static void calcularNroPeriodos(Date fecha1, Date fecha2) {
if (fecha1 != null && fecha2 != null) {
/**
* Parseamo al formato de fecha que maneja JodaTime
*/

DateTime myfecha1 = new DateTime(fecha1);
DateTime myfecha2 = new DateTime(fecha2);

/**
* Asignamos un instante es decir desde 1970 a la fecha actual
* registrado.
*/
Instant instanteFecha1 = myfecha1.toInstant();
Instant instanteFecha2 = myfecha2.toInstant();
/**
* Determino el intervalo, pero para ello debo saber que la fecha1
* sea mayor a la fecha2, para poder aplicar la regla. Con el
* instante de fecha ya se puede determinar que fecha es mayor
*/
if (instanteFecha1.getMillis() > instanteFecha2.getMillis()) {
Interval intervalTask = new Interval(instanteFecha1, instanteFecha2);

/**
* Y por ultimo defino el periodo
*/
Period periodo = intervalTask.toPeriod(PeriodType.dayTime());

/**
* Pueden explorar todos los metodos que tiene Periodo
*/
System.out.println("Nro de Años entre las fechas :" + periodo.getYears());
System.out.println("Nro de Meses entre las fechas :" + periodo.getMonths());
System.out.println("Nro de Dias entre las fechas :" + periodo.getDays());
}
}
}





Mas ejemplos en la pagina oficial del proyecto: http://www.joda.org/joda-time/



Saludos cordiales.



Plantillas en Netbeans



Muchos developers usan Netbeans IDE, como favorito, pero muchos también no aprovechan al máximo las herramientas y plugins que trae netbeans para facilitarle al developer en su trabajo.





Cuando creamos clases, interfaces, etc, muchas veces replicamos y tenemos que copiar de otra clase todo el contenido para poder avanzar.



Netbean IDE, tiene un modulo que se encarga de administrar plantillas, es decir puedes modificar las ya existentes y también puedes crear tus propias plantillas, según tu propósito.







En la imagen se puede apreciar una serie de plantillas por tipo y lenguajes, hay una variedad de plantillas disponibles para agilizar nuestros desarrollos, por ejemplo yo tengo un grupo de plantillas que me facilitan mi desarrollo.









Para acceder los hacemos: Tools -> Templates 

Al hacer click en Java Class, se mostrar la siguiente Plantilla.








 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<#assign licenseFirst = "/*">
<#assign licensePrefix = " * ">
<#assign licenseLast = " */">
<#include "${project.licensePath}">

<#if package?? && package != "">
package ${package};

</#if>
/**
*
* @author ${user}

 */
public class ${name} {

}





Por eso cuando le dan agregar una clase siempre te sale con esa estructura.



Entonces como podéis ver allí pueden agregar todo el código que necesiten para no tener que estar repitiendo, pero ojo, tienen que crear su propia plantilla por ejemplo ClaseDAO.

A continuación podemos ver mi plantilla personalizada, ustedes tienen que hacerlo según su propósito








 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

<#assign licenseFirst = "/*">
<#assign licensePrefix = " * ">
<#assign licenseLast = " */">
<#include "${project.licensePath}">

<#if package?? && package != "">
package ${package};

</#if>
/**
*
* @author ${user}
* @email reyiclaros@gmail.com
* @Company Java for developers
* @website www.claros-java.blogspot.com
*/
public class ${name} {

private static String getNameClass(){
return this.getClass.getSimpleName();
}

}





Saludos cordiales.



Plantillas con Freemarker








Cuando programamos, aveces necesitamos utilizar Plantillas, como por ejemplo correos electrónicos, los que estan comenzando suelen usar un  String para concatenar el cuerpo y descripciones del asunto y si lo han estado usando pues a partir de leer este post, van a comenzar a utilizar una  plantilla para ese cometido.









Una de las librerías que mas difundidas para el uso de plantillas es Freemarker ahora un proyecto mas de la Apache Software Fundation.



A continuación vamos hacer un ejemplo del uso de esta motor de plantilla, haremos una plantilla para enviar un correo electrónico, es decir el body del correo.



 TemplateEmail.ftl






<html>
<head>
<title>Notificacion de Java for developers</title>
</head>
<body>
<h1>Estimado :${username} </h1>
<p>
<h2>Asunto : ${subjet}</h2>
<hr>
<i>
<a href="http://claros-java.blogspot.com">Java for Developers</a>
<br>
</i>
</body>
</html>




Primero necesitamos obtener las dependencias del repositorio de maven, para los que no usan maven lo pueden bajar directamente aquí: Jar Freemarker



Una vez descargado los descomprimen adjuntan como JAR externo a su libreria de su proyecto.



Continuando agregamos la dependencia a nuestro pom.xml


 




<!-- TEMPLATE-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>







Con esto ya tenemos el .jar en nuestro proyecto, ya sea por maven o adjuntando el .jar manualmente.

el código para un HTML parseado es el siguiente:






 public static String loadTemplate() throws Exception {
String templateString = "";
Writer out = new StringWriter();
try {
Template template = getConfig().getTemplate("TemplateEmail.ftl");
Map<String, Object> data = new HashMap<>();
data.put("username", "Reynaldo Claros");
data.put("subjet", "Ejemplo de Plantilla con Freemarker");
template.process(data, out);
templateString = out.toString();
out.close();
} catch (java.io.IOException | freemarker.template.TemplateException e) {
log.error("error ({})", e.getMessage());
throw new IOException(e.getMessage());
}
return templateString;
}




Una vez ejecutado, la cadena que se obtiene es la siguiente:






<html>
<head>
<title>Notificacion Java for Developers</title>
</head>
<body>
<h1>Estimado :Reynaldo Claros </h1>
<p>
<h2>Asunto : Ejemplo de Plantilla con Freemarker</h2>
<hr>
<i>
<a href="http://claros-java.blogspot.com">Java for Developers</a>
<br>
</i>
</body>
</html>




Saludos cordiales.


Multiples SpringContexts en web.xml



Cuando tenemos una aplicación bastante grande, en la configuración de Spring, este caso voy a ponerlos un escenario que que servirá de propósito general cuando necesitemos cargar archivos de spring automáticamente cuando se inicia la aplicación














La imagen muestra una aplicación con muchos archivos de configuración, una forma optima administrarlos es agrupándolos por carpetas, pero necesitamos decirle a spring que cargue todos los archivos que hemos definido.





A continuación vamos a ver como podemos pasarlo mediante parámetro a spring,  definiendo  en el web.xml de nuestra aplicación, ya que esta es la que se inicia al iniciar la aplicación, una vez iniciada spring sera capaz  de leer todos los SpringContexts, de una determinada carpeta, así no sera necesario especificar en el web.xml archivo por archivo, si no que mediante un comodín (*) y spring  cargara todos los archivos con la expresión similar.








<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/springfiles/applicationContext*.xml
</param-value>
</context-param>






Saludos cordiales.

26/09/2015

Pentaho – solutions


En este post veremos un servicios de pentaho y como sacar provecho de ello para poder integrarlo en nuestra aplicación empresarial

para ello tenemos que analizar  un servicio especifico:

 

/pentaho/SolutionRepositoryService

 

este servicio tiene una serie de parámetros que espera recibir al ser llamado.

  • component: getSolutionRepositoryDoc
  • component: getAcl
  • component: setAcl
  • component: createNewFolder
  • component: delete

 

por cada tipo de componente que queremos solicitar hay otros parámetros que debemos enviar

getSolutionRepositoryDoc         userid password
getAcl solution path filename   userid password
setAcl solution path filename aclXml userid password
createNewFolder solution path name   userid password
delete solution path name   userid password

 

Paso 1: Listar el repositorio de pentaho: pentaho/SolutionRepositoryService?component=getSolutionRepositoryDoc &userid=xx&password=xxx

el resultado es en formatto xml.

 

<?xml version="1.0" encoding="UTF-8"?><repository path="/pentaho-solutions" product-id="POBS" version-build="49886" version-major="4" version-milestone="stable" version-minor="5" version-relase="0">
    <file description="Links for updating the system settings and web service examples" isDirectory="true" lastModifiedDate="1433353698531" localized-name="Admin Services" name="admin" visible="false">
        <file description="Tools for the content Repository" isDirectory="true" lastModifiedDate="1433353698529" localized-name="Content Repository Admin" name="Content" visible="false"/>
        <file description="resources" isDirectory="true" lastModifiedDate="1433353698533" localized-name="resources" name="resources" visible="false">
            <file description="metadata" isDirectory="true" lastModifiedDate="1433353698553" localized-name="metadata" name="metadata" visible="false"/>
        </file>
        <file description="Removes files from the content repository that are more than 180 days old." isDirectory="false" lastModifiedDate="1335477348000" localized-name="Clean Repository" name="clean_repository.xaction" param-service-url="/pentaho/ServiceAction?solution=admin&amp;path=&amp;action=clean_repository.xaction&amp;component=xaction-parameter" url="/pentaho/ViewAction?solution=admin&amp;path=&amp;action=clean_repository.xaction" visible="true"/>
        </file>
    <file description="cdb" isDirectory="true" lastModifiedDate="1436830518047" localized-name="cdb" name="cdb" visible="false">
        <file description="queries" isDirectory="true" lastModifiedDate="1436830518077" localized-name="queries" name="queries" visible="true"/>
        <file description="saiku" isDirectory="true" lastModifiedDate="1436830518013" localized-name="saiku" name="saiku" visible="true"/>
    </file>
    <file description="CDE" isDirectory="true" lastModifiedDate="1443127490702" localized-name="cde" name="cde" visible="true">
        <file description="components" isDirectory="true" lastModifiedDate="1437605131147" localized-name="components" name="components" visible="true"/>
        <file description="widgets" isDirectory="true" lastModifiedDate="1437605131393" localized-name="widgets" name="widgets" visible="true">
            <file description="" isDirectory="false" lastModifiedDate="1437605131329" localized-name="SampleWidget" name="sample.wcdf" url="/pentaho/content/pentaho-cdf-dd/Render?solution=cde&amp;path=widgets&amp;file=sample.wcdf" visible="true"/>
        </file>
    </file>
</repository>

 

  Para como se puede apreciar el xml contendrá todos los archivos y carpetas del repositorio de pentaho

y las propiedades son las siguientes:

  • lastModifiedDate
  • name
  • localized-Name
  • param-service-url
  • url
  • isDirectory
  • isVisible

Siguiendo los nodos y con estos parámetros podemos diferencias si son carpetas o archivos.

 

Ahora supongamos que en la vista nuestro frontend utiliza JSON, acá tendremos una trabajito de procesar este xml y pasarlo a json, filtrando solo los archivo y carpetas visibles

al repositorio.

un resultado en JSON considero necesario para tener todo a la mano seria este JSON.

 

{
    "data":
            [
                {
                    "localizedName": "Testing",
                    "visible": true,
                    "isDirectory": true,
                    "description": "Testing",
                    "name": "Testing",
                    "path": "",
                    "children": [{
                            "localizedName": "saiku",
                            "visible": true,
                            "isDirectory": true,
                            "description": "saiku",
                            "name": "saiku",
                            "path": "",
                            "children": [{
                                    "localizedName": "miarchivo",
                                    "visible": true,
                                    "isDirectory": false,
                                    "description": "",
                                    "name": "miarchivo.saiku",
                                    "path": "saiku",
                                    "editUrl": "/pentaho/content/saiku-ui/index.html?solution=Testing&path=saiku&action=miarchivo.saiku&dimension_prefetch=false&mode=edit&biplugin=true#query/open/miarchivo.saiku",
                                    "lastModifiedDate": "1442801950932",
                                    "param-service-url": "",
                                    "leaf": true,
                                    "type": "saiku",
                                    "solution": "Testing",
                                    "url": "/pentaho/content/saiku-ui/index.html?solution=Testing&path=saiku&action=miarchivo.saiku&dimension_prefetch=false&mode=view&biplugin=true#query/open/miarchivo.saiku"
                                }],
                            "lastModifiedDate": "1442806447920",
                            "expanded": false,
                            "solution": "Testing"
                        }],
                    "lastModifiedDate": "1442801913318",
                    "expanded": false,
                    "solution": "Testing"
                }]
    ,
    "success": true,
    "expanded": false
}

Con este JSON, ya podemos construir un TreePanel en cualquier framework javascript

image

Las extensiones en pentaho son:

Set<String> map = new HashSet<>();
       map.add("saiku");
       map.add("prpt");
       map.add("xcdf");
       map.add("wcdf");
       map.add("adhoc");
       map.add("xaction");
       map.add("waqr");

con ello podemos colocar un icon en función al tipo.

 

PASO 2 :  Crear una carpeta en el repositorio de pentaho :

  pentaho/SolutionRepositoryService?component=createNewFolder&solution=SOLUTION&path=PATH&name=NAME &userid=xx&password=xxx

      

al realizar esta peticion se recibirá un mensaje en XML

  <result>true</result>  si se creo correctamente la carpeta

 y  <result>false</result> si no pudo crearlo

 

interpretamos este resultado y enviamos al cliente.

 

PASO 3:  Eliminar un archivo o carpeta del repositorio de pentaho

  pentaho/SolutionRepositoryService?component=delete&solution=SOLUTION&path=PATH&name=NAME &userid=xx&password=xxx

al realizar esta peticion se recibirá un mensaje en XML

  <result>true</result>  si se elimino

 y  <result>false</result> si no pudo eliminarlo

 

interpretamos este resultado y enviamos al cliente.

 

PASO 4: Obtener los permisos de una carpeta o archivo de pentaho

pentaho/SolutionRepositoryService?component=getAcl&solution=SOLUTION&path=PATH&filename=FILENAME&userid=xx&password=xxx

la respuesta es un xml con la siguiente estructura

<?xml version='1.0' encoding='UTF-8'?>
<acl>
    <entry role='Admin' permissions='-1'/>
    <entry role='cto' permissions='-1'/>
    <entry role='dev' permissions='3'/>
    <entry role='Authenticated' permissions='1'/>
</acl>

 

interpretamos este xml y podemos modificarlo y enviarlo al paso siguiente.

 

PASO 5:  dar o quitar permiso a una carpeta o archivo de pentaho

pentaho/SolutionRepositoryService?component=setAcl&solution=SOLUTION&path=PATH&filename=FILENAME&aclXml=XML_IGUAL_AL_ANTERIOR&userid=xx&password=xxx

 

al realizar esta peticion se recibirá un mensaje en XML

  <result>true</result>  si se añadió los nuevos permisos

 y  <result>false</result> si no pudo añadir los permisos

 

interpretamos este resultado y enviamos al cliente.

 

Como observamos los pasos anteriores, es fácil integrar pentaho a nuestra aplicación empresarial y no depender de su consola de administración para administrar nuestros reportes.

 

Cualquier consulta no duden en escribir.

 

Saludos cordiales.

Save As saiku


En la entrada anterior vimos como integrar adhoc con nuestra aplicación  en el cual nosotros tenemos el control del repositorio de pentaho

en este post explicaremos como hacemos con saiku

 

Saiku es una de las mejores por no decir la mejor de los plugin para hacer análisis OLAP y es además open source

si queremos la independencia de la consola de pentaho, podemos hacer el mismo paso que el post anterior y podremos guardar nuestro análisis saiku según nuestra necesidad

puesto que este también dispone de un plugin:

\pentaho-solutions\system\saiku\ui\js\saiku\plugins\BIServer\plugin.js

el proceso es exactamente el mismo del post anterior para el proceso de guardado.

image

Vamos hacer algo mas, para darle a saiku la independencia total.

si abrimos saiku en modo edición en una ventana vemos que que no dispone de un botón guardar, solo dispone de un botón guardar como

entonces si modificamos el análisis no podemos guardarlo automáticamente salvo que tendríamos que navegar y remplazar el archivo y esto para mi

es un proceso engorroso cuando ya hay parámetros y esta en modo edición

 

entonces hagamos un script para poder guardar el análisis saiku sin que nos este solicitando navegar por el repositorio de soluciones de pentaho

lo primero es que tenemos que agregar un nuevo botón en la plantilla index.html para que figure como un icono mas.

 

save

después agregamos un class css .save_action y le agregamos un icono, recuerden el nombre: href=”#save_action”

.workspace_toolbar .save_action{
   background-image: url('../../../../../../mantle/images/save_32.png');
   height: 16px !important;
   width: 16px !important;
}

podemos ver que nuestro botón se agrego correctamente. obviamente que el botón save as también lo he cambiado de icono

buttom

 

Ubiquemos los siguientes archivos:

 

\pentaho-solutions\system\saiku\ui\js\saiku\views\WorkspaceToolbar.js

y en la línea 157 des pues de la función open_query crear esta función

 

save_action: function(event) {

  var SaveSolution = Settings.GET.SOLUTION;
   var SavePath = Settings.GET.PATH;
   var SaveName = Settings.GET.ACTION;

   if (SaveSolution == undefined || SavePath == undefined || SaveName == undefined) {
           if(top.mantle_initialized !== undefined && top.mantle_initialized &&
           top.parent.enableAdhocSave ) {
               if (window.ALLOW_PUC_SAVE === undefined || ALLOW_PUC_SAVE) {
                   top.parent.enableAdhocSave(isAllowed);
               }
           } else {
               this.save_query(event);
           }
   } else {
       var nwPath;
       var temp = SavePath.toString();
       if (temp == 'NaN') {
           nwPath = '';
       } else {
           nwPath = Settings.GET.PATH;
       }
       puc.save_to_solution(SaveName, SaveSolution, nwPath, null, true);
   }
    },

Lo que hace la función es  recoger las variables globales capturadas en iniciar la ejecución del análisis por las librerías de saiku.

si el análisis esta en modo edición, entonces estas variables tiene un valor valido asignado y como decía al inicio

(No podemos estar preguntando donde guardar si tenemos la ruta y queremos actualizar el análisis).

 

ahora si el análisis no tiene valores validos quiere decir que es un nuevo análisis, y entonces si no esta integrado a pentaho re direcciono al SAVE_QUERY,

para que me muestre el explorador de soluciones, también podemos directamente  llamar a SAVE_QUERY, sin preguntar si esta en un iframe de la consola de pentaho

 

save_action: function(event) {

  var SaveSolution = Settings.GET.SOLUTION;
   var SavePath = Settings.GET.PATH;
   var SaveName = Settings.GET.ACTION;

   if (SaveSolution == undefined || SavePath == undefined || SaveName == undefined) {
               this.save_query(event);
          
   } else {
       var nwPath;
       var temp = SavePath.toString();
       if (temp == 'NaN') {
           nwPath = '';
       } else {
           nwPath = Settings.GET.PATH;
       }
       puc.save_to_solution(SaveName, SaveSolution, nwPath, null, true);
   }
    },

image

Al hacer click me re direcciona a save_query, por que las variables no están definidas

 

image

Al hacer click, ya existe valores validos para las variable

- solution

- path

- name

Por ello procedemos a guardarlo sin estar preguntando donde queremos guardarlo.

 

Cualquier consulta no duden en escribir.

Saludos cordiales

Save As adhoc


adhoc, en la versión community no tiene botones guardar o guardar como, por ello cuando se requiere abrirlo en una ventana individual, no es posible actualizarlo o crear otro a partir de ello

pero estos magníficos plugins disponen de un api de integración para este cometido (saiku y adhoc), después explicare en otro post a cerca de saiku.

 

por hora veamos algunos escenario con la integración de adhoc con un proyecto web.

En muchos casos necesitamos integrar pentaho en nuestra aplicación web, pero como lo hacemos si no queremos incluirlo al BA SERVER, como un iframe o como una aplicación independiente

entonces insertamos en un iframe  /pentaho/content/saiku-adhoc/web/index.html?biplugin=true, pero si queremos administrar estos reportes desde nuestra aplicación se nos complica la cosa

y solo podremos ver los reportes guardados y creados desde el BASERVER.

 

hoy vamos a dar las pautas necesarias para aquellos que quieran integrarlo en su aplicación web.

 

para comenzar tanto saiku como adhoc tiene un plugin de integración que se encuentra en la ruta:

\pentaho-solutions\system\saiku-adhoc\web\js\adhoc\plugins\BIServer\plugin.js

 

pero veamos en funcionamiento de adhoc.

  adhoc

 

y veamos la parte mas importante del archivo anteriormente mencionado.

var puc = {
    allowSave: function(isAllowed) {
       

        if(top.mantle_initialized !== undefined && top.mantle_initialized &&
            top.parent.enableAdhocSave ) {
            if (window.ALLOW_PUC_SAVE === undefined || ALLOW_PUC_SAVE) {
                top.parent.enableAdhocSave(isAllowed);
               
            }
        }
    },
   
    refresh_repo: function() {
        if(top.mantle_initialized !== undefined && top.mantle_initialized) {
            top.mantle_refreshRepository();
        }
    },
   
    save_to_solution: function(filename, solution, path, type, overwrite) {
       
        var self = this;
       
        var query = Application.tabs._tabs[0].content.query;
        query.action.get("/json", {
            success: function(model, response) {
               
                var queryToSave = jQuery.parseJSON(response.json);

                queryToSave.maxClientSeq = query.workspace.idCounter;
               
                var savedQuery = JSON.stringify(queryToSave, null, ' ');
               
                (new SavedQuery({
                    name: filename,
                    newname: query.get('name'),
                    json: savedQuery,
                    solution: solution,
                    path: path,
                    action: filename,
                    overwrite: overwrite
                })).save({},{
                    success: function() {
                        puc.refresh_repo();}
                });
            }
        });
    }
};

 

como podemos observar hay tres metodos: allowSave,refresh_repo y save_to_solution

- allowSave : esta tiene una función principal y es la encargada de notificar a la pagina que contiene al iframe embebido, siempre en cuando la pagina tenga una variable mantle_initialized

-  refresh_repo : esta función también hace un llamado al pagina que contiene el iframe notificando que el reporte fue guardado y que refresquen el repositorio

- save_to_solution: aquí recibimos los parámetros de la pagina que contienen al iframe, el cual nos enviar los parámetros siguientes:

  • solution
  • path
  • name
  • write

 

en un .html debemos definir un iframe con src=”/pentaho/content/saiku-adhoc/web/index.html?biplugin=true” para cargar el adhoc

una vez definida creamos un archivo javascript que contenga las siguientes funciones.


/**
* JS Integration, interaccion con js iframe content report.
*
*/

var mantle_initialized = true;
var lastMessage;
/**
*
* @param {type} contentEdit
* @returns {undefined}
*/
function enableContentEdit(contentEdit) {
    console.log("enableContentEdit : " + contentEdit);
}
/**
*
* @param {type} contentEdit
* @returns {undefined}
*/
function setContentEditSelected(contentEdit) {
    console.log("setContentEditSelected : " + contentEdit);
}

/**
*  EN ESTA FUNCION ADHOC NOS DICE QUE YA SE PUEDE GUARAR ESTE REPORTE

* @param {type} adhocSave
* @returns {undefined}
*/
function enableAdhocSave(adhocSave) {
    console.log("Call : enableAdhocSave ->  " + adhocSave);
    if (adhocSave) {
        /**
         * Mostrar Boton Guardar y Guardar como
         */
    } else {
        /**
         * Ocultar Boton Guardar y Guardar como
         */
    }
}
/**
* ADHOC NOS NOTIFICA PARA REFRESCAR EL REPOSITORIO
* @returns {undefined}
*/
function mantle_refreshRepository() {
    console.log("Call refresh Tree");
}

/**
* ADHOC  NOS ENVIA UN MENSAJE SI SE PRODUJO UN ERROR AL GUARDAR EL REPORTE

* @param {type} title
* @param {type} details
* @returns {undefined}
*/
function mantle_showMessage(title, details) {
    lastMessage = title + ": " + details;
    console.log("mantle_showMessage Title : " + title + ", Detail: " + details);
}

 

ahora procedemos hacer una corrida, supongamos que inicializamos el iframe, y creamos nuestro reporte, una vez haya datos en el reporte, adhoc ejecutara la función ALLOWSAVE

y si el usuario desea guardar tendrá que seccionar una ubicación del repositorio y un nombre que desea darle al nuevo reporte

tree

 

Una vez que el usuario haya seleccionado la ubicación correspondiente procedemos a preparar estas variables

  • var solution
  • var path
  • var name
  • var write (true si el usuario selecciono un reporte del repositorio y desea sobrescribirlo)

con estos valores ya podemos decirle a ADHOC, que guarde nuestro reporte y ejecutamos el siguiente javascript

en este caso el iframe que contiene al adhoc es el siguiente

<iframe name="adhoc" ..

 

window.frames["adhoc"].gCtrlr.repositoryBrowserController.remoteSave(name,solution,'/' + path, null, write);

 

con esto se ejecutara la funcion remoteSave de plugin.js y esta enviara los parametros aa puc.save_to_solution(name,solution,path,null,write);

después de guardarlo adhoc nos notificara a

/**
* Refrescar el arbol
* @returns {undefined}
*/
function mantle_refreshRepository() {
    console.log("Call refresh Tree");
}

 

y en nuestro .html refrescaremos el repositorio en el cual se encontrara el archivo guardado.

 

pentaho

Cualquier consulta no duden en escribir.

Saludos cordiales