I had a small panel full of inputText boxes which were not updating on PPR. I was trying to partially refresh the container panel of all the input boxes. I even tried refreshing the input boxes individually. But to no avail.
I got a hint from somewhere that in addition to setting the partialTarget of the container panel you must programmatically call the following for each and every input box for PPR to be effective, in this case(assuming that ibField is the RichInputText component in your managed bean):
ibField.resetValue();
This did it for me!
Tuesday, October 11, 2011
Wednesday, September 28, 2011
Some JSF 2 rules to remember
- Request Bean can hold a reference to a Session Bean. Not vice versa, because a session bean has a longer life than a request bean and the rule is that longer scoped beans can't hold shorter scoped beans but vice versa is allowed.
- Cyclic references are a strict no. That is a request bean refers to a session bean which in turn holds a reference to the previous request bean is not allowed.
Thursday, June 3, 2010
Cascading dropdowns with AJAX on Java using DWR
This is one subject in Java that took me a helluva lot to crack. This subject literally took me through a journey to learn javascripting/ajax frameworks like Dojo, JQuery, etc. But finally landed on DWR to solve my problem.
I have usually been a loyal JSF person. And in JSF, ajax is almost always handled by the rich set of components provided by the various frameworks like Trinidad, Tomahawk, RichFaces, etc. But I had taken to develop my web application using Struts 2, which I heard is the raging in thing these days. I had worked with Struts 1 longtime ago and when I looked at picking up Struts 2 I was expecting the usual ActionForms, etc. But was surprised to find it a bit closer to JSF than I expected. I liked everything in struts 2 expect that Ajax had to be done manually. That is still okay if you have some good resources to learn how to do it with struts.
The first ajax problem I had hit upon was to get cascading dropdowns working. Everytime I searched for ajax and struts I would get patchy references of dojo-struts combination or struts' sx tag library. I had seen the publish-subscribe mechanism being used in a few examples but no resource explaining what is happening and how. Then by accident I stumbled upon DWR and god bless them for a more extensive documentation than others.
What I liked about DWR is
1. Project setup: Assuming that you are not new to maven 2, this is the best option to get started with this project. If you are new then please take the time out to get started with it. It is well worth the time. Go to your projects folder where you start new projects and issue the following command:
mvn archetype:create -DgroupId=com.struts.dwr.tutorial -DartifactId=tutorial -DarchetypeGroupId=org.apache.struts -DarchetypeArtifactId=struts2-archetype-starter -DarchetypeVersion=2.0.11.2-SNAPSHOT -DremoteRepositories=http://people.apache.org/repo/m2-snapshot-repository
Note at the time of writing the only version of struts2-archetype-starter available was 2.0.11.2-SNAPSHOT. This command will create a ready made struts-dwr project folder which you can immediately take for a ride.
Also note that this command creates a pom.xml file which loads dwr of version 1.1-beta-3. But we want to work with version 2.0.3. So, open the tutorial/pom.xml file and change the dwr dependency to look like this:
<dependency>
<groupId>org.directwebremoting</groupId>
<artifactId>dwr</artifactId>
<version>2.0.3</version>
</dependency>
2. Test the new Project: Test the newly created project by issuing the following command in the project folder i.e. the tutorial folder:
mvn jetty:run
This will start a jetty server. And you should be able to see the running web application at the following url: http://localhost:8080/tutorial
Tip: Even if you are struggling to find out the exact url at which you might find your web application, with jetty you can just go to the url http://localhost:8080 and it will tell you the url for all the web applications that are deployed in it. Sweet, right?
3. Load the project into eclipse: Now is the time to do some development. And without eclipse (assuming that that is the ide you are using, of course :) ). But you will need to prepare the project to be accepted by eclipse. To do that you will need to issue the command in the project folder:
mvn eclipse:eclipse
This will create the .classpath and .project files in the project root folder. With that in place you can import this project into eclipse. I won't go into how to do that.
Tip: Usually, this command is unable, for now, to make the eclipse project file so that it is recognized as a web project by eclipse. But that can be done using tips from this url: http://greatwebguy.com/programming/eclipse/converting-a-java-project-to-a-dynamic-web-project-in-eclipse/ or you can open the .project file of a dynamic web project you might've previously created and make the current .project file to look like it. Why is it important for the project to be recognized as a web project by eclipse? Because, otherwise, you will not get the design screen goodies for jsp files or faces-config.xml files, for instance.
4. Coding: Now let us dive right into the task of creating the Action and Model classes and the jsp file. We will have only three dropdowns on our page: country, state and city. Therefore we need three model classes Country, State and City. We will need one action class and one jsp page. Following is the code for them:
Country.java:
package com.struts.dwr.tutorial.model;
public class Country {
private int id;
private String name;
public Country(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
State.java:
package com.struts.dwr.tutorial.model;
public class State {
private int id;
private String name;
private int countryId;
public State(int id, String name, int countryId) {
this.id = id;
this.name = name;
this.countryId = countryId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCountryId() {
return countryId;
}
public void setCountryId(int countryId) {
this.countryId = countryId;
}
}
City.java:
package com.struts.dwr.tutorial.model;
public class City {
private int id;
private String name;
private int stateId;
public City(int id, String name, int stateId) {
this.id = id;
this.name = name;
this.stateId = stateId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStateId() {
return stateId;
}
public void setStateId(int stateId) {
this.stateId = stateId;
}
}
CascadingDDAction.java:
package com.struts.dwr.tutorial.action;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.struts.dwr.tutorial.model.City;
import com.struts.dwr.tutorial.model.Country;
import com.struts.dwr.tutorial.model.State;
public class CascadingDDAction extends ActionSupport {
List countries;
List cities;
List states;
public String input() {
countries = new ArrayList();
countries.add(new Country(1, "INDIA"));
countries.add(new Country(2, "US"));
states = new ArrayList();
states.add(new State(-1, "Other", -1));
cities = new ArrayList();
cities.add(new City(-1, "Other", -1));
return INPUT;
}
public List getStatesByCountryId(int countryId) {
List result = new ArrayList();
if (countryId == 1) {
result.add(new State(1, "Andhra Pradesh", 1));
result.add(new State(2, "Gujarat", 1));
} else if (countryId == 2) {
result.add(new State(3, "California", 2));
result.add(new State(4, "New Jersey", 2));
}
return result;
}
public List getCitiesByStateId(int stateId) {
List result = new ArrayList();
if (stateId == 1) {
result.add(new City(1, "Hyderabad", 1));
result.add(new City(2, "Secunderabad", 1));
} else if (stateId == 2) {
result.add(new City(3, "Ahmedabad", 2));
result.add(new City(4, "Vadodara", 2));
} else if (stateId == 3) {
result.add(new City(5, "Fremont", 3));
result.add(new City(6, "San Francisco", 3));
} else if (stateId == 4) {
result.add(new City(7, "Edison", 4));
result.add(new City(8, "Plainsboro", 4));
}
return result;
}
public List getCountries() {
return countries;
}
public List getStates() {
return states;
}
public List getCities() {
return cities;
}
}
Tip:Note that I have used the action class to write the methods that give out the states and cities based on countryId and stateId, respectively, to save space.
cascadingDD.jsp:
<%@taglib prefix="s" uri="/struts-tags"%>
<s:select list="countries" label="Country" listKey="id" listValue="name" />
<s:select id="stateId" list="states" label="State" listKey="id" listValue="name" />
<s:select id="cityId" list="cities" label="City" listKey="id" listValue="name" />
Add the following lines in the struts.xml:
<action name="cascadingdd" class="com.struts.dwr.tutorial.action.CascadingDDAction" method="input">
<result name="input">/jsp/cascadingDD.jsp</result>
</action>
With the above code in place you will be able to run the web-app and see the non-cascading dropdowns working in the following url: http://localhost:8080/tutorial/cascadingdd.action
5. Adding the magical DWR ajax stuff: Now is the time to add the magical ajax stuff. Do the following steps:
1. Modify the cascadingDD.jsp to look like this:
<%@taglib prefix="s" uri="/struts-tags"%>
<script type="text/javascript" src="<s:url value='/dwr/interface/CascadingDDAction.js'/>"></script>
<script type="text/javascript" src="<s:url value='/dwr/engine.js'/>"></script>
<script type="text/javascript" src="<s:url value='/dwr/util.js'/>"></script>
<script type="text/javascript">
function updateState(countryId){
CascadingDDAction.getStatesByCountryId(countryId, function(states){
dwr.util.removeAllOptions("stateId");
dwr.util.addOptions("stateId", [{"id":-1,"name":"Select one.."}], "id", "name");
dwr.util.addOptions("stateId", states, "id", "name");
});
}
function updateCity(stateId){
CascadingDDAction.getCitiesByStateId(stateId, function(cities){
dwr.util.removeAllOptions("cityId");
dwr.util.addOptions("cityId", [{"id":-1,"name":"Select one.."}], "id", "name");
dwr.util.addOptions("cityId", cities, "id", "name");
});
}
</script>
<s:select list="countries" label="Country" listKey="id" listValue="name" onchange="updateState(this.value)"/>
<s:select id="stateId" list="states" label="State" listKey="id" listValue="name" onchange="updateCity(this.value)"/>
<s:select id="cityId" list="cities" label="City" listKey="id" listValue="name" />
2. Add the following lines in the element in the dwr.xml file:
<create creator="new" javascript="CascadingDDAction">
<param name="class" value="com.struts.dwr.tutorial.action.CascadingDDAction"/>
<include method="getStatesByCountryId"/>
<include method="getCitiesByStateId"/>
</create>
<convert match="java.util.List" converter="collection"></convert>
<convert match="com.struts.dwr.tutorial.model.City" converter="bean"></convert>
<convert match="com.struts.dwr.tutorial.model.State" converter="bean"></convert>
That's it! Crank up the jetty server and check this url to see if your methods are performing as designed: http://localhost:8080/tutorial/dwr
Then go to the url http://localhost:8080/tutorial/cascadingdd.action and see if your test case is performing as we want it to.
I think that the javascript code is pretty self-explanatory even for beginners. That's all it takes to get ajax working on struts! For more details on the dwr functions please read the documentation on their site. It is good!
I have usually been a loyal JSF person. And in JSF, ajax is almost always handled by the rich set of components provided by the various frameworks like Trinidad, Tomahawk, RichFaces, etc. But I had taken to develop my web application using Struts 2, which I heard is the raging in thing these days. I had worked with Struts 1 longtime ago and when I looked at picking up Struts 2 I was expecting the usual ActionForms, etc. But was surprised to find it a bit closer to JSF than I expected. I liked everything in struts 2 expect that Ajax had to be done manually. That is still okay if you have some good resources to learn how to do it with struts.
The first ajax problem I had hit upon was to get cascading dropdowns working. Everytime I searched for ajax and struts I would get patchy references of dojo-struts combination or struts' sx tag library. I had seen the publish-subscribe mechanism being used in a few examples but no resource explaining what is happening and how. Then by accident I stumbled upon DWR and god bless them for a more extensive documentation than others.
What I liked about DWR is
- That it makes me to avoid the url definitions in the client side.
- The call to server code is almost the same as calling the actual java code on the server.
- You can test that the ajax call is succeeding through a url that dwr exposes on your web application. It is usually in the form: http://localhost:8080/
/dwr/ - It has some very good built-in functions to populate textfields, textareas, select components, etc. apart from nifty functions to get their values too.
1. Project setup: Assuming that you are not new to maven 2, this is the best option to get started with this project. If you are new then please take the time out to get started with it. It is well worth the time. Go to your projects folder where you start new projects and issue the following command:
mvn archetype:create -DgroupId=com.struts.dwr.tutorial -DartifactId=tutorial -DarchetypeGroupId=org.apache.struts -DarchetypeArtifactId=struts2-archetype-starter -DarchetypeVersion=2.0.11.2-SNAPSHOT -DremoteRepositories=http://people.apache.org/repo/m2-snapshot-repository
Note at the time of writing the only version of struts2-archetype-starter available was 2.0.11.2-SNAPSHOT. This command will create a ready made struts-dwr project folder which you can immediately take for a ride.
Also note that this command creates a pom.xml file which loads dwr of version 1.1-beta-3. But we want to work with version 2.0.3. So, open the tutorial/pom.xml file and change the dwr dependency to look like this:
<dependency>
<groupId>org.directwebremoting</groupId>
<artifactId>dwr</artifactId>
<version>2.0.3</version>
</dependency>
2. Test the new Project: Test the newly created project by issuing the following command in the project folder i.e. the tutorial folder:
mvn jetty:run
This will start a jetty server. And you should be able to see the running web application at the following url: http://localhost:8080/tutorial
Tip: Even if you are struggling to find out the exact url at which you might find your web application, with jetty you can just go to the url http://localhost:8080 and it will tell you the url for all the web applications that are deployed in it. Sweet, right?
3. Load the project into eclipse: Now is the time to do some development. And without eclipse (assuming that that is the ide you are using, of course :) ). But you will need to prepare the project to be accepted by eclipse. To do that you will need to issue the command in the project folder:
mvn eclipse:eclipse
This will create the .classpath and .project files in the project root folder. With that in place you can import this project into eclipse. I won't go into how to do that.
Tip: Usually, this command is unable, for now, to make the eclipse project file so that it is recognized as a web project by eclipse. But that can be done using tips from this url: http://greatwebguy.com/programming/eclipse/converting-a-java-project-to-a-dynamic-web-project-in-eclipse/ or you can open the .project file of a dynamic web project you might've previously created and make the current .project file to look like it. Why is it important for the project to be recognized as a web project by eclipse? Because, otherwise, you will not get the design screen goodies for jsp files or faces-config.xml files, for instance.
4. Coding: Now let us dive right into the task of creating the Action and Model classes and the jsp file. We will have only three dropdowns on our page: country, state and city. Therefore we need three model classes Country, State and City. We will need one action class and one jsp page. Following is the code for them:
Country.java:
package com.struts.dwr.tutorial.model;
public class Country {
private int id;
private String name;
public Country(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
State.java:
package com.struts.dwr.tutorial.model;
public class State {
private int id;
private String name;
private int countryId;
public State(int id, String name, int countryId) {
this.id = id;
this.name = name;
this.countryId = countryId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCountryId() {
return countryId;
}
public void setCountryId(int countryId) {
this.countryId = countryId;
}
}
City.java:
package com.struts.dwr.tutorial.model;
public class City {
private int id;
private String name;
private int stateId;
public City(int id, String name, int stateId) {
this.id = id;
this.name = name;
this.stateId = stateId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStateId() {
return stateId;
}
public void setStateId(int stateId) {
this.stateId = stateId;
}
}
CascadingDDAction.java:
package com.struts.dwr.tutorial.action;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.struts.dwr.tutorial.model.City;
import com.struts.dwr.tutorial.model.Country;
import com.struts.dwr.tutorial.model.State;
public class CascadingDDAction extends ActionSupport {
List
List
List
public String input() {
countries = new ArrayList
countries.add(new Country(1, "INDIA"));
countries.add(new Country(2, "US"));
states = new ArrayList
states.add(new State(-1, "Other", -1));
cities = new ArrayList
cities.add(new City(-1, "Other", -1));
return INPUT;
}
public List
List
if (countryId == 1) {
result.add(new State(1, "Andhra Pradesh", 1));
result.add(new State(2, "Gujarat", 1));
} else if (countryId == 2) {
result.add(new State(3, "California", 2));
result.add(new State(4, "New Jersey", 2));
}
return result;
}
public List
List
if (stateId == 1) {
result.add(new City(1, "Hyderabad", 1));
result.add(new City(2, "Secunderabad", 1));
} else if (stateId == 2) {
result.add(new City(3, "Ahmedabad", 2));
result.add(new City(4, "Vadodara", 2));
} else if (stateId == 3) {
result.add(new City(5, "Fremont", 3));
result.add(new City(6, "San Francisco", 3));
} else if (stateId == 4) {
result.add(new City(7, "Edison", 4));
result.add(new City(8, "Plainsboro", 4));
}
return result;
}
public List
return countries;
}
public List
return states;
}
public List
return cities;
}
}
Tip:Note that I have used the action class to write the methods that give out the states and cities based on countryId and stateId, respectively, to save space.
cascadingDD.jsp:
<%@taglib prefix="s" uri="/struts-tags"%>
<s:select list="countries" label="Country" listKey="id" listValue="name" />
<s:select id="stateId" list="states" label="State" listKey="id" listValue="name" />
<s:select id="cityId" list="cities" label="City" listKey="id" listValue="name" />
<result name="input">/jsp/cascadingDD.jsp</result>
</action>
With the above code in place you will be able to run the web-app and see the non-cascading dropdowns working in the following url: http://localhost:8080/tutorial/cascadingdd.action
5. Adding the magical DWR ajax stuff: Now is the time to add the magical ajax stuff. Do the following steps:
1. Modify the cascadingDD.jsp to look like this:
<%@taglib prefix="s" uri="/struts-tags"%>
<script type="text/javascript" src="<s:url value='/dwr/interface/CascadingDDAction.js'/>"></script>
<script type="text/javascript" src="<s:url value='/dwr/engine.js'/>"></script>
<script type="text/javascript" src="<s:url value='/dwr/util.js'/>"></script>
<script type="text/javascript">
function updateState(countryId){
CascadingDDAction.getStatesByCountryId(countryId, function(states){
dwr.util.removeAllOptions("stateId");
dwr.util.addOptions("stateId", [{"id":-1,"name":"Select one.."}], "id", "name");
dwr.util.addOptions("stateId", states, "id", "name");
});
}
function updateCity(stateId){
CascadingDDAction.getCitiesByStateId(stateId, function(cities){
dwr.util.removeAllOptions("cityId");
dwr.util.addOptions("cityId", [{"id":-1,"name":"Select one.."}], "id", "name");
dwr.util.addOptions("cityId", cities, "id", "name");
});
}
</script>
<s:select list="countries" label="Country" listKey="id" listValue="name" onchange="updateState(this.value)"/>
<s:select id="stateId" list="states" label="State" listKey="id" listValue="name" onchange="updateCity(this.value)"/>
<s:select id="cityId" list="cities" label="City" listKey="id" listValue="name" />
2. Add the following lines in the
<create creator="new" javascript="CascadingDDAction">
<param name="class" value="com.struts.dwr.tutorial.action.CascadingDDAction"/>
<include method="getStatesByCountryId"/>
<include method="getCitiesByStateId"/>
</create>
<convert match="java.util.List" converter="collection"></convert>
<convert match="com.struts.dwr.tutorial.model.City" converter="bean"></convert>
<convert match="com.struts.dwr.tutorial.model.State" converter="bean"></convert>
That's it! Crank up the jetty server and check this url to see if your methods are performing as designed: http://localhost:8080/tutorial/dwr
Then go to the url http://localhost:8080/tutorial/cascadingdd.action and see if your test case is performing as we want it to.
I think that the javascript code is pretty self-explanatory even for beginners. That's all it takes to get ajax working on struts! For more details on the dwr functions please read the documentation on their site. It is good!
Thursday, December 17, 2009
EJB 3 SessionBeans vs. Spring
SessionBeans provide the following:
- Instance pooling
- Automated session state maintenance
- Passivation/activation
- Annotations to escape the XML hell(that was EJB2.x).
- Native integration with JSPs, Servlets, JTA transactions, JMS providers, JAAS, etc being part of J2EE stack.
Monday, September 7, 2009
Java logging "message duplication" phenomenon
I've been stuck with this problem for a while and googled a lot on it before the resolution struck me. But firstly, what was the problem?
Problem Definition: There are times in multi-threaded java programming when you see duplicate messages in your log files.
What was I doing wrong: I had setup the logging infrastructure in a super class that all logging classes would extend. Everytime I was instantiating a class that would log it would initialize the logging infrastructure for itself. And everytime it did so it would add another Appender(log4j)/Handler(JUL) to the Logger class.
What is the resolution: Do this in the log setup code(specific for log4j):
Logger logger = Logger.getLogger("");
Enumeration appenders = logger.getAllAppenders();
if (!appenders.hasMoreElements()) {
//initialize an Appender class
//Add the appender object to the logger
}
When I was doing it wrongly, I did not have the if block. Hope you would benefit from this piece of wisdom :)
Problem Definition: There are times in multi-threaded java programming when you see duplicate messages in your log files.
What was I doing wrong: I had setup the logging infrastructure in a super class that all logging classes would extend. Everytime I was instantiating a class that would log it would initialize the logging infrastructure for itself. And everytime it did so it would add another Appender(log4j)/Handler(JUL) to the Logger class.
What is the resolution: Do this in the log setup code(specific for log4j):
Logger logger = Logger.getLogger("
Enumeration appenders = logger.getAllAppenders();
if (!appenders.hasMoreElements()) {
//initialize an Appender class
//Add the appender object to the logger
}
When I was doing it wrongly, I did not have the if block. Hope you would benefit from this piece of wisdom :)
Monday, August 17, 2009
Spring-2 some learnings while reading a book
I was reading this book "Sping in Action" by Manning publishers to catch up with what's new in Spring 2.0. Agree, kinda late in doing it when Spring 3 is already in the market. But it's better to be late than never.
Personally, I feel that they've overloaded the bean creation with lots of convenience methods like these:
class="com.springinaction.springidol.Instrumentalist"
autowire="byName">
<property name="song" value="Jingle Bells" />
</bean>
class="com.springinaction.springidol.Instrumentalist"
abstract="true">
<property name="instrument" ref="saxophone" />
<property name="song" value="Jingle Bells" />
</bean>
<bean id="kenny" parent="baseSaxophonist" />
<bean id="david" parent="baseSaxophonist" />
<property name="song" value="Somewhere Over the Rainbow" />
</bean>
<bean id="taylor" class="com.springinaction.springidol.Vocalist" parent="basePerformer" />
<bean id="stevie" class="com.springinaction.springidol.Instrumentalist" parent="basePerformer">
<property name="instrument" ref="guitar" />
</bean>
Personally, I feel that they've overloaded the bean creation with lots of convenience methods like these:
- Autowiring: Use "byName", "byType", "constructor" and "autodetect" to autowire a bean by name and type, resp. Something like this:
class="com.springinaction.springidol.Instrumentalist"
autowire="byName">
<property name="song" value="Jingle Bells" />
</bean>
- You can tell all the beans defined in the context file to autowire themselves with something like this:
- Autowiring, though it is convenient to use might lead to wrong wirings. So, I personally might resort to the old-fashioned manual wiring.
- Bean Scoping: By default beans are created as Singletons. Therefore, spring provides the following "scope" options when you create a bean: singleton, prototype, request & session(only valid in spring mvc) and global-session(only valid if used in portlet context).
- factory-method: If you already have a java implementation that returns a singleton, anyways. Then you can use it as a bean by declaring the method that spring might use to create an instance of the bean by using the factory-bean bean attribute.
- Now, this is pampering the users to no end. You, now have a way to tell spring to run init and cleanup methods when a bean is created. This, you can do using the init-method and destroy-method bean attributes where you can mention the methods of that bean that must be executed for the resp. stages. If you have used the same named init and cleanup methods across all your beans then you can specify it by using the default-init-method and default-destroy-method beans attributes.
- Parent-child concept in Spring! This is inheritance, the spring way. If you have a bean that will be created a multiple times in the context file, then you can use it like this:
class="com.springinaction.springidol.Instrumentalist"
abstract="true">
<property name="instrument" ref="saxophone" />
<property name="song" value="Jingle Bells" />
</bean>
<bean id="kenny" parent="baseSaxophonist" />
<bean id="david" parent="baseSaxophonist" />
- You can also abstract out the common properties into a parent-child relationship like this:
<property name="song" value="Somewhere Over the Rainbow" />
</bean>
<bean id="taylor" class="com.springinaction.springidol.Vocalist" parent="basePerformer" />
<bean id="stevie" class="com.springinaction.springidol.Instrumentalist" parent="basePerformer">
<property name="instrument" ref="guitar" />
</bean>
- Method injection: Another sorcery provided by Spring-2 is method injection whereby, you can, during runtime, replace one method with another method. This is done in two ways:
- Method replacement: Here you would implement an interface called org.springframework.beans.factory.support.MethodReplacer and implement the method public class TigerReplacer implements MethodReplacer {
public Object reimplement(Object target, Method method,
Object[] args) throws Throwable;} Then you do something like this in the context file: <bean id="magicBox" class="com.springinaction.springidol.MagicBoxImpl">
<replaced-method name="getContents" replacer="tigerReplacer" />
</bean>
<bean id="tigerReplacer" class="com.springinaction.springidol.TigerReplacer" /> - Getter injection: In this method you leave the getter method to be injected as abstract and then the lookup-method in the context file in the following way: <bean id="stevie"
class="com.springinaction.springidol.Instrumentalist">
<lookup-method name="getInstrument" bean="guitar" />
<property name="song" value="Greensleeves" />
</bean>
Wednesday, July 15, 2009
Java dependency walker tool
How many times you been stuck with a problem like this: You have numerous jars that cames with the external APIs you are using in your java project. When it is time to bundle the java application you do not know which jar files are the only ones that are absolutely needed for the application to run successfully. Most of the times to save time you bundle all the jars with the application with this unsettling feeling at the back of your mind that this is gonna be an inefficient application. Moreover, you bear the consequences of high upload times to the server of such a bloated application. In this situation haven't you felt if you had a tool that would tell you which are the absolutely necessary jar files to bundle?
Here it is: I've made this tool which is only one java class. I am also bundling the ant build file so that you do not have to spend too much time figuring out how to run this thing.
How to run this tool:
1. Pick one folder as a base folder for this tool. Make a folder structure src->com->gp. Copy the source code of the JavaDependencyWalker.java in a file with the same name in the gp folder.
2. Now, copy the code of build.xml into a file of the same name in the base folder.
3. If you already do not have an ant install please download it from here http://ant.apache.org and install it.
4. Edit the build.xml file and modify the three proerties mentioned at the top with the following guidelines:
Below is the source code:
JavaDependencyWalker:
package com.gp;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JavaDependencyWalker {
private Hashtable m_jarsHash = new Hashtable();
public JavaDependencyWalker(String jarPaths) {
StringTokenizer stTok = new StringTokenizer(jarPaths, ";");
while (stTok.hasMoreTokens()) {
File f = new File(stTok.nextToken());
if (f.exists() && f.isDirectory()) {
addAllJars(f);
} else {
System.err.println(f.getName() +
" :: Cannot find this folder.");
}
}
}
private void addAllJars(File f) {
if (f.isDirectory()) {
File[] files = f.listFiles();
for (int i = 0; i < files.length; i++) {
addAllJars(files[i]);
}
} else {
if (f.getName().toLowerCase().endsWith(".jar") ||
f.getName().toLowerCase().endsWith(".zip")) {
try {
JarFile jf = new JarFile(f);
Enumeration entries = jf.entries();
while (entries.hasMoreElements()) {
String entryName =
((JarEntry)entries.nextElement()).getName();
if (entryName.endsWith(".class")) {
entryName =
entryName.substring(0, entryName.indexOf("."));
StringTokenizer stTok =
new StringTokenizer(entryName, "/");
String str = "";
while (stTok.hasMoreTokens()) {
str += stTok.nextToken() + ".";
}
str = str.substring(0, str.length() - 1);
m_jarsHash.put(str, f.getCanonicalPath());
} else {
// skip it
}
}
jf.close();
} catch (IOException e) {
System.err.println(f.getName() + " :: " + e.getMessage());
}
} else {
// skip this file
}
}
}
public String getJarFilepath(String classname) {
return (String)m_jarsHash.get(classname);
}
public static void usage() {
System.out.println("java -Ddeps=<semi-colon seperated paths where all jars/zips may be found> -DclassToRun=<fully qualified classname to run> JavaDependencyWalker <all arguments to give to the class to run>");
}
public static void main(String[] args) {
String dependencies = System.getProperty("deps");
String classname = System.getProperty("classToRun");
if (dependencies == null || classname == null) {
usage();
System.exit(1);
}
JavaDependencyWalker javaDependencyWalker =
new JavaDependencyWalker(dependencies);
try {
String cp = "";
boolean noFound = true;
do {
Runtime rt = Runtime.getRuntime();
String cmdArr[] = null;
if (cp.equals("")) {
cmdArr = new String[args.length+2];
cmdArr[0] = "java";
cmdArr[1] = classname;
for (int i=0; i < args.length; i++) {
cmdArr[i+2] = args[i];
}
// cmd =
//"java " + args[1] + " ";
}
else {
cmdArr = new String[args.length+4];
cmdArr[0] = "java";
cmdArr[1] = "-classpath";
cmdArr[2] = cp;
cmdArr[3] = classname;
for (int i=0; i < args.length; i++) {
cmdArr[i+4] = args[i];
}
}
System.out.println("Command:" + cmdArr);
Process proc = rt.exec(cmdArr);
DataInputStream in =
new DataInputStream(proc.getErrorStream());
String line;
noFound = false;
while ((line = in.readLine()) != null) {
//System.out.println(line);
if (line.indexOf("ClassNotFoundException") != -1) {
String classNF =
line.substring(line.lastIndexOf(":") + 1).trim();
String cnfPackage = classNF.replace('/', '.');
if (javaDependencyWalker.getJarFilepath(cnfPackage) ==
null)
throw new Exception("No jar found for class " +
cnfPackage);
System.out.println("cnf: " + classNF + " found in " +
javaDependencyWalker.getJarFilepath(classNF));
noFound = true;
cp += javaDependencyWalker.getJarFilepath(cnfPackage) + ";";
} else if (line.indexOf("NoClassDef") != -1) {
String classNF =
line.substring(line.lastIndexOf(":") + 1).trim();
String ncdfPackage = classNF.replace('/', '.');
if (javaDependencyWalker.getJarFilepath(ncdfPackage) ==
null)
throw new Exception("No jar found for class " +
classNF);
System.out.println("NCDF: " + classNF + " found in " +
javaDependencyWalker.getJarFilepath(ncdfPackage) +
" " + ncdfPackage);
noFound = true;
cp += javaDependencyWalker.getJarFilepath(ncdfPackage) + ";";
}
}
} while (noFound);
StringTokenizer st = new StringTokenizer(cp, ";");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
}
}
}
ant build.xml:
<project name="JavaDependencyWalker" default="compile">
<property name="scolon.separated.folders.ofjars" value="c:/FUPv5/lib;c:/FUPv5/DeleteFile/classes;c:/FUPv5/WSClient/classes;c:/FUPv5/common/classes"/>
<property name="main.class" value="com.oracle.orion.fupv5.ws.client.SRWSClient"/>
<property name="args.to.class" value="-sr sdfds34 -user sldkfjd -password sdlfkjd -endpoint http://wd2088.us.oracle.com:7778/gateway/services/SID0003321 -type ddf -comm sdfd -stat Done"/>
<path id="classpath">
<pathelement location="classes"/>
</path>
<target name="compile">
<mkdir dir="classes"/>
<javac destdir="classes" classpathref="classpath" source="1.5" target="1.5" >
<src path="src"/>
</javac>
</target>
<target name="run" depends="compile">
<java classname="com.gp.JavaDependencyWalker" classpathref="classpath" fork="yes">
<jvmarg line="-Ddeps=${scolon.separated.folders.ofjars} -DclassToRun=${main.class}"/>
<arg line="${args.to.class}"/>
</java>
</target>
</project>
Here it is: I've made this tool which is only one java class. I am also bundling the ant build file so that you do not have to spend too much time figuring out how to run this thing.
How to run this tool:
1. Pick one folder as a base folder for this tool. Make a folder structure src->com->gp. Copy the source code of the JavaDependencyWalker.java in a file with the same name in the gp folder.
2. Now, copy the code of build.xml into a file of the same name in the base folder.
3. If you already do not have an ant install please download it from here http://ant.apache.org and install it.
4. Edit the build.xml file and modify the three proerties mentioned at the top with the following guidelines:
- scolon.separated.folders.ofjars = For the value of this property put a semi-colon separated absolute paths where the program can find all the jar files required for the class to run. For example, if the main class is a web service client that uses axis2 APIs then put the path to the lib folder of the axis2 install here. Also, do not forget to put paths to jar files of the custom classes you have built here. For example, you must put the path to the jar that contains the main class to run!
- main.class = This is the main class to run, for which you are finding the exact jars requirements.
- args.to.class = Here give all the arguments that will be required for the main class to run.
Below is the source code:
JavaDependencyWalker:
package com.gp;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JavaDependencyWalker {
private Hashtable m_jarsHash = new Hashtable();
public JavaDependencyWalker(String jarPaths) {
StringTokenizer stTok = new StringTokenizer(jarPaths, ";");
while (stTok.hasMoreTokens()) {
File f = new File(stTok.nextToken());
if (f.exists() && f.isDirectory()) {
addAllJars(f);
} else {
System.err.println(f.getName() +
" :: Cannot find this folder.");
}
}
}
private void addAllJars(File f) {
if (f.isDirectory()) {
File[] files = f.listFiles();
for (int i = 0; i < files.length; i++) {
addAllJars(files[i]);
}
} else {
if (f.getName().toLowerCase().endsWith(".jar") ||
f.getName().toLowerCase().endsWith(".zip")) {
try {
JarFile jf = new JarFile(f);
Enumeration entries = jf.entries();
while (entries.hasMoreElements()) {
String entryName =
((JarEntry)entries.nextElement()).getName();
if (entryName.endsWith(".class")) {
entryName =
entryName.substring(0, entryName.indexOf("."));
StringTokenizer stTok =
new StringTokenizer(entryName, "/");
String str = "";
while (stTok.hasMoreTokens()) {
str += stTok.nextToken() + ".";
}
str = str.substring(0, str.length() - 1);
m_jarsHash.put(str, f.getCanonicalPath());
} else {
// skip it
}
}
jf.close();
} catch (IOException e) {
System.err.println(f.getName() + " :: " + e.getMessage());
}
} else {
// skip this file
}
}
}
public String getJarFilepath(String classname) {
return (String)m_jarsHash.get(classname);
}
public static void usage() {
System.out.println("java -Ddeps=<semi-colon seperated paths where all jars/zips may be found> -DclassToRun=<fully qualified classname to run> JavaDependencyWalker <all arguments to give to the class to run>");
}
public static void main(String[] args) {
String dependencies = System.getProperty("deps");
String classname = System.getProperty("classToRun");
if (dependencies == null || classname == null) {
usage();
System.exit(1);
}
JavaDependencyWalker javaDependencyWalker =
new JavaDependencyWalker(dependencies);
try {
String cp = "";
boolean noFound = true;
do {
Runtime rt = Runtime.getRuntime();
String cmdArr[] = null;
if (cp.equals("")) {
cmdArr = new String[args.length+2];
cmdArr[0] = "java";
cmdArr[1] = classname;
for (int i=0; i < args.length; i++) {
cmdArr[i+2] = args[i];
}
// cmd =
//"java " + args[1] + " ";
}
else {
cmdArr = new String[args.length+4];
cmdArr[0] = "java";
cmdArr[1] = "-classpath";
cmdArr[2] = cp;
cmdArr[3] = classname;
for (int i=0; i < args.length; i++) {
cmdArr[i+4] = args[i];
}
}
System.out.println("Command:" + cmdArr);
Process proc = rt.exec(cmdArr);
DataInputStream in =
new DataInputStream(proc.getErrorStream());
String line;
noFound = false;
while ((line = in.readLine()) != null) {
//System.out.println(line);
if (line.indexOf("ClassNotFoundException") != -1) {
String classNF =
line.substring(line.lastIndexOf(":") + 1).trim();
String cnfPackage = classNF.replace('/', '.');
if (javaDependencyWalker.getJarFilepath(cnfPackage) ==
null)
throw new Exception("No jar found for class " +
cnfPackage);
System.out.println("cnf: " + classNF + " found in " +
javaDependencyWalker.getJarFilepath(classNF));
noFound = true;
cp += javaDependencyWalker.getJarFilepath(cnfPackage) + ";";
} else if (line.indexOf("NoClassDef") != -1) {
String classNF =
line.substring(line.lastIndexOf(":") + 1).trim();
String ncdfPackage = classNF.replace('/', '.');
if (javaDependencyWalker.getJarFilepath(ncdfPackage) ==
null)
throw new Exception("No jar found for class " +
classNF);
System.out.println("NCDF: " + classNF + " found in " +
javaDependencyWalker.getJarFilepath(ncdfPackage) +
" " + ncdfPackage);
noFound = true;
cp += javaDependencyWalker.getJarFilepath(ncdfPackage) + ";";
}
}
} while (noFound);
StringTokenizer st = new StringTokenizer(cp, ";");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
}
}
}
ant build.xml:
<project name="JavaDependencyWalker" default="compile">
<property name="scolon.separated.folders.ofjars" value="c:/FUPv5/lib;c:/FUPv5/DeleteFile/classes;c:/FUPv5/WSClient/classes;c:/FUPv5/common/classes"/>
<property name="main.class" value="com.oracle.orion.fupv5.ws.client.SRWSClient"/>
<property name="args.to.class" value="-sr sdfds34 -user sldkfjd -password sdlfkjd -endpoint http://wd2088.us.oracle.com:7778/gateway/services/SID0003321 -type ddf -comm sdfd -stat Done"/>
<path id="classpath">
<pathelement location="classes"/>
</path>
<target name="compile">
<mkdir dir="classes"/>
<javac destdir="classes" classpathref="classpath" source="1.5" target="1.5" >
<src path="src"/>
</javac>
</target>
<target name="run" depends="compile">
<java classname="com.gp.JavaDependencyWalker" classpathref="classpath" fork="yes">
<jvmarg line="-Ddeps=${scolon.separated.folders.ofjars} -DclassToRun=${main.class}"/>
<arg line="${args.to.class}"/>
</java>
</target>
</project>
Subscribe to:
Posts (Atom)