Thursday, January 26, 2012

Representing Java classes in diagrams

You probably wanted to display relationships from the Java classes in diagram if you needed to understand how the projects and files are related to each other. Eclipse can show you Call and Type hierarchy, but seeing it all in one graph can help you understand how project is structured. I have created the diagram for the docx4j using small application (using Apache BCEL 5.2) to create it (to import it I converted CSV file to XLSX and used yEd):


Application code that is creating CVS is this:
package ca.sapiens.main;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.JavaClass;

// TODO: Auto-generated Javadoc
/**
 * The Class MainApp.
 */
public class MainApp {

 /** The result. */
 private static Map<String, Set<String>> result = new HashMap<String, Set<String>>();

 /** The class files. */
 private static Set<File> classFiles = new HashSet<File>();

 /**
  * The main method.
  * 
  * @param args
  *            the arguments
  * @throws IOException
  *             Signals that an I/O exception has occurred.
  * @throws ClassNotFoundException
  *             the class not found exception
  */
 public static void main(String[] args) throws IOException, ClassNotFoundException {
  List<String> regexFilters = new ArrayList<String>();
  regexFilters.add("^java.*");
  regexFilters.add("^org.apache.*");

  List<String> projects = new ArrayList<String>();
  projects.add("c:\\workspace-rapidox\\docx4j-sapiens-2.7.1\\target\\classes\\");

  System.out.println("Starting with file processing");

  int filesCount = 0;

  for (String directory : projects) {
   classFiles.clear();
   System.out.println("Getting class files");
   getAllClassFiles(new File(directory));
   for (File file : classFiles) {
    filesCount++;
    System.out.println("Processing file " + file.getName());
    ClassParser parser = new ClassParser(file.getPath());
    JavaClass clazz = parser.parse();
    int index = clazz.getClassName().indexOf("$");
    if (index == -1)
     index = clazz.getClassName().length();
    String clazzName = clazz.getClassName().substring(0, index);
    if (result.get(clazzName) == null) {
     result.put(clazzName, new HashSet<String>());
    }

    for (Constant constant : clazz.getConstantPool().getConstantPool()) {
     if (constant != null) {
      if (constant instanceof ConstantClass) {
       ConstantUtf8 constantUtf8 = (ConstantUtf8) clazz.getConstantPool().getConstant(
         ((ConstantClass) constant).getNameIndex());
       String name = constantUtf8.getBytes().replaceAll("/", ".");
       // remove inner classes and enumerations
       int index2 = name.indexOf("$");
       if (index2 == -1)
        index2 = name.length();
       // remove arrays indicators
       String correctedName = name.substring(0, index2).replaceFirst("\\[\\w", "")
         .replaceAll("\\;", "");
       boolean pass = true;
       for (String regex : regexFilters) {
        if (Pattern.matches(regex, correctedName)) {
         pass = false;
         System.out.println("Removing " + correctedName);
        }
       }

       if (pass && !clazzName.equalsIgnoreCase(correctedName))
        result.get(clazzName).add(correctedName);
      }
     }
    }
   }
  }

  System.out.println("Writing data...");
  if (result.size() > 0) {
   writeResult("c:\\temp\\output.csv");
  }

  System.out.println("Finished with file processing. Processed " + filesCount + " files");
 }

 /**
  * Gets the all class files.
  * 
  * @param processingFile
  *            the processing file
  * @return the all class files
  */
 private static void getAllClassFiles(File processingFile) {
  if (processingFile.isDirectory()) {
   File[] files = processingFile.listFiles();
   for (File file : files) {
    getAllClassFiles(file);
   }
  } else if (processingFile.isFile() && processingFile.getName().endsWith(".class")) {
   classFiles.add(processingFile);
  }
 }

 /**
  * Write result.
  * 
  * @param filename
  *            the filename
  * @throws IOException
  *             Signals that an I/O exception has occurred.
  */
 private static void writeResult(String filename) throws IOException {
  BufferedWriter out = new BufferedWriter(new FileWriter(filename));

  out.write("Name,Link\n");
  for (Entry<String, Set<String>> entry : result.entrySet()) {
   for (String reference : entry.getValue()) {
    out.write(entry.getKey() + "," + reference + "\n");
   }
  }
  out.close();
 }
}
Once you have run this code, open file in Excel, save as XLSX and then open file in yEd. After that, choose Edge List and fill in your ranges. Import as Circular and then change Layout to Organic (I think that it is faster this way and it looks better). With graphs big as this one, some layouts will be extremely slow. What is useful in the end is that you can select neighboring nodes, and create new document out of that. That should give you a good overview of what your class is using or where it is being used.

Tuesday, January 24, 2012

Ruby log analyzer: rula

This would be my first Ruby application. Name of the application is rula, meaning RUby Log Analyzer. I started this project as I needed to do several searches on the log files that would include previous filters and that would not consume a lot of memory (I intended to run this on the server). I have submitted the code to Git, to location https://github.com/bcavlin/rula, so please feel free to check it out. Application is only working in console mode right now, but I intend to create curses support too. As far as the version, this would be version 0.1. Please feel free to develop it to suite your needs. Here is the image:

Friday, January 20, 2012

SyntaxHighlighter and blogger

If your blog uses SyntaxHighlighter from alexgorbatchev.com then setting it to display items correctly might require a small addition in styles. I needed this to make it look correct (additional style is to limit the length for mobile devices):

...
<!-- this is going in your template -->
<style type="text/css">
    .height_600px_class {height: 600px;}
    .overflow_y_hidden_class {overflow-y: hidden !important;}
</style>

<!--choose one of these for your code -->
<pre class="brush:xml; class-name:'height_600px_class'" style="height:400px; overflow:hidden;">
</pre>

<pre class="brush:xml; class-name:'overflow_y_hidden_class'" style="height:400px; overflow:hidden;">
</pre>
...

JSF, ICEfaces and resetting component values

I just spent some time trying to figure out how to reset the value of the HtmlSelectOneMenu when cancel button is clicked using ICEfaces. The solution is to add actionListener to your ice:commandButton (in this case Cancel) and to find any component that you want to reset. My reset code was:
...
public void resetData(ActionEvent ae){
    ((com.icesoft.faces.component.ext.HtmlInputText) ae.getComponent().getParent().getParent().findComponent("dateComponent")).setValue(null);
    ((com.icesoft.faces.component.ext.HtmlSelectOneMenu) ae.getComponent().getParent().getParent().findComponent("selectComponent")).resetValue();
}
...

Tuesday, January 17, 2012

MyEclipse and JUtils

This morning I needed to generate toString() for the few bean classes, but I could not find JUtils in MyEclipse 9 menus. I checked in the Common/plugins directory and found that it is already there but could not install it. What I did to get JUtils in MyEclipse is, I downloaded JUtils from sourceforge.net unzip it in MyEclipse 9/dropins folder. Restarted eclipse and system reported that it found new plugin. Done.

Thursday, January 12, 2012

Working with graphs: yEd

If you ever worked with Visio or needed to create some graphs, you will appreciate any tool that can simplify the process and as a plus, be freeware. One of such tools that I tried is yEd graph editor. This tools was written  in Java and it is absolutely amazing. It is simple, intuitive and can do everything that Visio can and even more. Just to mention some of the things that I found very useful:
  1. Creating your own shapes very easily
  2. Organizing shapes and lines in different layouts (and I can tell you that it works much better than Viso and it gives amazing results)
  3. Importing graphs from Excel (now this is very useful feature that gives you ability, for example, to have a list of classes in txt file, and to create Excel tables from it which you can import in yEd and entire graph with relationships that you defined will be created for you)
  4. Searching nodes, links and everything else is very simple and powerful as you have many options for search (for example you want to select nodes that are only of specific color)
 

Check out link for yEd Graph Editor if you want to download this tool.

Thursday, January 05, 2012

Spring and Hibernate

Creating configuration for Spring to use Hibernate is not difficult. Basic configuration can look like this, providing that you put in your variables. Just remember that you want annotation based configuration and then you can use @Transactional annotation for your classes/methods. Working with transactions has never been easier.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
    http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/your-data-source" />

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingResources">
            <list>
                <value>path/to/your/file/File1.hbm.xml</value>
                <value>path/to/your/file/File2.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            </props>
        </property>
        <property name="eventListeners">
            <map>
                <entry key="merge">
                    <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
                </entry>
            </map>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

Visual Studio and log4net

One of the projects here at Sapiens, required me to add logging to AddOn for the Word. I googled the subject and found a good logging framework, log4net. It is very similar to log4j if one is working with Java, so no surprises there. At first, I had a bit of difficulty on how to initially setup this, but I found a good post on this blog and a few other places. What this blog was missing is that once you download log4net, you need to decide which DLL you want to use, or otherwise, you will get compilation error. You can decide by going to Project->Properties->Application and under Target framework, you will find what is your current setting. You can then navigate to proper DLL.

Also, what I changed is location of my log, and added ${TMP}\myfile.log instead what original author put in place.

For all those experienced in Visual Studio, this would probably not be a problem, but I am a Java developer so this kind of post would help me :).

Spring and IBM MQ Series

Working with IBM MQ Series server can be at some times complicated, but if you follow a few basic steps, you will be able to connect and read/post messages from the MQ server. Posted is the configuration that will enable you to connect to your desired server.

Be careful what you set to transportType property as communication between client to server has to use same type as which is set on server.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:ctx="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/web-services 
    http://www.springframework.org/schema/web-services/web-services-2.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">

    <bean id="mqConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
        <property name="port" value="${mqSeriesQueueManagerPortNumber}" />
        <property name="hostName" value="${mqSeriesServerHostAddress}" />
        <property name="channel" value="${mqSeriesQueueManagerChannelName}" />
        <property name="queueManager" value="${mqSeriesQueueManager}" /> 
        <property name="CCSID" value="${mqSeriesQueueManagerCCSID}" />
        <property name="transportType">
            <util:constant static-field="com.ibm.mq.jms.JMSC.MQJMS_TP_CLIENT_MQ_TCPIP" />
        </property>
    </bean>

    <bean id="mqQueue_Reader" class="com.ibm.mq.jms.MQQueue">
        <property name="baseQueueName" value="${mqSeriesQueueName}" />
        <property name="CCSID" value="${mqSeriesQueueManagerCCSID}" />
        <property name="targetClient">
            <util:constant static-field="com.ibm.mq.jms.JMSC.MQJMS_CLIENT_NONJMS_MQ" />
        </property>
    </bean>

    <bean id="jmsQueueConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
        <property name="targetConnectionFactory" ref="mqConnectionFactory" />
        <property name="username" value="${mqSeriesUserName}" />
        <property name="password" value="${mqSeriesPassword}" />
    </bean>

    <bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.DynamicDestinationResolver"/>

    <bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="jmsQueueConnectionFactory" />
        <property name="destinationResolver" ref="jmsDestinationResolver" />
        <property name="pubSubDomain" value="false" />
        <property name="receiveTimeout" value="10000" />
    </bean>

    <bean id="messageListener" class="your.jms.JmsQueueListener"/>

    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsQueueConnectionFactory" />
        <property name="destination" ref="mqQueue_Reader" />
        <property name="messageListener" ref="messageListener" />
        <property name="receiveTimeout" value="3000" />
        <property name="maxMessagesPerTask" value="8" />
        <property name="autoStartup" value="true" />
        <property name="acceptMessagesWhileStopping" value="false" />
        <property name="maxConcurrentConsumers" value="3"/>
    </bean> 

</beans>

Wednesday, January 04, 2012

Spring configuration: web.xml

When dealing with web.xml there are only few basic step that you can implement and have your application running Spring in no time.

This is initialization:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <resource-ref>
        <res-ref-name>jdbc/your-data-source</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

This is configuration if you have web services:

    <servlet>
        <servlet-name>spring-web-service</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-web-service-servlet.xml </param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-web-service</servlet-name>
        <url-pattern>/web-service/*</url-pattern>
    </servlet-mapping>

This is configuration if you have regular servlets:

    <servlet>
        <servlet-name>spring-common</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-common-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-common</servlet-name>
        <url-pattern>/spring-common/*</url-pattern>
    </servlet-mapping>

Please be mindful about who loads which configuration file and at what time. For example, main context file is loaded in the beginning (when you deploy the application), but ws or servlet configuration might be initialized once you access them by the container and not by the Spring (depending on your configuration).

Tuesday, January 03, 2012

Working with VIM

VIM is one of the most useful editors that I have worked with (even though you will need a lot of time to learn and remember all commands). What you can accomplish with it is simply amazing, just if you remember some basic commands. The only other tools that I prefer more is Eclipse, but only because I work with Java most of the time.

Anyway, I just though to publish my config file, just in case anyone is having difficult time to get their own configuration. I copied most of mine from this blog, and changed a few things to better suit my needs. (BTW, config if located in your /Users/username/_vimrc file in Windows 7. I often find myself editing the file in Program Files/Vim directory :)).

Here is the config:

set nocompatible
source $VIMRUNTIME/mswin.vim
behave mswin

set diffexpr=MyDiff()
function MyDiff()
  let opt = '-a --binary '
  if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif
  if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif
  let arg1 = v:fname_in
  if arg1 =~ ' ' | let arg1 = '"' . arg1 . '"' | endif
  let arg2 = v:fname_new
  if arg2 =~ ' ' | let arg2 = '"' . arg2 . '"' | endif
  let arg3 = v:fname_out
  if arg3 =~ ' ' | let arg3 = '"' . arg3 . '"' | endif
  let eq = ''
  if $VIMRUNTIME =~ ' '
    if &sh =~ '\<cmd'
      let cmd = '""' . $VIMRUNTIME . '\diff"'
      let eq = '"'
    else
      let cmd = substitute($VIMRUNTIME, ' ', '" ', '') . '\diff"'
    endif
  else
    let cmd = $VIMRUNTIME . '\diff'
  endif
  silent execute '!' . cmd . ' ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3 . eq
endfunction

" Basics {
    set nocompatible " explicitly get out of vi-compatible mode
    set noexrc " don't use local version of .(g)vimrc, .exrc
    set background=dark " we plan to use a dark background
    set cpoptions=aABceFsmq
    "             |||||||||
    "             ||||||||+-- When joining lines, leave the cursor 
    "             |||||||      between joined lines
    "             |||||||+-- When a new match is created (showmatch) 
    "             ||||||      pause for .5
    "             ||||||+-- Set buffer options when entering the 
    "             |||||      buffer
    "             |||||+-- :write command updates current file name
    "             ||||+-- Automatically add <CR> to the last line 
    "             |||      when using :@r
    "             |||+-- Searching continues at the end of the match 
    "             ||      at the cursor position
    "             ||+-- A backslash has no special meaning in mappings
    "             |+-- :write updates alternative file name
    "             +-- :read updates alternative file name
    syntax on " syntax highlighting on
" }


" General {
    filetype plugin indent on " load filetype plugins/indent settings
    set autochdir " always switch to the current file directory 
    set backspace=indent,eol,start " make backspace a more flexible    
    set backupdir=c:/tmp/backup " where to put backup files
    set clipboard+=unnamed " share windows clipboard
    set directory=c:/tmp " directory to place swap files in
    set fileformats=unix,dos,mac " support all three, in this order
    set hidden " you can change buffers without saving
    set iskeyword+=_,$,@,%,# " none of these are word dividers 
    set mouse=a " use mouse everywhere
    set noerrorbells " don't make noise
    set whichwrap=b,s,h,l,<,>,~,[,] " everything wraps
    "             | | | | | | | | |
    "             | | | | | | | | +-- "]" Insert and Replace
    "             | | | | | | | +-- "[" Insert and Replace
    "             | | | | | | +-- "~" Normal
    "             | | | | | +-- <Right> Normal and Visual
    "             | | | | +-- <Left> Normal and Visual
    "             | | | +-- "l" Normal and Visual (not recommended)
    "             | | +-- "h" Normal and Visual (not recommended)
    "             | +-- <Space> Normal and Visual
    "             +-- <BS> Normal and Visual
    set wildmenu " turn on command line completion wild style
    " ignore these list file extensions
    set wildignore=*.dll,*.o,*.obj,*.bak,*.exe,*.pyc,
                    \*.jpg,*.gif,*.png
    set wildmode=list:longest " turn on wild mode huge list
" }
" Vim UI {
    set cursorline " highlight current line
    set incsearch " BUT do highlight as you type you 
                   " search phrase
    set laststatus=2 " always show the status line
    set lazyredraw " do not redraw while running macros
    set linespace=0 " don't insert any extra pixel lines 
                     " betweens rows
    set listchars=tab:>-,trail:- " show tabs and trailing 
    set matchtime=5 " how many tenths of a second to blink 
                     " matching brackets for
    set nohlsearch " do not highlight searched for phrases
    set nostartofline " leave my cursor where it was
    set novisualbell " don't blink
    set number " turn on line numbers
    set numberwidth=5 " We are good up to 99999 lines
    set report=0 " tell us when anything is changed via :...
    set ruler " Always show current positions along the bottom
    set shortmess=aOstT " shortens messages to avoid 
                         " 'press a key' prompt
    set showcmd " show the command being typed
    set showmatch " show matching brackets
    set statusline=%F%m%r%h%w[%L][%{&ff}]%y[%p%%][%04l,%04v]
    "              | | | | |  |   |      |  |     |    |
    "              | | | | |  |   |      |  |     |    + current 
    "              | | | | |  |   |      |  |     |       column
    "              | | | | |  |   |      |  |     +-- current line
    "              | | | | |  |   |      |  +-- current % into file
    "              | | | | |  |   |      +-- current syntax in 
    "              | | | | |  |   |          square brackets
    "              | | | | |  |   +-- current fileformat
    "              | | | | |  +-- number of lines
    "              | | | | +-- preview flag in square brackets
    "              | | | +-- help flag in square brackets
    "              | | +-- readonly flag in square brackets
    "              | +-- rodified flag in square brackets
    "              +-- full path to file in the buffer
" }

" Text Formatting/Layout {
    set completeopt= " don't use a pop up menu for completions
    set expandtab " no real tabs please!
    set formatoptions=rq " Automatically insert comment leader on return, 
                          " and let gq format comments
    set ignorecase " case insensitive by default
    set infercase " case inferred by default
    set wrap " do not wrap line
    set linebreak
    set shiftround " when at 3 spaces, and I hit > ... go to 4, not 5
    set smartcase " if there are caps, go case-sensitive
    set shiftwidth=4 " auto-indent amount when using cindent, 
                      " >>, << and stuff like that
    set softtabstop=4 " when hitting tab or backspace, how many spaces 
                       "should a tab be (see expandtab)
    set tabstop=8 " real tabs should be 8, and they will show with 
                   " set list on
" }

" Folding {
    set foldenable " Turn on folding
    set foldmarker={,} " Fold C style code (only use this as default 
                        " if you use a high foldlevel)
    set foldmethod=marker " Fold on the marker
    set foldlevel=100 " Don't autofold anything (but I can still 
                      " fold manually)
    set foldopen=block,hor,mark,percent,quickfix,tag " what movements
                                                      " open folds 
    function SimpleFoldText() " {
        return getline(v:foldstart).' '
    endfunction " }
    set foldtext=SimpleFoldText() " Custom fold text function 
                                   " (cleaner than default)
" }

" Plugin Settings {
    let b:match_ignorecase = 1 " case is stupid
    let perl_extended_vars=1 " highlight advanced perl vars 
                              " inside strings

    " TagList Settings {
        let Tlist_Auto_Open=0 " let the tag list open automagically
        let Tlist_Compact_Format = 1 " show small menu
        let Tlist_Ctags_Cmd = 'ctags' " location of ctags
        let Tlist_Enable_Fold_Column = 0 " do show folding tree
        let Tlist_Exist_OnlyWindow = 1 " if you are the last, kill 
                                        " yourself
        let Tlist_File_Fold_Auto_Close = 0 " fold closed other trees
        let Tlist_Sort_Type = "name" " order by 
        let Tlist_Use_Right_Window = 1 " split to the right side
                                        " of the screen
        let Tlist_WinWidth = 40 " 40 cols wide, so i can (almost always)
                                 " read my functions
        " Language Specifics {
            " just functions and classes please
            let tlist_aspjscript_settings = 'asp;f:function;c:class' 
            " just functions and subs please
            let tlist_aspvbs_settings = 'asp;f:function;s:sub' 
            " don't show variables in freaking php
            let tlist_php_settings = 'php;c:class;d:constant;f:function' 
            " just functions and classes please
            let tlist_vb_settings = 'asp;f:function;c:class' 
        " }
    " }
" }

" Mappings {
    " this is to enable line by line browsing when wrap is on, or otherwise,
    " vim will skip entire line
    map <Down> gj
    map <Up> gk

    " space / shift-space scroll in normal mode
    noremap <S-space> <C-b>
    noremap <space> <C-f>
" }

" Autocommands {
    " Ruby {
        " ruby standard 2 spaces, always
        au BufRead,BufNewFile *.rb,*.rhtml set shiftwidth=2 
        au BufRead,BufNewFile *.rb,*.rhtml set softtabstop=2 
    " }
    " Notes {
        " I consider .notes files special, and handle them differently, I
        " should probably put this in another file
        au BufRead,BufNewFile *.notes set foldlevel=2
        au BufRead,BufNewFile *.notes set foldmethod=indent
        au BufRead,BufNewFile *.notes set foldtext=foldtext()
        au BufRead,BufNewFile *.notes set listchars=tab:\ \ 
        au BufRead,BufNewFile *.notes set noexpandtab
        au BufRead,BufNewFile *.notes set shiftwidth=8
        au BufRead,BufNewFile *.notes set softtabstop=8
        au BufRead,BufNewFile *.notes set tabstop=8
        au BufRead,BufNewFile *.notes set syntax=notes
        au BufRead,BufNewFile *.notes set nocursorcolumn
        au BufRead,BufNewFile *.notes set nocursorline
        au BufRead,BufNewFile *.notes set guifont=Consolas:h12
        au BufRead,BufNewFile *.notes set spell
    " }
    au BufNewFile,BufRead *.ahk setf ahk 
" }

" GUI Settings {
if has("gui_running")
    " Basics {
        colorscheme molokai " my color scheme (only works in GUI)
        set columns=180 " perfect size for me
        set guifont=Consolas:h12 " My favorite font
        set guioptions+=cerbm 
        "               ||
        "               |+-- use simple dialogs rather than pop-ups
        "               +  use GUI tabs, not console style tabs
        set lines=55 " perfect size for me
        set mousehide " hide the mouse cursor when typing
    " }
endif
" }

" Useful stuff {
" I use this when publishing code to blog
function HtmlEscape()
    silent %s/&/\&amp;/eg
    silent %s/</\&lt;/eg
    silent %s/>/\&gt;/eg
    silent %s/©/\&copy;/eg
endfunction

map <F12> <ESC>:call HtmlEscape()<CR>
" }


Spring configuration: applicationContext

When I started working with Spring framework, I immediately recognized the value and simplicity of using such framework to develop and maintain J2EE applications. It is collection of best practices for coding, great design patterns implementation (some of them), and fantastic guideline for putting enterprise level applications together. However, starting with Spring can sometime be overwhelming experience, so I would like to create a few posts that would make this job easier. This is just one of the templates that works for me, but with Spring you have infinite number of possibilities on how to integrate and develop you code.

More about Spring framework can be found at Spring documentation. Of course, Springsource (and community) have created many other additions and standalone products that fit well with Spring. Please feel free to explore this product.

File included in this post would be the first applicationContext.xml file that will be loaded from the web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:ctx="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/web-services 
    http://www.springframework.org/schema/web-services/web-services-2.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- enable configuration with annotations -->
    <context:annotation-config />

    <!-- we want to use AspectJ proxy (Spring proxy is limited) -->
    <aop:aspectj-autoproxy />

    <!-- scan for all annotations in this package -->
    <ctx:component-scan base-package="my.package.structure" />

    <!-- load log4j configuration -->
    <!-- custom properties to get Properties when crating them -->
    <!-- should have private Properties customProperties, and should be set when loading log4j --> 
    <bean id="log4jPropertyConfigurer"
        class="my.package.structure.properties.CustomPropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:log4j.properties</value>
            </list>
        </property>
        <property name="order" value="0"></property>
        <property name="ignoreUnresolvablePlaceholders" value="true" />
    </bean>

    <!--configure log4j -->
    <!-- call this in your constructor to init log4j: org.apache.log4j.PropertyConfigurator.configure(properties.getCustomPropeties()) -->
    <bean id="applicationLog4j" class="my.package.structure.properties.log4j.Log4jPropertyConfigurer">
        <constructor-arg index="0" ref="log4jPropertyConfigurer" />
    </bean>        

    <!-- load additional properties -->
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:application.properties</value>
                <value>classpath:jdbc.properties</value>
                <value>classpath:messages.properties</value>
                <!-- This should be loaded last -->
                <value>file:override.properties</value>
            </list>
        </property>
        <property name="ignoreUnresolvablePlaceholders" value="true" />
    </bean> 

    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>messages</value>
                <value>messages_en_US</value>
            </list>
        </property>
    </bean>

    <!-- import hibernate or any other Spring xml -->
    <import resource="applicationContext-hibernate.xml" />  

</beans>

Windows 7 Unidentified Network and VMWare

I have spent some time trying to figure out why if Windows 7 showing Unidentified Network constantly. The reason was that I have VMWare installed and it is causing Windows to assign it's adapters as unidentified.

One of the solutions you can find here: www.moore-logic.com.

Sunday, January 01, 2012

Merging Word documents with docx4j

Recently I needed to merge some Word .docx  documents and the tools that we chose for this was docx4j (www.docx4java.org). This is library for Java for working with Microsoft Open XML. There we two things that we needed to accomplish:

  1. Binding XML to various templates
  2. Merging documents as a result of the binding

I will write about merging documents as binding them is already explained well in docx4j site. Author of docx4j also offers commercial package for merging documents but if you want to try it for yourself, here are couple of things that I managed to do and got pretty decent results.

In version 2.7.1 docx4j you can work with java.util.File or java.io.InputStream. First one will do a god job if you have file present in your drive and second one if you keep content in the database (for example).  When merging Word documents you have to take care of relationships in the document itself. There are several elements that have relationships that can span through the document, but we were interested in just a few of them (images, footers and headers). It is worth to mention that if you miss one relationship, your document will be unreadable (in most cases). These are listed as resources and have references that you can use in your paragraphs.

So, to start we would:

  1. Load our initial file in WordprocessingMLPackage (this is the file where we want to attach the rest of the files, so in the end they look as one)
  2. Create unique section template
  3. Reset sections (this will serve the purpose of removing all references from the existing template, remember that section defines page layout)
  4. Remove body section (we can add this in the end)
  5. Loop through the attachment files (if you do not have sections separating pages, you might add page breaks)
  6. Copy relationships that you are interested in
  7. Copy elements
  8. If you do not want page breaks, then you can add empty section
  9. Add body section
  10. Reapply all headers and footers to empty sections

This all might sound complicated, but in the end, once you get to know the structure of the WordprocessingMLPackage, it becomes easier.

These are the code snippets that might be useful:

//...
public class MergeUtil implements IMergeUtil {

    //...

    private void mergeDocxFiles(WordprocessingMLPackage initialFile, List<WordprocessingMLPackage> attachementFiles,
            String outputFile) throws Exception {
        //...
        resetSections(wordMLPackageDest);
        //...
        addEmptySection(wordMLPackageDest, SectionType.PARAGRAPH);

        for (WordprocessingMLPackage wordprocessingMLPackage : attachementFiles) {
            //...
            traverseAndCopyRelationships(wordprocessingMLPackage.getPackage().getRelationshipsPart());
            traverseAndCopyElements(wordprocessingMLPackage.getPackage().getRelationshipsPart(),
                    wordprocessingMLPackage.getMainDocumentPart().getContent());
            //...

        }

        addEmptySection(wordMLPackageDest, SectionType.BODY);
        assignHeaderFooterData(wordMLPackageDest);
        //...                

    }

    //...

    private void addPageBreak() {
        logger.debug("Adding page break");
        org.docx4j.wml.P p = new org.docx4j.wml.P();
        org.docx4j.wml.R r = new org.docx4j.wml.R();
        org.docx4j.wml.Br br = new org.docx4j.wml.Br();
        br.setType(STBrType.PAGE);
        r.getContent().add(br);
        p.getContent().add(r);
        wordMLPackageDest.getMainDocumentPart().addObject(p);
    }

    @SuppressWarnings({ "restriction", "rawtypes" })
        private void traverseAndCopyElements(RelationshipsPart rp, List<Object> content) throws InvalidFormatException {
            for (Object o : content) {

                //...
                findResourceById(rp, ((org.docx4j.dml.picture.Pic) o6).getBlipFill().getBlip()
                        //...
                        .getEmbed());
                findResourceByName(wordMLPackageDest.getPackage().getRelationshipsPart(),
                        imageRelPartName);
                //...  
            }
        }

    //...        
    private void findResourceById(RelationshipsPart rp, String lastId) {
        for (Relationship r : rp.getRelationships().getRelationship()) {
            Part part = rp.getPart(r);
            //...
            if (part.getRelationshipsPart(false) != null) {
                findResourceById(part.getRelationshipsPart(false), lastId);
            }
        }
    }

    private void findResourceByName(RelationshipsPart rp, String imageName) {
        for (Relationship r : rp.getRelationships().getRelationship()) {
            Part part = rp.getPart(r);
            //...
            if (part.getRelationshipsPart(false) != null) {
                findResourceByName(part.getRelationshipsPart(false), imageName);
            }
        }
    }

    private void traverseAndCopyRelationships(RelationshipsPart rp) throws InvalidFormatException {
        for (Relationship r : rp.getRelationships().getRelationship()) {
            Part part = rp.getPart(r);
            if (part != null) {
                //...
                if (part instanceof org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage
                        || part instanceof org.docx4j.openpackaging.parts.WordprocessingML.FooterPart
                        || part instanceof org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart) {
                    //...
                        }

                if (part.getRelationshipsPart(false) != null) {
                    traverseAndCopyRelationships(part.getRelationshipsPart(false));
                }
            }
        }
    }
    //...
    private void resetSections(WordprocessingMLPackage wordMLPackage) throws InvalidFormatException {
        Document doc = (Document) wordMLPackage.getMainDocumentPart().getJaxbElement();

        for (Object o : doc.getBody().getContent()) {
            if (o instanceof org.docx4j.wml.P) {
                if (((org.docx4j.wml.P) o).getPPr() != null) {
                    org.docx4j.wml.PPr ppr = ((org.docx4j.wml.P) o).getPPr();
                    if (ppr.getSectPr() != null) {
                        //...
                    }
                }
            }
        }

        wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().setSectPr(null);
    }
    //...
    private void addEmptySection(WordprocessingMLPackage wordMLPackage, SectionType type) {
        if (type.equals(SectionType.BODY)) {
            org.docx4j.wml.SectPr sectPr = objectFactory.createSectPr();
            sectPr.setPgSz(this.defaultSectionpgSz);
            sectPr.setPgMar(this.defaultSectionpgMar);
            sectPr.setCols(this.defaultSectioncols);
            sectPr.setDocGrid(this.defaultSectiondocGrid);
            wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().setSectPr(sectPr);
        } else {
            //...
        }
    }
    //...
    private void assignHeaderFooterData(WordprocessingMLPackage wordMLPackage) throws InvalidFormatException {
        Document doc = (Document) wordMLPackage.getMainDocumentPart().getJaxbElement();
        int sectionCounter = 0;
        wordMLPackage.getMainDocumentPart().getContent();

        HeaderPart headerPart = new HeaderPart();
        headerPart.setPackage(wordMLPackage);
        headerPart.setJaxbElement(objectFactory.createHdr());
        Relationship rHdr = wordMLPackage.getMainDocumentPart().addTargetPart(headerPart);
        FooterPart footerPart = new FooterPart();
        footerPart.setPackage(wordMLPackage);
        footerPart.setJaxbElement(objectFactory.createFtr());
        Relationship rFtr = wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);

        for (Object o : doc.getBody().getContent()) {
            if (o instanceof org.docx4j.wml.P) {
                if (((org.docx4j.wml.P) o).getPPr() != null) {
                    org.docx4j.wml.PPr ppr = ((org.docx4j.wml.P) o).getPPr();
                    if (ppr.getSectPr() != null) {
                        //...
                        if(!StringUtils.isEmpty(hr.getId()))
                            ppr.getSectPr().getEGHdrFtrReferences().add(hr);
                        //...
                    }
                }
            }
        }

        HeaderReference hr = objectFactory.createHeaderReference();
        hr.setType(HdrFtrRef.DEFAULT);
        FooterReference fr = objectFactory.createFooterReference();
        fr.setType(HdrFtrRef.DEFAULT);
        hr.setId(findRelationshipByTarget(wordMLPackage.getRelationshipsPart(),  String.format("/word/header1_%d.xml", lastHeaderReference)));
        fr.setId(findRelationshipByTarget(wordMLPackage.getRelationshipsPart(),  String.format("/word/footer1_%d.xml", lastHeaderReference)));

        if(!StringUtils.isEmpty(hr.getId()))
            wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().getSectPr().getEGHdrFtrReferences().add(hr);
        if(!StringUtils.isEmpty(fr.getId()))
            wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().getSectPr().getEGHdrFtrReferences().add(fr);
    }

    private String findRelationshipByTarget(RelationshipsPart rp, String target) throws InvalidFormatException {
        //...            
    }
}
Note: All code displayed in upper window is property of Sapiens North America