Thursday, March 29, 2018

Making Http Post request with MultipartForm data

My requirement was to upload a file and pass some JSON in an API in Java. So I googled and found some code as below:

HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(UPLOAD_URI);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(Charset.defaultCharset());
String text = "{\"name\":\"manish\",\"occupation\":\"engineer\"}";
builder.addTextBody("info", text);
builder.addPart("doc", new FileBody(file));
httpPost.addHeader("content-type", MediaType.MULTIPART_FORM_DATA);
httpPost .setEntity(builder.build()); httpclient.execute(httpPost);

When I used this code then I got 400 error with reason as "Bad Request" and empty response body. The target server was also in my control and was running Jersey framework on Tomcat. I checked the logs and found just error as 400. Absolutely no message or other info on the reason of the error.
When I ran the API from CURL then it worked with no issues. Thats makes me believe that the issue is in code above.

After a lot of hit and trial I found that the missing piece is the "boundary". Boundary was not added into the "content-type" header. I changed the code above to:

HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(UPLOAD_URI);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(Charset.defaultCharset());
String text = "{\"name\":\"maniildersh\",\"occupation\":\"engineer\"}";
builder.setBoundary("------------------------f8ee5b62c27a1db3");
builder.addTextBody("info", text);
builder.addPart("doc", new FileBody(file));
httpPost.addHeader("content-type", MediaType.MULTIPART_FORM_DATA+ "; boundary=------------------------f8ee5b62c27a1db3");
httpPost .setEntity(builder.build()); httpclient.execute(httpPost);

Thats it !  Code started working. Later I changed the boundary to be generated dynamically instead of being static.

Sunday, March 25, 2018

Ansible: ERROR: tag(s) not found in playbook:

I was getting this error when I was running an ansible playbook from my java application. And surprisingly when I copy the exact same command on the command line then it ran just fine.  Every time I am getting an error as shown below:
ERROR: tag(s) not found in playbook:  foobar.  possible values: foobar
I spent many hours but could not understand why it is failing to run from java app but runs fine from command line. Then I started playing with the command in java and placed the "-t foobar" at different places, such as after the "ansible-playbook" command or at the end of the statement, or along with an extra variable. Every time it failed with same error.
Then I noticed that there are two spaces before the "foobar" in the error.  And that was like the eureka moment for me. I changed the java code to use "-tfoobar" (without space between "-t" and tagname). That's it, it worked.
It seems the library I was using (commons-exec from apache) passes the arguments in such a way that ansible does not like space after "-t" option. But space works just fine when running the command via command line.

Learnings in handling input and output streams in Java

I learned it hard way that working with streams in Java can be tricky, specially when you are writing to an output stream. It happened to me a couple of times that I found that although I am writing to stream but still I lost some data. That happened because I forgot to close the stream. And sometimes I closed a stream which I should not have. Below I am providing some points that I have learnt while working with streams:

Flush the streams frequently: Main benefit of working with streams is you process the data as you receive from input stream and push the processed data into the output stream. So, you must flush your data into the output stream as you process. This ensures that the listener on the other end of the output stream gets the data continuously. And also make sure you flush the data when the processing is done to ensure even the last bits are sent to the output stream.

Close the stream only if you open it: It is a rule of thumb that you close any stream (input or output) only if you have open it. Simple reason is, if the caller method has opened the stream and passed it to you( in your method) then it is possible that caller can use it after your method call. Example is, input and output stream in a servlet is opened by the application container and then passed to the servlet, you don't have to close these streams. Container closes these streams when the http request is completed.
This includes the stream that you have created as wrapper on another stream.




Tuesday, February 6, 2018

Execute different methods based on accept header and set priority when no accept header provided

I had a REST API which was accepting a file upload and in response generating another file. This was working fine without any issue with below code:
@POST 
@Path("/convert")
@Produces("application/octet-stream")
@Consumes(MediaType.MULTIPART_FORM_DATA)
 But, later I had to add a requirement where in certain cases the response in NOT stream but a JSON object. I wanted to keep the API same but behave differently only for limited number of cases. After looking into some Jersey docs and some testing below is what implemented
@POST
@Path("/convert")
@Produces("application/octet-stream;qs=1")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@POST
@Path("/convert")
@Produces("application/json;qs=.5")
@Consumes(MediaType.MULTIPART_FORM_DATA) 
Note that both of the APIs have signature, and only difference is in response type. With these signatures client when passes "accept" header as  "application/octet-stream" then first method is called. And when "accept" header is "application/json" then second method is called.
In addition to this, I also used the "quality" (note qs), this is set to 1 for first method and 0.5 for the second method. Because of this my code change was backward compatible. Which means, when no accept header is provided. In that case the first method is called because qs=1.

Wednesday, April 6, 2016

How to use environment variables in Elastic Beanstalk extensions

I had this task to inject graylog collector in the Amazon instance as soon as it is deployed by Elastic Beanstalk. After some googling I found "ebextensions" (Elastic Beanstalk extensions). These are configuration files to customize the web application environment.
Below are the challenges I faced while writing ebextensions:

When does the ebextensions run and how will I know that the ebextension has run or not?
Ebextension configuration files are executed when the web application is deployed into the aws instance. Which means neither you have to rebuild the environment nor restart it. All you need is just deploy the web application which has the .ebextensions directory and all configuration yaml files. And all the logs are available in directory "/var/log/eb-activity.log" on the VM . If there are any failures or errors it should be logged into this file.

How to include hidden files in the war file using maven?
When I added ebextensions in my code and built the war I noticed that none of my configuration file are in the war. The I realized that maven war plugin does not include the hidden directory ".ebextensions" into the war file. So, I changed the pom.xml to explicitly include the ".ebextensions" along with all other files. Example is shown below:


How to read ElasticBeanstalk environment variables in the ebextenions?

Once I got the ebextensions working I wanted to read some properties from the Elastic Beanstalk configuration. From the AWS documents and some StackOverflow links I found out that environment variable and properties can be used only in "container_commands". Here I faced another problem, all of my environment variables has "." (dot) in it, example "my.prop.key" and I could not read it using the usual way. So, I had to use "printenv" to read the envirnment variable having "." (dot). Example:
container_commands:
  10_update_env_name:
    command: 'sed -i.bak "s/\"env\" = \".*\"/\"env\" = \"`printenv my.server.name`\"/g" collector.conf

After making these changes, I was able to inject environment name into the Graylog collector conf using ebextensions.

How to access OS environment variable in the ebextenions?

After injecting server name in collector conf. I had another task on installing some python modules on the AMI. So, I simply add some container commands as show below:
container_commands:
  10_install_python_pip:
    command: 'yum -y install python-pip'
  20_install_python_devel:
    command: 'yum -y install python-devel'
  30_install_libffi:
    command: 'yum -y install libffi-devel'
  40_install_openssl:
    command: 'yum -y install openssl-devel'

But, when I deploy the application then ebextensions failed with below error:
 File "/usr/lib/python2.5/UserDict.py", line 23, in __getitem__
    raise KeyError(key)
KeyError: 'PATH'


Command "python setup.py egg_info" failed with error code 1 in ...

Surprisingly, when I ran the exact command manually then it ran successfully. After scratching my head for some time and looking into the source code of "UserDict.py" I came to conclusion that for some reason ebextensions are not able to read the OS environment variable "PATH" and hence causing the error. After googling I found that OS envirnment variable are not accessible in container commands. So, I created a script and called the script from the container command and VOILA, all python modules got installed successfully. Working ebextension is as below:

files:
  "/tmp/installPython.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/bash

      yum -y install python-pip
      yum -y install python-devel
      yum -y install libffi-devel
      yum -y install openssl-devel

container_commands:
  10_install_python_pip:
    command: '/bin/sh /tmp/installPython.sh'



AWS Document for using ".ebextensions": http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions.html

I hope this will be useful.

Saturday, August 22, 2015

How to recreate index for a bucket in Riak

As we all know that all of the data in the Riak is indexed using solr. And if we have to add another field into the index then we have to re-index the entire data again. I had to do this task last week so here I am sharing the steps that I followed.


1. Disassociate the index from the bucket. 

If you are using custom bucket type then use below:

Format:
curl -v -XPUT http://localhost:8098/types/acme/buckets/Users/props \
-H 'Content-Type: application/json' \
-d '{"props":{"search_index":"_dont_index_"}}'

Example:
curl -v -XPUT http://localhost:8098/types/acme/buckets/Users/props \
-H 'Content-Type: application/json' \
-d '{"props":{"search_index":"_dont_index_"}}'

If you are using default bucket type then use below:


Format:
curl -v -XPUT http://localhost:8098/buckets/Users/props \
-H 'Content-Type: application/json' \
-d '{"props":{"search_index":"_dont_index_"}}'

Example:
curl -v -XPUT http://localhost:8098/buckets/Users/props \
-H 'Content-Type: application/json' \
-d '{"props":{"search_index":"_dont_index_"}}'

2. Delete the index

Format:
curl -v -XDELETE http://localhost:8098/search/index/_index

Example:
curl -XDELETE http://localhost:8098/search/index/Users_index

3. Create the new schema which has the new field to be indexed.

Format:
curl -XPUT http://localhost:8098/search/schema/ \
     -H 'Content-Type:application/xml' \
     --data-binary @

Example:
curl -XPUT http://localhost:8098/search/schema/users-schema \
     -H 'Content-Type:application/xml' \
     --data-binary @solr-schema.xml

4. Re-create the index using new schema

Format:
curl -XPUT http://localhost:8098/search/index/_index \
     -H 'Content-Type: application/json' \
     -d '{"schema":""}'

Example:
curl -XPUT http://localhost:8098/search/index/Users_index \
     -H 'Content-Type: application/json' \
     -d '{"schema":"users-schema"}'

5. Associate the new index to the bucket.

If you are using custom bucket type then use below:

Format:
curl -v -XPUT http://localhost:8098/types/acme/buckets/Users/props \
-H 'Content-Type: application/json' \
-d '{"props":{"search_index":""}}'

Example:
curl -v -XPUT http://localhost:8098/buckets/Users/props \
-H 'Content-Type: application/json' \
-d '{"props":{"search_index":"Users_index"}}'

6. Re-write all of the data in the bucket

For this I wrote some python code using the Riak python module.
Sudo code is as follows:
Get all keys from the bucket
FOREACH key
  Get the document for the key
  Save it back

This will re-index the data using the new schema.

Thursday, February 26, 2015

No form parameters are passed when submitting a form

The problem once identified was so stupid and obvious that for while I felt bad. I should have found it very quickly. Anyway, so what was happening is, I created a login for my new web application and after integrating with Spring security I was not able to login. I changed the log level of Spring security package to TRACE and then found that username and password are passed as empty strings.
Then I checked the request sent from browser and noticed that browser is not sending the params with the request.
Below is my html code of the login form:



I spent some time to figure out what is happening but it was already past midnight so I left it like that and slept.
In morning I looked into the code again and very quickly noticed that the input elements in the html does not have "name" attribute. Ooops !!! Very stupid mistake done by someone who has created many web applications.

But it shows that sometimes simple mistakes like this are hard to find.

~Manish

Wednesday, November 26, 2014

Some Messages Stuck in the ActiveMQ Queue

In one of our product we are using Apache ActiveMQ 5.5.0 with Spring 3.0.7.  I have two publisher pushing message to a common queue and two consumers listening from the same common queue.
Last week I encountered this strange issue where some messages got stuck in the queue (I could see the message in the ACTIVEMQ_MSGS table since I am using persisted queue).
The strange part was only few messages were getting stuck while others were still getting processes just fine.

I looked into the logs and started thinking that I am hitting some ActiveMQ bug (possibly https://issues.apache.org/jira/browse/AMQ-3966). But I continued my diagnosis. Today after spending about two days on this issue I realized that it was NOT ActiveMQ bug but a bug in my code.

Here is what was happening, for some of the messages the code was making an HTTP call to a REST API. Those calls never got completed and just got blocked. Since the consumer had 10 threads to handle messages , so even when one thread got stuck others were still working fine. But slowly even these threads got stuck as they receive similar message and tried to make the HTTP call the same REST API. And finally the consumer stopped processing messages.
Interestingly, this behavior was happening only on one of the consumer and the other consumer was able to make HTTP calls the REST API successfully.

From ActiveMQ brokers point of view both of the consumers are up and running , so it keep on sending half of the message to the first consumer and hence all the those message get piled up in the ACTIVEMQ_MSGS table.

Restarting the consumer resolved the issue because then the threads were re-created. And by that time the issue with making API calls was also resolved.

So, the learning from this issue is, I should have added some READ_TIMEOUT to the HttpClient while making the REST API call. That way the thread would have thrown the "READ_TIMEOUT" error and got freed to process next message.

Hope this will help someone.

Friday, October 31, 2014

Enable Conversation using Session attributes in Spring

In a Spring framework project we use form objects saved as session attributes to achieve a conversational style of creating and editing business objects.
But recently I realized that this functionality does not work if you have more than one tab open in the same browser. Reason is simple, if you are editing an object in first tab and start editing another object in second tab then the session attributes gets replaced by the second object. And now if you save first object then actually the second object will be updated with the information of first object.

This happens because spring saves the objects into session with same attribute name, so the object saved later will replace any other object. And when POST request is made from already loaded UI then it will always update the object which was saved later.

There is a very easy solution to the above issue. We can extend the class "DefaultSessionAttributeStore" and override just one method,  which is "getAttributeNameInSession(WebRequest request, String attributeName)", as shown below:
  
@Override
  protected String getAttributeNameInSession(WebRequest request, String attributeName) {
    String cid = request.getParameter(attributeName + "_cid") == null ? ""
        + request.getAttribute(attributeName + "_cid", WebRequest.SCOPE_REQUEST) : request.getParameter(attributeName
        + "_cid");
    if (cid != null || !"".equals(cid)) {
      return super.getAttributeNameInSession(request, attributeName + "_" + cid);
    }
    return super.getAttributeNameInSession(request, attributeName);
  }


This class should also implement interface "InitializingBean" and override method "afterPropertiesSet()" as shown below:
  @Override
  public void afterPropertiesSet() throws Exception {
    annotationMethodHandlerAdapter.setSessionAttributeStore(this);
  }

This will make sure that this custom session attribute store is added to the annotation handler adaptor.

Now when ever you save the form into model map, make sure that you add another attribute with name "{your form name}_cid" and value as the unique id for the form.
And from the JSP add a hidden input which will be sent along with the POST request.
<input name="{your form name}_cid" type="hidden" value="<c:out value='${your form unique id}'/>" />

Thats it! You can now edit different entities in different tabs under same browser session.

Please add comment if you have any question.

Thanks,
Manish

Monday, October 20, 2014

Resolving "Could not resolve view with name ... in servlet with name ..."

I was creating a web application using Spring with Apache Tiles, and my application did not work after I enabled the TilesView and TilesConfigurer. My configuration was as shown below:



I was getting below error.
SEVERE: Servlet.service() for servlet [onecode] in context with path [/onecode] threw exception [Could not resolve view with name 'search' in servlet with name 'onecode'] with root cause
javax.servlet.ServletException: Could not resolve view with name 'search' in servlet with name 'onecode'
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1200)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1005)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:952)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)


From the error it seems the control is not even reached to Tiles because I did not see any Tiles class in the stack trace. I checked all the configuration files multiple times and everything looked just fine.
After spending some time struggling to find the issue, I noticed that I had a typo in the "tiles-def.xml" file. One of the definition was extending a base definition and there I typed the incorrect name of the extended definition. The issue got resolved as soon as I corrected the base definition.

~Manish

Friday, September 5, 2014

Jail breaking Cognos: Fix ClickJacking in Cognos

All of us who work with Cognos, know that sometimes how difficult it can be to fix or customize a simple request from the End user, for example modifying Prompt behaviors.
I’ve faced my share of Fancy requests like Hiding the Prompt name from the Drop-down <ToDo> or Reload a prompt without Refreshing the page.
Thanks to experts like “Cognos Paul” and groups like Ironside and Cognoise, we know how to get these done.

This time I got a little more complex request than UI customizations. In the security testing for our application, Testing team reported that Cognos is susceptible to  ClickJacking (or Frame busting).
IBM replied with “you can configure your Web-Server to set X-FRAME-OPTIONS that disables framing”.
But this works  only with latest browsers and if the victim is using a really old browser, like Mozilla 3.20 in our case, then it doesn’t.

There is a simple Solution to this, provided by OWASP, Link:
Add the following code to the landing page of your application.


Now comes the second part of the problem. In our application we are using Cognos LDAP authentication and hence don’t have a customized page for Login.
So the solution was to find Where to add this piece of code in Cognos so that the entire portal is secured from this issue.

With some effort I figured out that there are two pieces of the puzzle. First is that Cognos generates the HTML from its XSL files at runtime, so we can’t directly paste the code in the HTML.
Second was that there are some JS files, which are loaded for every page, as required by the portal.

So I found that for Login page, HTML is generated from this “render.xsl” file and for Landing/Portal pages, “framework.xsl”.
Then I added the above code to these two files like this:

For Login page:


For Portal pages:

You have to do it in two places as “framework.xsl” didn’t allow us to add HTML “Style” element, which then has to be added to  “presentation.xsl


For “framework.xsl



If done correctly, any webpage which tries to use your portal in an iframe, your portal will bust out of that frame and URL will be changed to actual portal URL.

Hope it helps.


Thursday, August 7, 2014

Incorrect position of form object in spring controller

Today I wasted some time to find out why my spring controller method errors out even before entering into the method.
My failing method is:


But I knew I am doing things right. I debugged  the application and then searched on net but could find why it is failing. Suddenly I thought is it the position of the "User" model attribute which is causing the issue. Then I changed the method as below:


Notice that I moved the method argument "@ModelAttribute @Valid User user" from first to second last. And Voila!! it worked.

FYI: I am using Spring 3.2.

Thursday, July 24, 2014

Learning MongoDB

I spent sometime to learn some basic stuff about MongoDB. I started searching internet and found some very helpful links.
Try the MongoDB online (interactive tutorial): http://try.mongodb.org/
Little MongoDB book (PDF) : http://openmymind.net/mongodb.pdf

After going through above links I got some confidence and I downloaded the MongoDB from http://www.mongodb.org/downloads .  Installing the db was very simple and within minutes I was having mongo up and running on my Macbook.

Then I thought of creating a sample spring application to learn spring integration with mongo. I followed the documentation at http://docs.spring.io/autorepo/docs/spring-data-mongodb/1.4.3.RELEASE/reference/html/index.html.

I created an application which fetches all users from the database. The code for the sample application is available at https://github.com/itsmanishagarwal/SpringMongoDb.

Anybody can download the code and use as per their need.

~Manish

Why should we allow only single session per user?

Some time back me , Mishra and Mittal were discussing some technical issues and then Mishra mentioned that he had a requirement to implement this feature to allow only one session per user. He implemented this without any problem but then we started thinking what are the possible reasons for which people want to implement this requirement. After some thinking, discussing and googling we could come with below reasons:
  1. Security. If we allow only one session per user then on creation of second session we can alert the user that there is already a active session and allow a way to kill the previous session. This way if the user is unaware of previous session then this will serve as a warning that his/her credentials may have been compromised. And user can change his credentials.
  2. Licensing. Some products are priced as per number of users using the product. So, avoiding the multiple session per user will prevent the misuse of the license.
  3. Product Implementation. This is very specific to the product requirement. If the application maintains some kind of user's working state then multiple sessions can mess it up.
 I will update this post if I could find some more reasons.

~Manish

Wednesday, June 11, 2014

Unable to add any directory into the watched directory chain in virgo tomcat server 3.0.3

Recently I had a requirement where I had to add a directory into the watched directory chain in virgo tomcat server (VTS 3.0.3). Requirement was to check if some jars are present in a particular directory, and if jars are present then add the directory programmatically (using shell script) in the watched directory chain.
So, I created a shell script which looks like below:

if [ -f /usr/local/vts/external_jars/util* ] ; then
echo "Found the util jar under /usr/local/vts/external_jars"
sed -i '
/usr.watchDirectory=repository\/usr/ a\
external_jars.type=watched \
external_jars.watchDirectory=external_jars
' $VTS_HOME/config/org.eclipse.virgo.repository.properties
sed -i "s/usr,/usr,external_jars,/" $VTS_HOME/config/org.eclipse.virgo.repository.properties
fi
When I ran the script it worked all fine, and the configuration file also got updated, but when I restarted my virgo server it did not pick my "external_jars" directory into the chain of watched directory.
I compared the changes with other watched directories but could not find any clue.

Then after struggling a lot I realized that there is a extra space after the value of property "external_jars.type". And as soon as I removed the extra space, it worked.

Corrected script is:

if [ -f /usr/local/vts/external_jars/util* ] ; then
echo "Found the util jar under /usr/local/vts/external_jars"
sed -i '
/usr.watchDirectory=repository\/usr/ a\
external_jars.type=watched\
external_jars.watchDirectory=external_jars
' $VTS_HOME/config/org.eclipse.virgo.repository.properties
sed -i "s/usr,/usr,external_jars,/" $VTS_HOME/config/org.eclipse.virgo.repository.properties
fi

Just thought of sharing this info, so I blogged it.

Saturday, March 29, 2014

How to quickly setup ActiveMQ broker, publisher and subscriber

Last week I was working on an issue related to ActiveMQ messaging. During my debugging the most painful part was to start the entire application and then execute the test scenario just to test some functionality or feature in ActiveMQ. After spending some time I realized that to speed up my debugging and anaysis I have to create a separate application/program which I can start and stop quickly after making changes. So, I created two programs:
ActiveMQPublisherTest: This program start the ActiveMQ broker and then push messages into a queue.
ActiveMQSubscriberTest: This program listens to the ActiveMQ broker started by "ActiveMQPublisherTest" and receives the event published by it.

I have published the entire source code on GitHub at: https://github.com/itsmanishagarwal/ActiveMQTest

To use these programs you just need to change the XML files to point to your database.

Any suggestions or feedback is welcome.

~Manish

Thursday, March 20, 2014

How to verify if a file which belongs to an RPM is modified?

Recently I had to struggle to find out a way to verify if a file which belongs to an RPM is modified or not. After searching a bit on google I found that there is a option in "rpm" tool to verify all the files but there is no direct way to find if a particular file is modified. So, I decided to create a function which can help me doing that.


function
 isFileModified {
  FILE=$1
  if rpm -Vf $FILE | grep $FILE >/dev/null 2>&1 && rpm -Vf $FILE | grep $FILE \
| awk -F" " '{print $1}' | grep -e ".*5.*" >/dev/null 2>&1; then
    return 0
  else
    return 1
  fi
}

Explanation:
rpm -Vf $FILE : Returns list of all the files which got modified in the RPM package.
grep $FILE : Check if the file to be checked is in the list of modified file.
awk -F" " '{print $1}' : Truncates the attributes of the provided file
 grep -e ".*5.*"  : Check if the md5 digest of the file is changed.

So, the functions returns 0 if the file's md5 digect is changed after it is installed by the RPM. Else it will return 0.

Thanks,
Agry

Tuesday, October 2, 2012

Override the properties in wro.properties

Sometimes there is a need to override some properties on local environment to facilitate faster development. If you have implemented the wro4j using spring then it is very easy to override any property in wro.properties file.

Just replace the  wroProperties bean in the applicationContext.xml with below code and spring will look for the property file from other locations.
    <bean id="wroProperties"
       
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
       
<property name="ignoreResourceNotFound" value="true"></property>
       
<property name="locations">
           
<list>
               
<value>file:${catalina.home}/conf/wro.properties</value>
               
<value>file:${catalina.home}/wro.properties</value>
               
<value>file:${user.home}/wro.properties</value>
           
</list>
       
</property>
   
</bean>

Property "<property name="ignoreResourceNotFound" value="true"></property>" ensures that bean creation will not fail even if the wro.properties file is missing. And property file at location mentioned latter overrides the property file before it. Means, if there is a property file at user home then it will override all the property file at other locations.

In my development setup I have placed a wro.properties i user home and set the managerfactoryclassname property to my custom class which disables the minimization. 
(To disable minimization check my blog at: http://msquare-tech.blogspot.in/2012/10/disable-minimizing-resources-when-using.html)

~Manish


Disable minimizing the resources when using wro4j


After implementing the wro4j in my application, the performance of the pages improved but there was one problem. Now because all the resources are minified, it become difficult to debug the Javascript issues from Firebug. 
I resolved the above issue by following below steps:

1. Extending the "DefaultGroupExtractor" class and overriding only one method :
/*
* Never minimize the resources
*/ 

@Override
  public boolean isMinimized(HttpServletRequest request) {
    return false;
  }
2.  Extending the "BaseWroManagerFactory" class and setting new group extractor as created in step 1:
/*
* Return the custom extractor as created above.
*/
  @Override
  protected GroupExtractor newGroupExtractor() {
    return new CustomDefaultGroupExtractor(); // extractor created in step 1
  }
3. In the wro.properties file add the manager factory class as below:
managerFactoryClassName=com.vmops.web.optimizer.CustomWroManagerFactory

4. Restart the server. Now you will see no resources are minimized.

~Manish

Wednesday, June 27, 2012

Implement wro4j in five steps.


WRO4J (Web Resource Optimizer For Java) is an awesome open source resource optimizer. I recently implemented it in my application. So here I am providing steps I followed to implement it and issues I faced.

Tools used: Maven

1. Add maven dependency for the WRO4J in you pom.xml as follows:

2. Add a filter in web.xml as follows:

3. Under WEB-INF create a folder wro.xml with content as follows:

This will create a js and css at runtime by combining all js and css under the group all and return and all.js and all.css respectively.

4. Under same folder WEB-INF create another file wro.properties with content as follows:
debug=true
disableCache=true
gzipResources=true
jmxEnabled=false
preProcessors=semicolonAppender
postProcessors=jsMin,cssCompressor,cssMin

5. Open any new existing JSP page and add a js call as follows:
and for css add the call as:
Thats it!! Now start your server and open your page.

I faced one issue while implementing wro4j and that is due to a dependency of wro4j jars. This version of wro4j requires commons-io.2.1 but because some other dependency older version of common-io got loaded. I did not get any error but was getting empty results when calling /wro/all.js and /wro/all.css. So, be careful.

~Manish