domingo, 13 de agosto de 2017
Spring Security 4 + JSF 2, Roles and Privileges
Hola
Estoy desarrollando una aplicación con spring 4, spring-security 4 y jsf 2 (primefaces) y para el esquema de seguridad ademas de roles tambien tenemos privileges o permisos, un rol tiene asociado los permisos.
Para ese esquema de seguridad segui este tutorial:
https://blog.lukaspradel.com/user-role-permission-security-rbac-in-spring-security-4/
Para larte de jsf para poder mostrar secciones de acuerdo a los roles y permisos seguí la documentación de spring-webflow (agregue la dependencia spring-faces.2.4.5.RELEASE)
http://docs.spring.io/spring-webflow/docs/2.4.5.RELEASE/reference/html/spring-faces.html#spring-faces-security-taglib
Pero al querer validar los privileges/permisos no podia hacerlo ya que las tags de spring-faces invocan a los métodos hasRole y hasAnyRole y para poder validar los privileges/permisos se debe hacer con métodos hasAuthority y hasAnyAuthority, por lo que modifique las clases para agregar nuevos métodos para poder validar los privilege/permisos.
Aca les dejo el jar, no es necesario crear un archivo taglib ya que está incluido en el jar y se detecta automáticamente
https://github.com/neo-lestat/simple-spring-security-facelets-taglib/releases/tag/v0.1
El codigo fuente
https://github.com/neo-lestat/simple-spring-security-facelets-taglib/
lunes, 27 de agosto de 2012
Java Compresion Alternativa gzip y zip
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.0</version>
</dependency>
y en el codigo seria algo asi (tomado de la respuesta de stackoverlfow)
FileInputStream inFile = new FileInputStream("src.tar");
FileOutputStream outfile = new FileOutputStream("src.tar.xz");
LZMA2Options options = new LZMA2Options();
options.setPreset(7); // play with this number: 6 is default but 7 works better for mid sized archives ( > 8mb)
XZOutputStream out = new XZOutputStream(outfile, options);
byte[] buf = new byte[8192];
int size;
while ((size = inFile.read(buf)) != -1)
out.write(buf, 0, size);
out.finish();
Con la compresion xz el archivo de 90 megas se redujo casi 6 megas, el archivo como lo mencione es un pdf que contiene texto e imagenes, si fuera solo texto pues bastaria usar zip, por eso opte por esta alternativa
jueves, 3 de mayo de 2012
Internacionalización JSF 2.0
Vamos a utilizar un PhaseListener de jsf 2, El codigo es el siguiente
package org.nl.example;
import java.util.Locale;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class LifeCycleListener implements PhaseListener {
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
public void beforePhase(PhaseEvent event) {
System.out.println("START PHASE " + event.getPhaseId());
FacesContext context;
if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
context = event.getFacesContext();
Application application = context.getApplication();
ValueExpression ve = application.getExpressionFactory()
.createValueExpression(context.getELContext(),
#{localeManagedBean.locale}", Locale.class);
try {
System.out.println("setting locale in phase listener");
Locale localeToSet = (Locale) ve.getValue(context.getELContext());
context.getViewRoot().setLocale(localeToSet);
} catch (Exception e) {
System.out.println("error in getting locale");
}
}
}
public void afterPhase(PhaseEvent event) {
System.out.println("END PHASE " + event.getPhaseId());
}
}
El PhaseListener debe ser colocado en faces-config.xml asi:
<lifecycle>
<phase-listener>org.nl.example.LifeCycleListener</phase-listener>
</lifecycle>
El PhaseListener uunicamente le importa la fase de RENDER_RESPONSE que es la fase donde faces crea el contenido de respuesta, y ahi obtiene un locale a partir de un managebean, en este caso el managebean es localeManageBean el cual su alcance es de sesion, este a su vez contienen una propiedad de tipo Locale, la cual tiene almacenado la locale de nuestro idioma, tambie podemos actualizarla con una nueva Locale correspondiente al nuevo idioma en el que se presenten las paginas, el codigo es mas o menos asi:
package org.nl.example;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ValueChangeEvent;
@ManagedBean(name = "localeManagedBean")
@SessionScoped
public class LocaleManagedBean implements Serializable {
private static final long serialVersionUID = 1L;
private String localeCode;
private Locale locale;
private static Map countries;
static {
countries = new LinkedHashMap();
countries.put("English", Locale.ENGLISH); // label, value
countries.put("Spanish", new Locale("es"));
}
public LocaleManagedBean() {
super();
locale = (Locale) countries.get("Spanish");
}
public Map getCountriesInMap() {
return countries;
}
public String getLocaleCode() {
return localeCode;
}
public void setLocaleCode(String localeCode) {
this.localeCode = localeCode;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public void countryLocaleCodeChanged(ValueChangeEvent e) {
String newLocaleValue = e.getNewValue().toString();
// loop country map to compare the locale code
for (Map.Entry entry : countries.entrySet()) {
if (entry.getValue().toString().equals(newLocaleValue)) {
locale = (Locale) entry.getValue();
break;
}
}
}
}
Este MeanageBean puede ser utilizado para colocar el nuevo idioma con el metodo countryLocaleCodeChange, no estoy seguro si este método debe retornar a una pagina, dado que en mi caso yo obtengo la Locale a travez de una cookie que fue colocada por spring mvc, espero me comenten si tienen que hacer alguna adecuación.
Espero les sirva les dejo algunas ligas que me sirvieron
http://www.mkyong.com/jsf2/jsf-2-internationalization-example/
http://www.java.net/node/703940
martes, 7 de febrero de 2012
Eclipselink Join Fetch - Cargar Relaciones Jpa
Creamos el query
Query query = entityManager.createQuery("select p from Person p where p.id = :id");
query.setParameter("id", 10);
Para colocar el query hint hacemos los sigueinte
query.setHint("eclipselink.join-fetch", "p.telefonos" );
Person p = query.getSingleResult();
Ahora en el objeto p sus telefonos ya estan cargados, desafortunadamente si en telefonos su objeto tiene otra relación a la que queremos acceder tendremos que inicializarla también pero seria con código o bueno no encontré una forma de hacerlo con otro queryhint, si la encuentran pues espero me la digan.
Esto solo tenemos que hacerlo cuando invocamos desde una aplicación un ejb remoto que devuelve una entidad jpa y usamos eclipselink de lo contrario al acceder a una relación no inicializada nos lanzara una excepción que nos indica que debemos inicializarla, si trabajamos las entidades en ejb locales no es necesario inicializar las relaciones, eclipselink lo hace en cuanto se intente acceder a ella
Glassfish, Eclipselink, Ejb Remoto Problema con Entidad
Verificando lo anterior revise que tuviera la misma versión de la entidad en ambas aplicaciones pero aun se mantenía el error, buscando en google me encontré que el problema era provocado por eclipselink (usamos eclipselink para jpa en esta aplicación), eclipselink tiene una optimización llamada weaving, esta optimización la realiza por default al cargar las clases en el classloader, hace modificaciones del bytecode por lo que al hacer esto ya no tenia la misma versión de las clases en la aplicaciones.
Para solucionarlo existen 2 opciones una es deshabilitar el weaving de eclipselink pero esta opción nos quita las optimizaciones por lo que puede impactar en el performance de la aplicación, y la otra opción es utilizar el weaving estático con esta opción se procesan las clases de nuestras entidades para utilizarlas en las aplicaciones por lo que tendríamos el mismo bytecode en cada aplicación.
Para deshabilitar el weaving dinámico podemos colocar la siguiente propiedad en nuestro persistence.xml
< property name="eclipselink.weaving" value="false" / >
Para habilitar el weaving estático son mas pasos, el primero colocar la propiedad de eclipse link con valor estático
< property name="eclipselink.weaving" value="static" / >
El segundo paso es "compilar" las classes entidad que tenemos, esto lo podemos hacer con ant, si tiene la ultima version (1.8.2) el build.xml es diferente al que esta en la pagina de eclipse link, seria así :
<project name="Weaving-Eclipselink" default="clean" basedir=".">
<description>
Build file to generate static weaving
</description>
<!-- set global properties for this build -->
<property name="dist" location="dist"/>
<property name="jarToWeaving" value="myjpa.jar"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<mkdir dir="${dist}"/>
<!--copy file="${jarToWeaving}" todir="${dist}"/-->
<copy todir="${dist}" overwrite="true" >
<fileset dir=".">
<include name="${jarToWeaving}" />
</fileset>
</copy>
</target>
<taskdef name="weave" classname="org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask"/>
<target name="weaving" description="perform weaving" >
<weave source="${jarToWeaving}"
target="${dist}/${jarToWeaving}"
persistenceinfo="./persistence">
<classpath>
<pathelement path="./lib/javax.persistence.jar"/>
</classpath>
</weave>
</target>
<target name="clean" description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${dist}"/>
</target>
</project>
Donde myjpa.jar es el jar que contiene las clases que deseo procesar para generar el weaving estático, se crea una carpeta dist y ahí se genera el nuevo jar con el weaving estático, otro punto importante es persisteninfo ahí le indico el archivo persistence.xml en el que se basara para generar el weaving estático, en mi caso cree la carpeta persistence al mismo nivel del build.xml y dentro de ella tengo META-INF y dentro de META-INF se encuentra mi archivo persistence.xml (eclipselink requiere la carpeta META-INF). El classpath de la tarea weaving no me funciono muy bien por lo que tuve que pasar las lib en la ejecución de ant
ant -lib ./lib/eclipselink.jar:./lib/javax.persistence.jar weaving
Una vez que tenemos el jar con el weaving estatico podemos incluir este en las aplicaciones que usan las entidades y con esto al invocar el ejb remoto obtenemos correctamente la entidad, bueno hay que tener cuidado con las relaciones lazy hay que inicializarlas en el ejb remoto si las vamos a utilizar.
Mas informacion de eclipselink weaving
http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#Using_EclipseLink_JPA_Weaving
jueves, 12 de mayo de 2011
Parameters in sql of JdbcCursorItemReader
http://static.springsource.org/spring-batch/index.html
http://www.dosideas.com/cursos/course/view.php?id=7
Estaba realizando algunos pruebas con esta herramienta, cuando me tope con un pequeño detalle al usar JdbcCursorItemReader, esta clase es una implementacion de spring-batch para leer datos de la bd, entre sus propiedades tiene una nombrada "sql", y es en esta donde se declara el query que sera ejecutado, hasta ahi todo bien pero me surgio la inquietud de pasarle parametros a ese query buscando en google encontre algunas sugerencias pero siempre me falto algo para que funcionara el paso de parametros, bien les dejo la configuracion para poder hacerlo; en la declaración del itemReader debemos asignarle el scope de tipo step, en nuestro query colocamos el signo de ? en donde queremos meter el parámetro, después colocamos la propiedad preparedStatementSetter que es de tipo ListPreparedStatementSetter que a su vez es una lista por lo que podemos asignarle varios valores, para agregar el parámetro que se recibe al ejecutar el Job de spring usamos spring(#{...}) EL para pasar el valor del parámetro a la lista, esto se realica en tiempo de ejecucion.
<bean id="bdItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
select *
from foo
where propertyStr like ?
]]>
</value>
</property>
<property name="preparedStatementSetter">
<bean class="org.springframework.batch.core.resource.ListPreparedStatementSetter">
<property name="parameters">
<list>
<value>#{jobParameters[propertyStr]}</value>
</list>
</property>
</bean>
</property>
<property name="rowMapper">
<bean id="beneficiarioMapper" class="org.springframework.jdbc.core.BeanPropertyRowMapper">
<property name="mappedClass">
<value type="java.lang.Class">pgs.example.Foo</value>
</property>
</bean>
</property>
</bean>
y al lanzar el Job debemos pasar el parametro propertyStr
JobParametersBuilder builder = new JobParametersBuilder();
builder.addDate("StartTime", new Date());
builder.addString("jobName", "Export");
builder.addString("propertyStr", "condition");
JobParameters parameters = builder.toJobParameters();
jobLauncher.run(job, parameters);
y listo con estos, el query recibe el parámetro.
Nota: Probado con spring-batch-2.1.7
jueves, 18 de marzo de 2010
Ejemplo Message Drive Bean con Jboss 4.3
El código de nuestro Message Bean es el siguiente:
package examples.messaging;
import javax.annotation.PreDestroy;
import javax.jms.*;
import javax.ejb.*;
@MessageDriven (
activationConfig={
@ActivationConfigProperty(propertyName="destination",
propertyValue="topic/testTopic"),
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Topic")
}
)
public class LogBean implements MessageListener {
public LogBean() {
System.out.println("Log Bean Created");
}
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
TextMessage txtMessage = (TextMessage) message;
String text = txtMessage.getText();
System.out.println("Recived new message : " + text);
} catch (JMSException ex) {
ex.printStackTrace();
}
}
}
@PreDestroy
public void remove(){
System.out.println("Log Bean Destroyed");
}
}
Estamos utilizando anotaciones para realizar la configuración de nuestro message bean, si no queremos usar estas anotaciones podemos usar el descriptor de despliegue (ejb-jar.xml) .
La primera anotación declara la configuración necesaria, primero el destino de nuestros mensajes y después el tipo. En el código utiliza la configuración de jms que jboss tiene por defecto, podemos crear las nuestras y cambiarlas.
Esta clase tenemos que compilarla y empaquetarla en un jar, las dependencias se encuentran en las lib de jboss, colocamos el jar en nuestra carpeta deploy de nuestra configuración.
El código del cliente es el siguiente, es un cliente standalone :
package org.nl.examples.client;
import java.util.Properties;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
*
* @author usuario
* This class is a simple client for a stateful session bean
* To ilustrate how passivation works, configure your EJB server
* to allow only two stateful session beans in memory (Consult your
* vendor documentation for details on how to do this). We create
* 3 beans in this example to see how and when beans are passivated.
*/
public class LogClient {
public static final int noOfClients = 10;
public static void main(String args[]) {
try{
/*Estas propiedades nos sirven para conectarnos a jboss desde nuestro cliente standalone,
si el cliente corre dentro de jboss esto no seria necesario*/
Properties properties = new Properties();
properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
properties.put("java.naming.provider.url","localhost:1099");
Context ctx = new InitialContext(properties);
//ctx.lookup(CountBean.class.getSimpleName()+"/remote");
// 1: Lookup the connection factory
TopicConnectionFactory factory = (TopicConnectionFactory) ctx.lookup("ConnectionFactory");
// 2: Use connection factory to create a JMS connection
TopicConnection connection = factory.createTopicConnection();
// 3: Use connection to create a session
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
// 4: Lookup destination
Topic topic = (Topic) ctx.lookup("topic/testTopic");
// 5: Create a message publisher
TopicPublisher publisher = session.createPublisher(topic);
// 6: create and publish a message
TextMessage msg = session.createTextMessage();
msg.setText("This is a test message.");
publisher.send(msg);
// finish
publisher.close();
System.out.println("Message published. Please check aplication server's console to see the response from MDB");
} catch(Exception e){
e.printStackTrace();
}
}
}
Para compilarlo y ejecutarlo se necesitan las bibliotecas:
-jboss-aop.jar
-jbossall-client.jar
-javassist.jar
-trove.jar
-log4j.jar
El libro que les mencione antes pueden descargarlo de masteringEJB3,
Problemas con charset en Spring MVC
<filter>
<filter-name>charsetFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>charsetFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Y eso es todo con este filtro podemos reciber correctamente los caracteres de nuestra forma.
viernes, 2 de octubre de 2009
Servlet para el envio de multiples reportes al cliente
/*Este metodo lo invocamos en doPost, doGet o ambos del servlet */
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Connection conection = null;
byte[] bytesReporte = null;
int in = 0;
try {
/*La clase HelpConnection devuelve la conexion jdbc para llenar el reporte*/
conection = HelpConnection.getConnection();
/*Aqui recibo unos ids que sera mi dato para llenar mi reporte,
* en mi caso tengo que llenar varios reportes de acuerdo a los id que recibo
* y de acuerdo a esos ids se generan n reportes */
String [] ids = request.getParameterValues("idusuario");
int numReportes = ids.length ;
/*Estos archivos temporales van a guardar los datos del reporte, lo queria hacer
* en memoria pero hubo algunos problemas. */
File files [] = new File [numReportes];
for(int i = 0; i < files.length; i++){
files[i] = getTempFile(i+"report","tmp");
}
String cs = File.separator;
String path = request.getSession().getServletContext().getRealPath("/");
/*La ruta de mi reporte*/
String rutaReporte = path + "reporte"+ cs + "user.jasper";
//JasperReport reporte = (JasperReport) JRLoader.loadObject(rutaReporte);
FileInputStream fileJasper = null;//new FileInputStream(rutaReporte);
JasperPrint jasperPrint = null;
HashMap paramReport = new HashMap();
JRXmlExporter exporter = new JRXmlExporter();
/*Se llenan los reportes y se guardan en los archivos temporales*/
for (int k = 0; k < numReportes; k++ ){
System.out.println("entro " + k);
fileJasper = new FileInputStream(rutaReporte);
paramReport.put("id", Integer.parseInt(ids[k]));
jasperPrint = JasperFillManager.fillReport(fileJasper, paramReport, conection);
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_FILE, files[k] );
exporter.exportReport();
}
/*y aqui los envio al cliente*/
OutputStream outStream = response.getOutputStream();
DataOutputStream dataOut = new DataOutputStream(outStream);
//envio el numero de archivos a enviar
dataOut.writeInt(numReportes);
FileInputStream fileStrm = null;
BufferedInputStream bufferStrm = null;
byte fileBytes[] = null;
for (int k = 0; k < numReportes; k++ ){
//envio nombre del archivo
dataOut.writeUTF(files[k].getName());
//envio tamño del archivo
dataOut.writeLong(files[k].length());
//abrimos el archivo a enviar
fileStrm = new FileInputStream(files[k]);
bufferStrm = new BufferedInputStream(fileStrm);
//Envia el archivo
while ((in = bufferStrm.read()) != -1){
dataOut.write(in);
}
outStream.flush();
fileStrm.close();
bufferStrm.close();
}
outStream.close();
dataOut.close();
outStream = null;
bufferStrm = null;
fileStrm = null;
dataOut = null;
for(int i = 0; i < files.length; i++){
files[i].delete();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
El codigo funciona para 1 o n archivos, espero publicar la version del applet para un solo archivo, evidentemente eso es mas facil
Saludos
jueves, 1 de octubre de 2009
Imprimir reporte de Jasperreports en el Cliente
Les traigo un ejemplo de como imprimir directamente a la impresora del cliente un reporte generado con jasperreport en el servidor.
Para poder invocar a la impresora del cliente tenemos que hacer un applet y firmarlo ya que esta restringido y no puede invocar recursos del cliente, por eso debemos firmarlo, este applet contiene una clase que va a recibir los datos que se generan en el servidor.
Este codigo que les proporciono ha sido probado con la version 3.0 de jasperreports.
Primero tenemos el codigo del Servlet que nos genera los reportes , bueno en este caso como estaba haciendo pruebas genere unos archivos.xml con ireport que contienen datos de un reporte, lo que tiene que hacer es cambiar esa parte por la generacion de sus reportes, este metodo lo invoco en doPost y doGet
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int in;
try {
/*Esta parte la deben de cambiar de acuerdo al numero de reportes que tiene que
imprimir, deberia de funcionar con uno o mas reportes*/
int numFiles = 3;
OutputStream outStream = response.getOutputStream();
DataOutputStream dataOut = new DataOutputStream(outStream);
//envio el numero de archivos a enviar
dataOut.writeInt(numFiles);
/* Temporal */
File file = new File("C:\\prueba1.xml");
File files [] = new File [numFiles];
files[0] = file;
files[1] = new File("C:\\prueba2.xml");
files[2] = new File("C:\\prueba3.xml");
/*Lo de arriba son mis archivos temporales, esto lo pueden cambiar generando archivos temporales en el servidor con jasperreport y luego los envian al cliente otra opcion es llenar su reporte y obtener los bytes para mandarlos al cliente, tiene que llenar sus repores con JRPrintXmlLoader, ejemplo
JasperReport reporte = (JasperReport) JRLoader.loadObject("reporte1.jasper");
JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, null, conection);
JRExporter exporter = new JRXmlExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
y en el applet vamos a recuperar el (los) objeto jasperPrint para mandarlos a la impresora
*/
FileInputStream fileStrm = null;
BufferedInputStream bufferStrm = null;
byte fileBytes[] = null;
for (int k = 0; k < numFiles; k++ ){
/*logger.info(" enviando el archivo no. " + k + " nombre Archivo " + files[k].getName()
+ " tamaño" + files[k].length());
*/
//envio nombre del archivo
dataOut.writeUTF(files[k].getName());
//envio tamño del archivo
dataOut.writeLong(files[k].length());
//abrimos el archivo a enviar
fileStrm = new FileInputStream(files[k]);
bufferStrm = new BufferedInputStream(fileStrm);
//Envia el archivo
while ((in = bufferStrm.read()) != -1){
dataOut.write(in);
}
// if (logger.getLevel().INFO )
// logger.info("Archivo enviado :: " + files[k].getName() );
outStream.flush();
fileStrm.close();
bufferStrm.close();
}
outStream.close();
dataOut.close();
outStream = null;
bufferStrm = null;
fileStrm = null;
dataOut = null;
Ahora el codigo de el applet, como dije antes en este applet vamos a recibir los datos de los objetos jasperPrint y despues los mandamos a imprimir
/*
* AppletPrint.java
*
* Created on 1 de octubre de 2009, 12:40 PM
*/
package mx.com.lmp.erp.print;
import java.awt.BorderLayout;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperPrintManager;
import net.sf.jasperreports.engine.export.JRPrintServiceExporter;
import net.sf.jasperreports.engine.export.JRPrintServiceExporterParameter;
import net.sf.jasperreports.engine.print.JRPrinterAWT;
import net.sf.jasperreports.engine.xml.JRPrintXmlLoader;
import net.sf.jasperreports.view.JRViewer;
public class AppletPrint extends javax.swing.JApplet {
/** Initializes the applet AppletPrint */
public void init() {
try {
this.urlService = getParameter("urlService");
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
initComponents();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void initComponents() {
jPanel1 = new javax.swing.JPanel();
jBtnImprimir = new javax.swing.JButton();
setLayout(new java.awt.BorderLayout());
jBtnImprimir.setText("Imprimir");
jBtnImprimir.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jBtnImprimirActionPerformed(evt);
}
});
jPanel1.add(jBtnImprimir);
add(jPanel1, java.awt.BorderLayout.CENTER);
}
private void jBtnImprimirActionPerformed(java.awt.event.ActionEvent evt) {
comunicacionServer();//para recibir los datos del server
}
// Variables declaration - do not modify
private javax.swing.JButton jBtnImprimir;
private javax.swing.JPanel jPanel1;
// End of variables declaration
//Guardara la url del servlet al que se comunicara para imprimir
private String urlService;
private void comunicacionServer(){
int in=0;
long tamFile=0;
/* Primero creamos la URL para la conexión. Tiene sentido construir la
dirección de esta forma tan "complicada" puesto que el applet solo puede
establecer conexiones con su servidor, y así, al construir la dirección
dinámicamente, no tenemos que retocar el código al irnos a otro servidor.
En todo caso, lo siguiente sería válido:
URL direccion = new URL ("http://www.javahispano.com/servlet/MiServlet");
*/
URL pagina = this.getCodeBase();
String protocolo = pagina.getProtocol();
String servidor = pagina.getHost();
int puerto = pagina.getPort();
//String servlet = "/servlet/MiServlet";/ServletEnviaReport
String servlet = "";
if ( this.urlService != null && !this.urlService.equals(""))
servlet = this.urlService;
else
servlet = "/PruebaApplet/ServletEnviaReportes";
URL direccion = null;
URLConnection conexion = null;
try {
direccion = new URL(protocolo, servidor, puerto, servlet);
conexion = direccion.openConnection();
System.out.println(" Url del urlService " + direccion.toString());
} catch (IOException ex) {
ex.printStackTrace();
}
/*Lo siguiente es decirle al navegador que no use su
cache para esta conexión, porque si lo hace vamos a
tener un página estatica, y para eso no nos metemos
en estos líos ;-). */
conexion.setUseCaches(false);
/* Ahora añadimos todas las cabeceras de HTTP que necesitemos, Cookies, contenido,
autorizacion, etc. con el método:
conexion.setRequestProperty ("cabecera", "valor");
Consultar la especificación de HTTP para más detalles. Por ejemplo, para decir
que preferentemente hablamos español: */
conexion.setRequestProperty("Accept-Language", "es");
BufferedInputStream buffer = null ;
try {
/* Procesamos la información de la forma adecuada, según se
trate de datos ASCII o binarios. */
//Obtenemos el stream de entrada para leer la informacion que nos envie el
//server
DataInputStream dataIn = new DataInputStream(conexion.getInputStream());
buffer = new BufferedInputStream(conexion.getInputStream());
// Recibo el numero de archivos que enviara el servidor
int numArchivosRecibir = dataIn.readInt();
System.out.println(" Numero de archivos para imprimir " + numArchivosRecibir);
// Recibe los archivos
String nameFiles[] = new String[numArchivosRecibir];
File f = null;
File files[] = new File[numArchivosRecibir];
FileOutputStream ouputStream = null;
for(int k = 0; k < f =" this.fileTemp(dataIn.readUTF(),null);" tamfile =" dataIn.readLong();" ouputstream =" new" j =" 0;" datain =" null;" buffer =" null;" ouputstream =" null;" inputstream =" null;" jasperprintlist =" new" j =" 0;" inputstream =" new">
/*Aqui es donde se cargan los datos recibidos del server y se lanza a la impresion*/
JasperPrint jsprint = JRPrintXmlLoader.load(inputStream);
JasperPrintManager.printReport(jsprint,true);
}
} catch (JRException e1) {
e1.printStackTrace();
}
//borramos los archivos temporales
for(File fi : files ){
fi.delete();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void ejecutaConJavascript (final String urlConParametros){
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
comunicacionServer(urlConParametros);
} catch(Exception e) {
e.printStackTrace();
return false;
}
return null;
}
});
}
public void comunicacionServer(String urlConParametros) {
this.urlService = urlConParametros;
this.comunicacionServer();
}
private File fileTemp (String name, String suffix) {
try {
return File.createTempFile(name,suffix);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
}
Bueno el codigo me base en un applet que encontre en javahispano, la parte importante es el metodo comunicacionServer() en este metodo se reciben los arhivos o datos que nos envia el server y despues se lanzan a la impresora, tambien tiene un metodo ejecuta con javascript este metodo es util para poder armar nuestra url con los parametros que necesitemos para generar nuestros reportes, ahora un codigo de ejemplo que iria en el jsp :
<applet name="nomApplet"
code="mx.com.lmp.erp.print.AppletPrint"
archive="AppletPrintJRV.jar,jasperreports-3.0.0.jar,commons-digester-1.7.jar,
commons-javaflow-20060411.jar,commons-logging-1.0.2.jar,commons-logging-api-1.0.2.jar,
commons-beanutils-1.7.jar,commons-collections-2.1.jar"
width="100" height="100">
<param value="/WebApplication2/ServletReport" name="urlService">
</applet>
como pueden ver le estoy poniendo algunas libs y la de jassperreport bueno esto lo hice para que el applet pueda encontrar las libs necesarias tambien firme el jar de jasperreports ya que como observan en el codigo genero archivos temporales, si no lo hago asi pues no podria leer esos archivos temporales y pues otra opcion seria que en lugar de guardar los datos que recibo del server en temporales pues tomarlos de memoria y cargalos con JRPrintXmlLoader para generar el jasperPrint y enviarlo a la impresora (creo que lo intentare despues).
Con javascript seria algo asi :
<script type="text/javascript" language="JavaScript">
function ejecutoApplet() {
//aqui hay que mandarle el nombre que le dimos al applet
var Myapplet = document.applets['nomApplet'];
//alert(Myapplet);
var p = Myapplet.ejecutaConJavascript('/WebApplication2/ServletEnviaReport');
//alert(p) //si el metodo del applet retorna algo con esto podemos ver el resultado
}
</script>
Bueno pues eso es todo por el momento en cuanto mejore el codigo actualizo este post o hago uno nuevo, les dejo mi codigo de ejemplo en la siguientes ligas applet, aplicacion web de ejemplo, firmas del applet
No olviden firmar el applet y colocarlo en su aplicacion si le hacen algun cambio pues de lo contrario les pueden salir problemas de permisos, espero les sirva