Showing posts with label Using. Show all posts
Showing posts with label Using. Show all posts

Sunday, February 12, 2012

Using JasperReports

JasperReports is a java report library that can be used to add reporting capability to an application.

Using JasperReports in a Java EE application


Prerequisistes

JRE
Servlet engine
RDBMS + JDBC driver for a database
Apache Ant

Versions used
JRE 1.6.0_21
Apache Tomcat 6.0.29
MySQL 5.1.50
MySQL connector/J 5.1.13
JasperReports 3.7.0
Apache Ant 1.8.1

Steps
  • Download the jasperreports project zip file from sourceforge, http://sourceforge.net/projects/jasperreports/
  • Extract the zip file to a temporary location e.g. C:\temp
  • Open a command prompt
  • Navigate to c:\temp\jasperreports-3.7.0-project\jasperreports-3.7.0\demo\samples\webapp
  • Type the command ant javac [the path to the ant bin folder needs to be in the system path]
  • Create a folder in the tomcat webapps directory e.g. jasper
  • Copy the contents of the webapp directory to the webapps\jasper directory. This sample web application can serve as a guide on how to integrate jasperreports into your own web application.


Using in a custom application
  • Copy the following files from the webapps\jasper\web-inf\lib\ directory to your application's web-inf\lib directory. commons-beanutils-1.8.0.jar, commons-collections-2.1.1.jar, commons-digester-1.7.jar, commons-logging-1.0.4.jar, jasperreports-3.7.0.jar, iText-2.1.0.jar (for pdf export), poi-3.2-FINAL-20081019.jar (for xls export). For generating charts you'll need jfreechart and jcommon.


Generating a report
import net.sf.jasperreports.engine.*; //for main jasper objects
import net.sf.jasperreports.engine.export.*; //for exporters

JasperReport jasperReport;
JasperPrint jasperPrint;
JRResultSetDataSource ds;

ServletContext context = this.getServletConfig().getServletContext();  
String fileBase="report1";

String fileName=context.getRealPath("/reports/"+fileBase+".jasper");

File reportFile = new File(context.getRealPath("/reports/"+fileBase+".jasper"));
if (!reportFile.exists()) {
//compile file
fileName=JasperCompileManager.compileReportToFile(context.getRealPath("/reports/"+fileBase+".jrxml"));   
}

//rs is a resultset generated by your application. You can also pass a connection instead of a resultset if you want to use the query as it is in the repoort template    
ds = new JRResultSetDataSource(rs);

HashMap parameters=new HashMap(); //use a map to pass report parameters  
jasperPrint = JasperFillManager.fillReport(fileName, parameters,ds);

//export to pdf   
JasperExportManager.exportReportToPdfFile(jasperPrint,fullFileName);

//export to html
JRXhtmlExporter exporter = new JRXhtmlExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, fullFileName);
exporter.exportReport();

//export to xls
JRXlsExporter exporter = new JRXlsExporter();    
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, fullFileName);
exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
exporter.exportReport();


Passing parameters
  • If passing parameters, the parameter value data type in the parameters map must match the data type of the parameter as declared in the report template.

Example template query using a single value parameter
select * from customers where customer_id = $P{customer_id_param}
Example template query using a multi value parameter
select * from products where $X{IN, category, categories_param}

select * from products where $X{NOTIN, category, categories_param}
For multi value parameters, define the parameter class in the jrxml file as java.util.List. Pass values using an array list e.g
List categories = new ArrayList();
categories.add("Laptop");
categories.add("PC");
categories.add("Printer");
parameters.put("categories_param", categories);

Using virtualizers
  • A virtualizer can be used to enable large reports to be generated successfully, without resulting in out of memory errors. Using a virtualizer doesn't guarantee filling of arbitrarily large reports. Heap memory is still needed but memory requirements are reduced.
  • Also, once a report is filled, whether it can be exported successfully depends on the export format and library. e.g. Filling may be successful, generating a jasper print object but exporting to excel may fail because poi puts all the data in memory before generating the final file. Adding available heap memory may help to have a successful export.
  • If a report contains images, this may also be a cause of out of memory errors. In such a case, the virtualizer doesn't help until you set the image's properties "isUsingCache" to false and "isLazy" to true in the report template.
import net.sf.jasperreports.engine.fill.*; //for virtualizers
import net.sf.jasperreports.engine.util.*; //for jrswapfile

//use virtualizer if required
JRAbstractLRUVirtualizer virtualizer=null;

if(!props.getProperty(VIRTUALIZER).equals("none")){
if(props.getProperty(VIRTUALIZER).equals("file")){
int maxSize=Integer.parseInt(props.getProperty(FILE_MAX_SIZE));
virtualizer=new JRFileVirtualizer(maxSize,System.getProperty("java.io.tmpdir"));
params.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
} else if(props.getProperty(VIRTUALIZER).equals("gzip")){
int maxSize=Integer.parseInt(props.getProperty(GZIP_MAX_SIZE));
virtualizer=new JRGzipVirtualizer(maxSize);
params.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
} else {
//use swap virtualizer by default
int maxSize=Integer.parseInt(props.getProperty(SWAP_MAX_SIZE));
int blockSize=Integer.parseInt(props.getProperty(SWAP_BLOCK_SIZE));
int minGrowCount=Integer.parseInt(props.getProperty(SWAP_MIN_GROW_COUNT));

JRSwapFile swapFile=new JRSwapFile(System.getProperty("java.io.tmpdir"),blockSize,minGrowCount);
virtualizer=new JRSwapFileVirtualizer(maxSize,swapFile);
params.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
}
}

//fill report with data
JasperPrint jasperPrint;
if(rs==null){
//use template query
connQuery = ArtDBCP.getConnection(datasourceId);      
jasperPrint = JasperFillManager.fillReport(jasperFileName, params,connQuery);
} else {
//use recordset based on art query 
JRResultSetDataSource ds;
ds = new JRResultSetDataSource(rs);
jasperPrint = JasperFillManager.fillReport(jasperFileName, params,ds);
}

//set virtualizer read only to optimize performance. must be set after print object has been generated
if(virtualizer!=null){
virtualizer.setReadOnly(true);
}

//export report
...

//clean up
if(virtualizer!=null){
virtualizer.cleanup();
}

Notes
  • If the resultset is null, by default, the generated report will consist of a completely blank page. To display other report sections and only have the data section blank, if using iReport, change the report properties "when no data" option to "all sections, no detail".
  • If the report contains images, the image files should be located in the same directory as the jrxml file
  • If a parameter is used in a query and is not provided, it will be ignored, as if the condition didn't exist

Using Mondrian

Using mondrian with Jpivot in a Java EE application

Versions used
Mondrian 3.2.1.13885
JPivot 1.8

Required files
  • Deploy the mondrian war
  • Copy the folders webapps\mondrian\jpivot and webapps\mondrian\wcf to the root of your application e.g. webapps\myapp
  • Copy the folders webapps\mondrian\web-inf\jpivot and webapps\mondrian\web-inf\wcf to the web-inf folder of your application e.g. webapps\myapp\web-inf
  • Copy the libraries in the mondrian\web-inf\lib folder to your applications web-inf\lib folder
  • Copy the file mondrian\web-inf\mondrian.properties to the myapp\web-inf\classes folder
  • Edit this mondrian.properties file to have contents like the following
# Allow the use of aggregates
mondrian.rolap.aggregates.Use=true
mondrian.rolap.aggregates.Read=true
mondrian.native.topcount.enable=true
mondrian.native.filter.enable=true

# result set limit
mondrian.result.limit=50000

# format sql if logging is configured to output sql or mdx
mondrian.rolap.generate.formatted.sql=true

# don't automatically load any drivers
mondrian.jdbcDrivers=

  • Create a file log4j.xml in the myapps\web-inf\classes folder with contents like the following
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">


  
    
    
      
    
  
  

  
        
 

   
        
 

   
        
 

 



 



  
    
    
  
  
  • Create a file named userconfig.xml in myapps\web-inf\jpivot\print with the following contents
<!-- empty configuration file to avoid error being logged because file is missing -->



web.xml modifications
  • Copy and adapt contents of mondrian\web-inf\web.xml to your application's web.xml file
  • Files that will launch pivot tables should be contained in the JpivotController filter mapping e.g.
 
    JPivotController
    /user/showAnalysis.jsp
  
  • You can change the location of the error and busy pages e.g.
 
    JPivotController
    com.tonbeller.wcf.controller.RequestFilter
    
      errorJSP      /user/jpivotError.jsp      URI of error page
    
    
      busyJSP      /user/jpivotBusy.jsp      This page is displayed if a the user clicks
        on a query before the previous query has finished
    
      

File displaying pivot table
  • Use the mondrian\testpage.jsp file as a template for the file that displays pivot tables. Remove the <wcf:include add="" and="" for="" li="" logic="" mdx="" own="" query<="" section="" the="" your="">
if(request.getParameter("action")==null && request.getParameter("null")==null){
...
 %>

<jp:mondrianquery jdbcUrl="<%=databaseUrl%>" jdbcUser="<%=databaseUser%>" jdbcPassword="<%=databasePassword%>" catalogUri="<%=schemaFile%>">
<%=query%>

<% } %>
...

...

  • If you want to have a title for the pivot table, store it as a session attribute and retrieve it when you want to display it.

Alternative for getting Required files
  • Download the mondrian zip package from sourceforge. http://sourceforge.net/projects/mondrian/files/
  • Extract the mondrian zip file to a temporary folder e.g. C:\temp
  • Download the jpivot zip package from the jpivot website, jpivot.sourceforge.net
  • Extract the jpivot zip file to a temporary folder e.g. C:\temp
  • Copy all the jar files from the mondrian lib directory e.g. C:\temp\mondrian-3.2.0.13661\lib to the application's web-inf\lib directory
  • Rename the jpivot war file c:\temp\jpivot-1.8.0\jpivot.war to jpivot.zip and unzip the file
  • Copy the wcf folder to the application's root e.g. webapps\myapp
  • Copy the jpivot folder to the application's root
  • Copy the web-inf\wcf folder to the application's web-inf folder e.g. webapps\myapp\web-inf
  • Copy the web-inf\jpivot folder to the application's web-inf folder
  • Copy the files in the web-inf\lib folder to the application's web-inf\lib folder
  • Add the contents of the jpivot web.xml file to your application's web.xml file

Printing
  • To ensure is done correctly regardless of the path of the file displaying the pivot table change the wcf toolbar tags that do the printing to
 
  

Modifying pdf output header and footer
  • Printing to pdf generates a pdf file with default header and footer text. To modify this, edit the file myapp\web-inf\jpivot\table\fo_mdxtable.xsl


Maintaining scroll position
  • When you expand a dimension, the page is refreshed and the scroll position goes back to the top of the page. You then have to manually scroll to the element you just expanded. To maintain scroll position have the page that displays the pivot table to have code like the following





<jp:table id="table01" query="#{query01}">
...

Get the current mdx
  • If you want to get the mdx query for the currently displayed pivot table view, add code to the file that displays the pivot table similar to the following
<%@ page import="com.tonbeller.jpivot.table.TableComponent,com.tonbeller.jpivot.olap.model.*,com.tonbeller.jpivot.tags.OlapModelProxy" %>
<%@ page import="com.tonbeller.jpivot.olap.query.MdxOlapModel" %>

<%
//get the current mdx
TableComponent table = (TableComponent) session.getAttribute("table01"); 
//assuming table has id of table01 e.g. <jp:table String mdx="";
if( table != null ) {
    OlapModel olapModel = table.getOlapModel();
    while( olapModel != null ) {
        if( olapModel instanceof OlapModelProxy ) {
            OlapModelProxy proxy = (OlapModelProxy) olapModel;
            olapModel = proxy.getDelegate();
        }
        if( olapModel instanceof OlapModelDecorator) {
            OlapModelDecorator decorator = (OlapModelDecorator) olapModel;
            olapModel = decorator.getDelegate();
        }
        if( olapModel instanceof MdxOlapModel) {
            MdxOlapModel model = (MdxOlapModel) olapModel;
            mdx = model.getCurrentMdx();
            olapModel = null;
        }
    }
}
%>

Clear the mondrian cache

  • Mondrian uses a cache held in memory to make analysis faster. Every unique mondrian connection [connection string] has a cache. If the underlying database changes and you need to clear the cache, you can use a jsp page like the following
<%@ page contentType="text/html; charset=UTF-8" %>

<%
//clear all mondrian caches
java.util.Iterator<mondrian.rolap.rolapschema> schemaIterator =  mondrian.rolap.RolapSchema.getRolapSchemas();
while(schemaIterator.hasNext()){
    mondrian.rolap.RolapSchema schema = schemaIterator.next();
    mondrian.olap.CacheControl cacheControl = schema.getInternalConnection().getCacheControl(null);
        
    cacheControl.flushSchemaCache();  
}
  
%>
Errors and possible causes
  • 404 - Invalid url
  • No metadata for catalog - Misspelt catalog name. Names are case sensitive
  • XMLA connection datasource not found - Misspelt datasource name. Names are case sensitive
  • XMLA Discover unparse results error - Definition file in catalog section of datasources.xml does not exist. If change made to datasources.xml, app has to be redeployed for the changes to take effect

Sample project
The ART reporting tool, http://art.sourceforge.net, makes use of mondrian + jpivot for OLAP queries (pivot tables). It's source code can be used as a reference. It uses custom jpivot.jar, wcf.jar and tbutils-wcf.jar files.

Thursday, July 15, 2010

Using html2ps

html2ps is a perl script available from http://user.it.uu.se/~jan/html2ps.html that can be used to convert html to postscript [and then you can convert the postscript to pdf].

Prerequisites
Perl
Ghostscript
GSView [for viewing postscript files]
ImageMagick

Versions used
html2ps – 1.0 beta7
ActivePerl – 5.8.8.820
Ghostscript – 8.71
GSView – 4.9
ImageMagick - ImageMagick-6.6.2-3-Q16-windows-dll


Installation
  • Download the zip package from http://user.it.uu.se/~jan/html2ps.html
  • Extract the zip package to c:\
  • Rename the extracted folder to c:\html2ps
  • Download ghostscript from http://pages.cs.wisc.edu/~ghost/
  • Run the ghostscript installation file
  • Add ghostscript to the system path. Add both the bin and lib directories
  • Download the windows binary release exe of InstallMagick from http://www.imagemagick.org/script/index.php
  • Install InstallMagick and specify that the imagemagick installation path should be included in the system path
  • Ensure the perl bin directory is in the system path
  • Open a command prompt window
cd c:\html2ps
perl install
  • Accept the installation script defaults. When asked to enter the name of this directory, type c:\html2ps
  • Once the install script is finished, edit the file c:\html2ps\html2ps. Replace the line $tmpname=$posix?POSIX::tmpnam():"h2p_$$"; with
$tmpname=$posix?POSIX::tmpnam():"h2p_$$";
if($^O =~ m/win/i) {
$tmpname="h2p_$$";}
  • Ghostscript and ImageMagick aren't required for html2ps to work, but some configuration parameters and documents may need them, or other additonal libraries.


Converting a html file to postscript
Example converting the html2ps user guide
cd c:\html2ps
perl html2ps -d -D -f sample -o test.ps html2ps.html


Converting the postscript file to pdf
Use ps2pdf that comes with the ghostscript installation
ps2pdf test.ps test.pdf


Locating the table of contents at the beginning of the document
You can modify many aspects of the postscript file generated by html2ps. This would involve creating and modifying a configuration file. The file "sample" is one such configuration file. You can make a copy of it and add your own modifications to customize the file generated. Review the html2ps user guide html document for configuration options. As an example, you can set the table of contents to be generated at the start of the document instead of the end. Modify the file sample, editing the toc line to the following

option {
toc: hb;

Run html2ps and ps2pdf to confirm that the table of contents is now at the beginning of the document.

perl html2ps -d -D -f sample -o test.ps html2ps.html
ps2pdf test.ps test.pdf


Correcting display of euro signs in pdf bookmarks
If generating a table of contents, after converting the postscript file to pdf the pdf bookmarks may be displayed with two euro signs at the beginning of the bookmark text. To correct this, edit the file html2ps. Replace the line $dh.="/h$nhd [($hind\\240\\240)($htxt)] D\n"; with

$dh.="/h$nhd [($hind\\56\\40)($htxt)] D\n";

Replace the line $toc.="$hv NH le{$nref($hind\\240\\240)$hv C($htxt)$nref 1 TN()EA()BN}if\n"; with

$toc.="$hv NH le{$nref($hind\\56\\40)$hv C($htxt)$nref 1 TN()EA()BN}if\n";


Processing a file with images
If an html file contains links to images held locally and referenced with relative links e.g. src="images/sample.png", use the base option when calling html2ps providing the base url to be appended to all relative links for images.

perl html2ps -d -D -b file:///c:/some/path/ -f sample -o example.ps c:\some\path\example.html


Changing the default look of hyperlinks
By default, text for all links, both to internal document sections and to external web locations, are rendered in a final pdf surrounded by boxes. To have them rendered without the boxes, add a definition to the style sheet definitions in the configuration file used.

A:link { color: blue }
The color must be something other than black. In addition, when running html2ps, you'll need to add the -U parameter.

perl html2ps -d -D -U -f myconfig.txt -o example.ps example.html


Left aligning H1 elements
The example configuration file "sample" provided specifies that H1 elements are centred. If left aligning is required, remove the text-align: center portion of the H1 style rule.


Undesired blank pages
By default, an extra (empty) page is printed, when necessary, to ensure that the title page, the table of contents, and the document itself will start on odd pages. This is typically desirable for double sided printing. If this is not desired, add the extrapage flag to the @html2ps block of the configuration file, setting it to 0.

@html2ps {
extrapage: 0;


Starting new pages
You can have a page break inserted anywhere in the html text, e.g. before H1 elements. To do this, modify the source html and insert <!--NewPage-->


Using CSS
html2ps ignores css contained in the html document. You can define styles in the configuration file. Only a subset of css is supported by html2ps. This subset is outlined in the user guide, in the CSS2 blocks section.


Using custom colours
By default html2ps only recognizes 16 colours. These are defined in the colour block of the html2ps file. To use additional colours, e.g. in css rules, edit your configuration file. In the @html2ps block, add a colour block with the custom colours you use in the style rules. e.g.
Colour{
brown: A52A2A;
}


The user guide that comes with html2ps has explanations for all possible options for modifying the look of the generated postscript, and possibly eventual pdf document.

Tuesday, May 11, 2010

Using Quartz

Quartz is an open source job scheduling library that can be used within a Java application.

Using quartz in a Java EE application

Prerequisistes
JRE
Servlet engine
RDBMS + JDBC driver for storing jobs in a database

Versions used
JRE 1.6.0_20
Apache Tomcat 6.0.20
MySQL 5.0.45
MySQL connector/J 5.1.10
Quartz 1.8.0

Steps
  • Download the package from the quartz website, http://www.quartz-scheduler.org/
  • Unzip the package to a temporary location e.g. C:\temp
  • Add the quartz tables to your application's database schema. The scripts with the quartz table schemas will be in c:\temp\quartz-1.8.0\docs\dbtables.
C:\> mysql -h localhost --database=mydb --user=dbuser --password=dbpassword
mysql> \.  c:\temp\quartz-1.8.0\docs\dbtables\tables_mysql_innodb.sql
mysql> quit
  • Create indexes on the quartz tables just created by running the following additional script
create index idx_qrtz_t_next_fire_time on qrtz_triggers(NEXT_FIRE_TIME);
create index idx_qrtz_t_state on qrtz_triggers(TRIGGER_STATE);
create index idx_qrtz_t_nf_st on qrtz_triggers(TRIGGER_STATE,NEXT_FIRE_TIME);
create index idx_qrtz_ft_trig_name on qrtz_fired_triggers(TRIGGER_NAME);
create index idx_qrtz_ft_trig_group on qrtz_fired_triggers(TRIGGER_GROUP);
create index idx_qrtz_ft_trig_n_g on qrtz_fired_triggers(TRIGGER_NAME,TRIGGER_GROUP);
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(INSTANCE_NAME);
create index idx_qrtz_ft_job_name on qrtz_fired_triggers(JOB_NAME);
create index idx_qrtz_ft_job_group on qrtz_fired_triggers(JOB_GROUP);
  • Copy the file c:\temp\quartz-1.8.0\quartz-all-1.8.0.jar to your application's web-inf\lib folder e.g. tomcat\webapps\myapp\web-inf\lib
  • Copy all the jar files in c:\temp\quartz-1.8.0\lib to tomcat\webapps\myapp\web-inf\lib
  • Create a file named quartz.properties in myapp\web-\classes with the following details
# quartz configuration

# jobstore
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

# datasource
org.quartz.jobStore.dataSource = anyString

org.quartz.dataSource.anyString.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.anyString.URL = jdbc:mysql://localhost/mydb
org.quartz.dataSource.anyString.user = dbuser
org.quartz.dataSource.anyString.password = dbpassword
org.quartz.dataSource.anyString.validationQuery=select 1

# thread pool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5

# disable quartz version update check
org.quartz.scheduler.skipUpdateCheck=true
  • Create a file named log4j.xml in myapp\web-\classes with the following details
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

  <appender name="default" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="[%p] %d{dd MMM yyyy HH:mm:ss.SSS} %t [%c]%n%m%n%n"/>
    </layout>
  </appender>
    
 <logger name="org.quartz">
   <level value="info" />     
 </logger>

  <root>
    <level value="warn" />
    <appender-ref ref="default" />
  </root>
  
</log4j:configuration>
  • Create a servlet class to be running the scheduler
package my.app;

import javax.servlet.*;
import javax.servlet.http.*;

import org.quartz.impl.StdSchedulerFactory;
import org.quartz.utils.*;

import org.quartz.*;

public class TestScheduler extends HttpServlet
{
Scheduler myscheduler;
public void init(ServletConfig config) throws ServletException
{
try
{
//start scheduler and run a test job
// Initiate a Schedule Factory
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        // Retrieve a scheduler from schedule factory
        myscheduler = schedulerFactory.getScheduler();
                        
        // Initiate JobDetail with job name, job group, and executable job class
        JobDetail job1 = new JobDetail("myjobDetail", "myjobDetailGroup", MyJob.class);
        // Initiate SimpleTrigger with its name and group name
        SimpleTrigger simpleTrigger = new SimpleTrigger("mysimpleTrigger", "mytriggerGroup");
        
//example setting int parameter for the job
job1.getJobDataMap().put("int-parameter-name",5);

//schedule the job and start the scheduler
myscheduler.scheduleJob(job1, simpleTrigger);
        
        // start the scheduler
        myscheduler.start();
}
catch(Exception e)
{
System.err.println(e);
}
}

public void destroy()
{
//shut down the scheduler
try
{
myscheduler.shutdown(true);
}
catch(Exception e)
      {
      System.err.println(e);
      }
}
}
  • Include the scheduler class in the application's web.xml file so that it runs on startup
 <servlet>
  <servlet-name>TestScheduler</servlet-name>
  <servlet-class>my.app.TestScheduler</servlet-class>
  <!-- Load this servlet at server startup time. Number not special. Just indicates the sequence of loading servlets -->
  <load-on-startup>3</load-on-startup>
 </servlet>
  • Create a class that will be doing the work. The job class. This class needs to implement the org.quartz.job interface
package my.app;

import org.quartz.*;

public class MyJob implements Job
{

//no-argument public constructor
public MyJob()
{
}

//execute method of job interface that does the work
public void execute (JobExecutionContext context) throws JobExecutionException
{
//do anything here.

//you can take parameters passed by the scheduler and use them e.g
JobDataMap dataMap=context.getMergedJobDataMap();
int myIntVariable;
myIntVariable=dataMap.getInt("int-parameter-name");

if (myIntVariable==1)
{ 
//do something
}
else
{
//do something else
}
}
}
  • Instead of creating your own class to start the scheduler, you can use one provided by quartz. Modify the web.xml to have the following
<servlet>
    <servlet-name>
        QuartzInitializer
 </servlet-name>
    <display-name>
        Quartz Initializer Servlet
 </display-name>
    <servlet-class>
        org.quartz.ee.servlet.QuartzInitializerServlet
 </servlet-class>
    <load-on-startup>3</load-on-startup>    
</servlet>
  • A default scheduler instance will now be automatically created and started when the application starts, and automatically shut down when the application is stopped.

  • To access this scheduler within the application e.g. In a jsp page, you can retrieve the scheduler instance from the servlet context. You can have the following
<%@ page import="org.quartz.*,org.quartz.impl.*,org.quartz.utils.*,org.quartz.ee.servlet.QuartzInitializerServlet" %>

<%
  StdSchedulerFactory factory = (StdSchedulerFactory) getServletConfig().getServletContext().getAttribute(QuartzInitializerServlet.QUARTZ_FACTORY_KEY);
  Scheduler scheduler=factory.getScheduler();
  
  // Initiate JobDetail with job name, job group, and executable job class
        JobDetail job1 = new JobDetail("myjobDetail", "myjobDetailGroup", MyJob.class);
        // Initiate SimpleTrigger that will fire immediately
        SimpleTrigger simpleTrigger = new SimpleTrigger("mysimpleTrigger", "mytriggerGroup");        
        job1.getJobDataMap().put("int-parameter-name",5);                
        
        scheduler.scheduleJob(job1, simpleTrigger);
  %>
  • You can create jobs and triggers according to the application's logic and user inteface components used and then schedule using the scheduler object.


Deleting jobs and triggers
You can't add a trigger or job if another one with a similar name and group exists. Use methods of the scheduler object to do the deletion. An exception is not raised if the job or trigger doesn't exist.
scheduler.deleteJob("job name","job group");
scheduler.unscheduleJob("trigger name","trigger group");

Checking if a cron expression is valid
If using a cron trigger, and the expression provided isn't valid, an exception will be raised when creating the trigger. To avoid this you can check whether the expression is valid before creating the trigger object. There's a static method in the CronExpression class for this.
if (CronExpression.isValidExpression(myCronString)){

Determining next run job run time
You can determine the next time a job will run using the trigger's getFireTimeAfter method
java.util.Date nextRunDate=myTrigger.getFireTimeAfter(new java.util.Date())

Or within the job implementation's execute method,
public void execute(JobExecutionContext context) throws JobExecutionException {

java.util.Date nextRunDate=context.getTrigger().getFireTimeAfter(new java.util.Date());

}

Setting job end date
You can set the date on which a job should start or end using the associated trigger's setStartTime and setEndTime methods. By default, a trigger's start time is the time the object is instantiated with no end date. One can set the end date without specifying the start date and vice versa. The end date can't be before the start date, else an exception will be thrown.

Setting quarz properties in code
Instead of having the quartz configuration properties residing in a properties file, you can create a scheduler instance with the properties defined from code. For instance if you don't want to have the database username/password in clear text in the properties file. You'll need to create a java.util.Properties object, populate it with all the relevant quartz properties and then pass the properties object to the StdSchedulerFactory constructor e.g.
import java.util.*;
import org.quartz.*;
import org.quartz.impl.*; 

props=new Properties();
props.setProperty("org.quartz.threadPool.threadCount","10");
//...set other properties

//create scheduler instance 
SchedulerFactory schedulerFactory = new StdSchedulerFactory(props);      
org.quartz.Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start(); 

//if doing this from a servlet that's loaded on startup, you can put the scheduler instance in the servlet context so that you can access it from anywhere within the application

//save scheduler in the servlet context, to make it accessible throughout the application
getServletConfig().getServletContext().setAttribute("myscheduler",scheduler);

//to access the scheduler elsewhere in the application e.g. to schedule new jobs
Scheduler scheduler=(Scheduler) getServletConfig().getServletContext().getAttribute("myscheduler");
scheduler.scheduleJob(someJobObject, someTriggerObject);

//make sure to call the scheduler's shutdown method in the servlet's destroy method
If creating the scheduler instance like this, you won't need the QuartzInitializerServlet entry in the web.xml file.

Friday, April 16, 2010

Using Apache

Disable caching

  • Uncomment the line #LoadModule expires_module modules/mod_expires.so in the httpd.conf file
  • Create a file named .htaccess under the relevant website directory and add the text

# enable expiration
ExpiresActive On

#expire all pages to prevent caching
ExpiresDefault "access plus 1 second"
  • Restart Apache

Use folder structure not in the document root
  • Add an alias in the httpd.conf file e.g.
Alias /mysite "c:/somefolder/mysite"


    AllowOverride None    
    Options Indexes FollowSymLinks ExecCGI
    Order allow,deny
    Allow from all

Using PostgreSQL

Creating a database
createdb -U postgre_user database_name
[assumes postgre bin location has been added to the system path]

Starting the command line client
psql -U postgre_user

Changing database within psql
postgres=# \c mydatabase

Using MySQL

Using keywords as column names or in queries
Enclose keywords around backticks e.g. Select `desc` from mytable

Using Activelock

Activelock is an open source software licensing and copy protection component for windows applications. Its source is available in VB6 and VB.NET.

Prerequisites
Visual Studio 6 with SP6
MSXML 4
Visual Studio 2008
IIS with ASP.NET configured (only for testing web integration)

Version used
Activelock 3.6.0.3
Visual Studio 6 with SP6
.NET framework 3.5
Visual Studio 2008 Express edition
IIS on Windows XP (IIS 5.1)


Installation
  1. Download the VB6 and VB2008 core dlls and source code packages from the activelock website, activelocksoftware.com
  2. Run the setup programs to install the activelock files

Customizing the VB6 dll
  1. Copy the contents of the dll source folder C:\Program Files\Activelock_VB6_3.6\Activelock3.6 for VB6 to your own folder e.g. C:\MyLock
  2. Copy the contents of the test application folder C:\Program Files\Activelock_VB6_3.6\ALTestApp3.6 for VB6 to your own folder e.g. C:\MyLock TestApp
  3. Using the code in the C:\MyLock folder, open the file ActiveLock3.vbp
  4. Save the project with a different name e.g. MyLock.vbp
  5. Change the name of the project under project properties e.g. MyLock
  6. Right click on the project and click on the properties menu
  7. Clear the help file text box
  8. Change the project description text box e.g. MyLock
  9. In the make tab, change the version number e.g. 1.0.0
  10. Change the application title e.g MyLock
  11. Change all the items under version information as desired
  12. Open a code window and replace all occurrences of the string ActiveLock3 with e.g. MyLock. Ensure the Find whole word only option is selected.
  13. In activelock.cls, change the value of the AL_REGISTRY_HIVE$ constant
  14. In activelock.cls, modify the ValidateShortKey procedure. Comment out the section referring to alcrypto, only leaving the automatic registration code. If you don't want the short key to be tied to a specific computer, comment out the line fprint = modHardware.GetFingerprint()
  15. In activelock.cls, modify the IactiveLock_InstallationCode Get procedure. Don't pass the fingerprint to the GenerateShortSerial function. This is also only if the short key isn't required to be tied to a specific computer
  16. Make any other customizations as required
  17. Add a reference to the Microsoft WMI Scripting V1.1 Library
  18. If you don't have MDAC 2.8, remove the existing reference and select the reference for the highest version of Microsoft ActiveX Data Objects Library you have.
  19. Compile the dll, giving it a different name e.g MyLock.dll
  20. Select the project properties, and under the component tab, set version compatibility to binary compatibility, referencing the dll just compiled.
  21. Compile the dll again.
  22. Close the project.

Customizing the key generator
  1. Using the code in the C:\MyLock folder, open the file Alugen.vbp
  2. Save the project with a different name e.g. MyGenerator.vbp
  3. Change the name of the project e.g. MyGenerator
  4. Open a code window and replace all occurrences of the string ActiveLock3 with the same name used in the dll e.g. MyLock
  5. Under project references, remove the reference to the ActiveLock Object Library 3.6 and add a reference to the modified dll you've compiled e.g MyLock.dll.
  6. Build the exe, giving it a different name e.g. MyGenerator.exe
  7. Close the project

Customizing the test application
  1. Using the code in the C:\MyLock TestApp folder, open the file ALVB6Sample.vbp.
  2. Save the project with a different name e.g. MyLockTestApp.vbp
  3. Open a code window and replace all occurrences of the string ActiveLock3 with the same name used in the dll e.g. MyLock
  4. Under project references, remove the reference to the ActiveLock Object Library 3.6 and add a reference to the modified dll you've compiled.
  5. In the form_load procedure of frmMain, comment out the line with CheckForResources
  6. In the form_load procedure of frmMain, modify the software password e.g. .SoftwarePassword="pass"
  7. In the form_load procedure of frmMain, modify the licence key type to .LicenseKeyType = alsShortKeyMD5. Only if a short key will be used
  8. Build the exe, giving it a different name e.g. MyLockTestApp.exe
  9. Close the project

Generating an installation code
  • Create a new project and add two text boxes to it, txtUser and txtInstallCode
  • Add a reference to the modified dll, MyLock.dll
  • Open a code window and paste the following code
Option Explicit

Private ActiveLock As DVTransManager.IActiveLock
Private WithEvents ActiveLockEventSink As ActiveLockEventNotifier

Private Sub Form_Load()
    InitializeActiveLock
End Sub

Private Sub txtUser_Change()
    txtInstallCode.Text = ActiveLock.InstallationCode(txtUser.Text)
    
    On Error GoTo 0
    Exit Sub
End Sub

Private Sub InitializeActiveLock()
    Set ActiveLock = DVTransManager.NewInstance()
    
    Set ActiveLockEventSink = ActiveLock.EventNotifier
    
    With ActiveLock
        .SoftwareName = "ALVB6Sample"
        .SoftwareVersion = "3.6"
        .SoftwarePassword = "pass"
        .SoftwareCode = "RSA1024BgIAAAAkAABSU0ExAAQAAAEAAQCBcwKp9p1rkQhZyxTeREh9EM273wBqpODS+KLkeu/xn/Q0+w8uhBQfZq8f/sdRfL+S5LIBItOv0okG42mKcaNk0mRoSoJkUPMrRp43j8nAKVCmRrD7pZ1Do3uHM4SjydLY0omeK8vOCyZ2WldYy0IxwgQjNqMHLuG1rCg1DR4+5g=="
        
        .LicenseKeyType = alsShortKeyMD5
        .LockType = lockNone
    End With
   
    ActiveLock.Init
End Sub

  • Run the program and in the txtUser text box, type in "test"
  • Copy the installation code displayed

Generate and test a licence key
  1. Run MyGenerator.exe
  2. In the licence keygen tab, make sure the product is ALVB6Sample – 3.6
  3. Paste the installation code in the installation code text box
  4. In the user name text box, type in "user"
  5. Click on the Generate button to generate the licence key
  6. Run MyLockTestApp.exe
  7. In the user name text box, type in "user"
  8. In the installation code text box, paste the installation code used in the key generator
  9. In the liberation key text box, paste the licence key generated by the key generator
  10. Click on the Register button
  11. Registration should be successful, indicating that you have successfully created a licence key and had it verified.

Using with a new product
  1. For your own custom application, you start by creating a new product in the key generator.
  2. Type in a name and version for your product and click on the Generate button to generate the product codes. The codes are a public/private key-pair with the Vcode being the public key, and the Gcode the private key. The codes are specific to a particular product name and version combination
  3. Click on Add to product list to add the new product to the list of known products
  4. The source code in the test application can be used as a guide for your own windows application. Further customization would be required e.g to include different product details, and apply custom stealth measures if desired.


Customizing the VB.NET version
To customize the VB.NET version of Activelock, download and install the corresponding VB.NET package and make code changes in the same places as the VB6 version.
The product codes, installation codes and licence keys [liberation keys] generated by the VB.NET and VB6 versions are not the same. This means a licence key generated by the VB.NET key generator will not be valid when an attempt to register the licence is made from the VB6 component and vice versa.


Web integration for the VB6 version
One may require to automate licence key generation and distribution through a website. If your windows application is using the VB6 component to verify licence keys, you'll need to generate keys using the VB6 key generator.
  1. Download and install the VB6 version of Activelock
  2. Copy the following files from C:\Program Files\Activelock_VB6_3.6\Activelock3.6 for VB6 to your own folder e.g. C:\MyWebGenerator. modActiveLock.bas, modALUGEN.bas and modWindowsVersion.bas.
  3. Open the VB6 IDE and create a new ActiveX DLL project
  4. Give the project a name e.g. MyWebGenerator and change the name of the default class e.g WebGenerator and save the project to the folder you created e.g. C:\MyWebGenerator
  5. Add the module files to the project
  6. Open the project file Alugen.vbp from C:\Program Files\Activelock_VB6_3.6\Activelock3.6 for VB6
  7. Create a public function in you class e.g. GenerateLicenceKey and use code from the cmdKeyGen_Click procedure of the Alugen project to implement the licence key generation as per your requirements.
  8. Declare a private class variable for the Activelock object and use the InitActiveLock procedure from frmMain in the Class_Initialize method.
  9. Add a project reference to the VB6 Activelock dll, perhaps one which you've modified and compiled.
  10. Compile your new dll e.g. MyWebGenerator.dll

Using the VB6 key generation dll in an ASP.NET website
  1. Set up an IIS server that can serve ASP.NET pages
  2. Copy MyWebGenerator.dll to the web server, if it's on a different machine, e.g to a folder C:\Components
  3. Register MyWebGenerator.dll on the web server
  4. Use the tlbimp.exe utility that comes with the .NET framework to create a runtime callable wrapper for the ActiveX dll you've just compiled. This is to allow you to use the functions of the dll from a .NET development environment just like it was a .NET component.
  5. Search for the file tlbimp.exe on the machine and copy it to C:\Components
  6. Open a command prompt window and navigate to C:\Components
  7. Run tlbimp giving a new name for the .NET wrapper. The syntax to use would be something like tlbimp MyWebGenerator.dll /out:MyWebGeneratorRCW.dll /verbose
  8. Create a new website project e.g using Visual Web Developer Express edition
  9. Under the Website | Add Reference menu, browse and add a reference to the wrapper dll MyWebGeneratorRCW.dll
  10. Use the ildasm.exe utility that comes with the .NET framework to confirm the name of the namespace, class and methods in the wrapper dll you've just created. Run ildasm and open the MyWebGeneratorRCW.dll. The namespace is the name of the node directly under the MANIFEST node. Classes are blue nodes and interfaces are blue nodes with an I.
  11. Add a line in your webpage to import the wrapper namespace as indicated by ildasm e.g <%@ Import Namespace="MyWebGeneratorRCW" %>
  12. Instantiate and use the methods in your dll as you would any .NET component in ASP.NET.


Web integration for the VB.NET version
Create a .NET dll using code from the VB.NET ALUGEN, just like is done for the VB6 version. Since ASP.NET will use the .NET dlls without having to jump any hoops, add a reference to this dll and use as required.

Using Subversion

Creating a repository
  • Create a new repository using visualsvn server

  • In windows explorer, right click on the root directory of items you want to add to source control and select TortoiseSVN > Import.Type the path to parent folder to create in your repository e.g. file:///C:/svn/myrepository/myfolder [don't specify the repository root path e.g. svn/myrepository]. Click on OK. This will add all files and subfolders under that folder to source control.
  • Right click on the folder again and click on SVN Checkout. Note the repository URL and checkout directory and amend as appropriate.
  • Right click on the folder and select SVN Commit

  • To add only selected files or folders, in windows explorer, from any directory, right click and select TortoiseSVN > Repo-browser. Type the URL of the repository just created.
  • Create the directory structure you want for your repository structure. Right-click in the left pane of the repository browser and select Create folder as appropriate.
  • Click on OK to close the repository browser.
  • Right click on the folder to use as the working directory and select SVN Checkout.
  • Set the repository URL e.g. file:///C:/svn/myrepository/myfolder and the checkout directory and click on OK
  • Accept any warning message that the target folder is not empty. Already existing files in the folder won't be deleted
  • To add files to source control, right click on the file and select TortoiseSVN > Add. Click on OK to mark the file for adding to source control.
  • When finished adding files, right click on the working folder and select SVN Commit. Click on OK to add the files to source control.
  • Items are now under version control

Daily use
  • To check in items, right click and select SVN Commit
  • To check out, no special action is needed. SVN by default uses an update-and-merge technique rather than lock-update-check-in, so that multiple people can update the same file at the same time. If you require to lock the file so that you are the only one who can modify it, right click and select TortoiseSVN > Get lock. Use TortoiseSVN > Release lock when finished.
  • To get latest, right click and select SVN update
  • To remove a file from version control, shift+right click on the file while viewing it from the right hand pane of windows explorer and choose the TortoiseSVN > Delete (keep local) menu. This will mark it for deletion from svn but keep the local copy. Right click on the file and select SVN Commit to effect the change. The normal TortoiseSVN > Delete command will immediately delete a file from the working folder. For a folder, it's marked for deletion and actually deleted when you commit. You can get a file back by browsing the repository and copying it from a previous revision, before it was deleted. Use TortoiseSVN > Revert to unmark an item previously marked for deletion.
  • To remove a folder from version control, right-drag the working copy onto itself. Windows explorer doesn't allow dragging items onto themselves in the same window though, so you must either use a second explorer window or drag onto the tree view. On the pop-up menu provided select SVN Export to here and accept to unversion the folder.
  • To export a folder, such that a copy is made to a different location without the .svn directories, right click on the folder and select TortoiseSVN > Export.
  • To create a tag or branch, right click on the folder, select TortoiseSVN > Branch/tag and in the To path: section, enter the path of the new branch/tag e.g. /tags/version2.2. The base directory e.g. tags in this case, needs to exist already.

Right-drag
In Windows explorer, it is possible to move/copy files not only by dragging them with the left mouse button, but also with the right mouse button. The difference is that the left-drag executes the operation immediately, and the right-drag will first show you a context menu where you can choose the operation. TortoiseSVN adds some of its commands to that right-drag context menu.

To add the files/folders to the working copy, right-drag unversioned files/folders on to a versioned folder and select SVN Add files to this WC.

To export items, right-drag versioned files/folders from your working copy to an unversioned folder and select one of these two commands
  • SVN Export to here: exports the dragged files/folders to the target location, i.e. creates a copy without the .svn folders
  • SVN Export all to here: exports the dragged files/folders to the target location, including unversioned files.

To move and copy your files and folders around in your working copy, right-drag files/folders inside your working copy and select one of these four commands
  • SVN Move versioned files here: moves the files to the drop location. If a file already exists, it will ask you what to do (rename the file, overwrite it or cancel).
  • SVN Move and rename versioned files here: the same as 'SVN Move versioned files here', except that it will ask for a new name for every dropped file.
  • SVN Copy versioned files here: the same as 'SVN Move versioned files here' but it leaves the original file, i.e. the file is copied not moved
  • SVN Copy and rename versioned files here: the same as 'SVN Move and rename versioned files here' but it leaves the original file, i.e. the files are copied not moved