Quantcast

Ben McCann

Co-founder at Connectifier.
ex-Googler. CMU alum.

Ben McCann on LinkedIn Ben McCann on AngelList Ben McCann on Twitter

Helpful Bash Aliases

01/08/2009

Just a few to post for right now, but this entry may grow later.

# change to the real directory if in a linked directory
alias rd='cd `pwd -P`'

# search for and open file in sublime
function s {
  FILE=$1; test -f $FILE || FILE=`find . -name $FILE`; sublime $FILE;
}

Using the GWT ClickListener on an Element

11/24/2008

GWT uses what it calls a ClickListener instead of the standard HTML onclick attribute.  The normal way to use a GWT CLickListener would be as follows:

focusWidget.addClickListener(new ClickListener() {
  public void onClick(Widget sender) {
    // do something here
  }
});

Unfortunately, this seems not to work on an HTML Element.  Luckily, you can use JSNI to write some native JavaScript and get around the problem:

/**
 * Example of adding an onlick attribute to an element.
 */
public native void addClickListener(Element element) /*-{
  element.onclick = function() {
    // do something here
    return false;
  };
}-*/;

Just be careful about what your onclick is doing in order to avoid possible memory leaks.

Using a TV as a Monitor in Linux

10/22/2008

I’ve connected both a Syntax Olevia and an LG TV to my openSUSE desktop.  Neither have worked well because they caused the default font size to be unusably large.  I’m talking gigantically huge, like only a few letters on the screen at any one time.  The solution is to modify your /etc/X11/xorg.conf file. OpenSUSE 11.2 no longer uses an xorg.conf file by default, so you will need to create one by running sax2.

Because these TVs apparently lie to the operating system about their dimensions, in the Monitor Section you need to add the following:

Option "UseEDIDDPI" "FALSE"

Then change the DisplaySize using the following formula:

DisplaySize = (pixels/desiredDPI)*25.4

So, if I want to run at 110 DPI and 1366×768 resolution then I would set the following in my xorg.conf:

DisplaySize 315 177

19 Things College Students Should Know about a Career in IT

10/15/2008

I’m currently a Software Engineer at Google. It’s a great job that I absolutely love.  I’ll make the standard disclaimer here that anything I’ve written on this blog are my own opinions and not those of my employer.

My previous job was as a Web Developer at Sherwin-Williams.  One of the girls that interned there emailed me today to interview me for one of her classes.  I was happy to hear from her and decided to post my answers here to share with everyone else as well.  The first five answers are just about my own background.  Others will probably find questions six and onwards more interesting.

1.) How would you describe your typical day?
65% programming, 15% code reviews, 10% email, 10% meetings.

2.) What are the frustrations of your job?
Dealing with productivity losses – eg. slow builds, server restarts, insufficient system permissions, etc.

3.) What is your educational and career background?
BS Computer Science – Carnegie Mellon ’06
MBA – Carnegie Mellon ’06
Vanguard Group Technology Leadership Program – 7/06-1/07
Sherwin-Williams Web Developer – 1/07-7/08

4.) What are your career goals?
I’m aiming at becoming a Senior Software Engineer in the next 4-5 years.

5.) Where do you expect to be in 5 and 10 years in your career?
I generally don’t plan that far ahead.  It’s too hard to do and I find it’s better to be open to any opportunity that comes along.  But if I had to guess, I’d say I’d most likely be in either a Senior Engineering or Product Management position.

6.) What suggestions can you give a college student who is interested in your field as a career?
Do everything you can to position yourself for the internship you want.  Learn things they don’t always teach in school that are essential in the industry such as getting really good with some type of IDE (Eclipse, IntelliJ), source control (CVS, SVN, Git), unit testing (jUnit & EasyMock), Linux essentials, and possibly a build system (Maven, Ant, Make).  If you take the time to learn these things well you’ll find your course work all of a sudden gets a lot easier too.
Also, pay attention when algorithmic running time analysis is taught.  A lot of people have trouble with it at first, but once you grasp it, it’s not too hard.  If you want to be taken seriously then you’ve got to know when to use a linked list vs. hash table vs. binary tree and a lot of programmers don’t.

7.) What do you like/dislike most about what you do?
I love that I get to build something and see the tangible results at the end of the day/week/month.  It’s very rewarding.  It’s challenging and there’s always room to get better and learn something new.  I love seeing the creative ways in which people can do the same thing in a better way.
One of the less fun things to do, which isn’t uncommon, is try to figure out how to use technology that’s poorly documented or not yet mature.  I also probably wouldn’t mind getting to interact with a larger variety of people on a daily basis.

8.) Which part of the job is most challenging to you?
There’s such a multitude of technologies that goes into building any single system that there is always technology I’m unfamiliar with in something that I’m working with.  Also, it used to be very frustrating to have to debug code.  It’s getting to be less of an issue for me as I get better with writing tests and making incremental changes.

9.) How did you enter the field?
I started by making a guitar website in high school (tabworldonline.com).  It started out being a very basic static HTML site.  As I saw how many people were visiting it, I continued to work on it to try to draw in more visitors.  The site eventually got big enough that it was becoming very hard to maintain, so I was forced to learn PHP, MySQL, and JavaScript.  After I got my first taste of programming doing that, I went to CMU to study it.  I had a few internships in college as well, which were essential for getting a fulltime job.

10.) How do most people get into this field?
It’d be hard without studying it for a number of years – likely in college.  I found that when I went to college I was one of the few who hadn’t taken AP Computer Science in high school, which put me at a disadvantage.  A lot of my classmates had been programming since before they were teenagers, so I was always working to play catchup, but was also always surrounded by smart people that I could learn from.

11.) What is the best educational preparation?
A university Computer Science program.  If you want to be a developer, then know that an Information Systems program is better suited towards becoming a Business Analyst or Project Manager.

12.) What are the personal attributes that are essential for success in your field?
The people that are excel are the ones that really enjoy it.  That’s by far the number one contributer to success because it takes a lot of time, energy, and practice to get really good.  A good indicator of a job candidate’s ability is often whether they have any personal projects they’ve worked on outside of work.

13.) How much travel is required in you job?
None in my previous positions.  A minimal amount in this job.  Travel’s not strictly required for what I do, but sometimes face-to-face meetings are more effective, so it’s not completely uncommon.

14.) What can I do to expand my experiences to fit in your career area?
Find a personal project to work on that you’re interested in.  It will give you an opportunity to learn some essential industry skills you wouldn’t in school.  Or at least take a project course where you do one project you’re interested in the whole semester.  The technologies you use don’t matter because it’s a job requirement that you can learn new technologies and no one is expected to know them all.  What’s important are the skills and concepts.  If you’ve used one tool in each area (IDE, source control, unit testing, build, etc.), then there’s a good chance you’ve had to learn the associated concepts and can pick up a different technology easily.  A bigger project forces you to learn a lot of these things because it will be really tough if you don’t have a well thought out project structure, source control, and unit testing.  It’s nice if you’ve used somebody else’s framework at least once before you’ve graduated (eg. do a web development project using WebWork and Hibernate).

15.) What training opportunities are available in an entry level job?
Depends on your company’s budget, but the formal training I’ve gone to has been of extremely poor quality and the managers that book it don’t always realize what training would most directly relate to your job.  Sometimes colleauges will be asked to give presentation, which I find can be better since there’s a good chance it will be tailored towards your job.  There’s so much free information on the internet that formal training is really not needed.  Or see if your team has some type of a book budget and you can buy a couple of technical books (much cheaper than training, so managers will usually agree.)  You just have to be dedicated to taking an hour or two out of your day or after work to continue learning.

16.) Why do people leave the field?
There’s always something new to learn.  That’s why I love the job.  But a lot of people don’t like the fact that they have to continue to improve themselves or they’ll fall behind.

17.) Have you noticed that it is necessary to change companies in order to advance?
No, but it can certainly speed up your rate of pay increases.  If a company’s going to hire you away from your job, then they’ll expect to have to offer you a 10-15% pay increase in most cases.  It’s hard to get a promotion by switching companies because you won’t have a lot of luck getting hired for something you haven’t done before.  The most important thing for advancement is to take every opportunity to gradually take on new responsibility and perform well.  Find some small initiative to lead so that you can demonstrate that you have the ability to manage a project and execute.  It’s not too hard to find something that needs to get done that no one else wants to do.  You’ll be appreciated for doing it.  You’ll also be likely to succeed because the people around you will want the job to get done and won’t mind giving you advice.  If you don’t get a promotion for doing it, you’ll have something meaningful to talk about in your next job interview.

18.) How many hours a week do you work on average?
In my current position, I’d say 45 is average on a good week.  Sometimes it’s more during a crunch.  I often work extra hours though because I love the work and am trying to implement some extra feature or refactoring.  I never feel like it’s required and work short days fairly often too when I have something outside of work that I want to do.

19.) What do you wish you had known before choosing your career path?
I probably would have benefitted from sacrificing my grades a bit to put more time into finding better internships.  When you go to a job interview they care alot more about what you did at your past job than anything else.  I put a lot of effort in Freshman year to land an internship at Sherwin-Williams, which has benefited me ever since.  It’s very hard for a Freshman to get an internship, but it’s also very doable if you make it a priority.  Classes got harder the following years and I didn’t make it as big a priority, which I regret.

Struts 2 AJAX Tutorial – Dojo Autocompleter Example

09/20/2008

This tutorial was written for Struts 2.1.2. A lot has changed in newer versions. The Dojo Plugin is no longer officially supported by the Struts 2 team. Also, please see Muhammad’s comment in the comment section if you’re using the latest version of Struts 2.  You may want to check out Google Closure if you’re looking for a good JS library.

Struts 2 comes with a Dojo Toolkit plugin which makes developing an AJAX application easier than ever.  In addition, the AJAX plugin I would most recommend is the Struts 2 JSON plugin.  In this example, we will use the two side-by-side to create an autocompleter.  In order to use the Ajax support provided in Struts 2, I would recommend Struts 2.1.2 or later.  Include struts2-dojo-plugin-2.1.2.jar and jsonplugin-0.30.jar in your WEB-INF/lib directory.

First, we will create the action for the field that we wish to autocomplete:

package com.lumidant.tutorial.struts2.action;

import java.util.HashMap;
import java.util.Map;

import com.opensymphony.xwork2.ActionSupport;
import com.lumidant.tutorial.struts.dao.*;

public class AutocompleteField extends ActionSupport {

    private String city;
    private Map<String,String> json;

    public String execute() throws Exception() {
        return SUCCESS;
    }

    public String getCities() throws Exception() {
        json = new HashMap<String,String>();

        if(city != null && city.length() > 0) {
            CityDao dao = new CityDao();
            List<City> cities = dao.getCitiesStartingWith(city);
            for(City city : cities) {
                json.put(city.getId(), city.getName() + ", " + city.getState().getAbbreviation());
            }
        }

        return SUCCESS;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public Map<String,String> getJson() {
        return json;
    }

}

The getCities method in our action acts on the input String city that we’re given and creates a Map of cities starting with that text.  The key of our Map is going to be the hidden option value while the value of our Map is going to be our option text.

Next, we will modify our struts.xml configuration file to utilize the JSON plugin by extending json-default:

<package name="example" extends="json-default">
  <action name="AutocompleteField" class="com.lumidant.tutorial.struts2.action.AutocompleteField">
    <result type="json><param name="root">json</param></result>
  </action>
</package>

The root parameter that we specify is the name of the variable from our Action that we want to have converted and serialized to a JSON output.

If instead of serializing a Java object to JSON you wish to create the JSON string directly yourself, you can instead use a result type of “stream”:

<action name="jsondata" class="com.lumidant.tutorial.struts2.action.JsonDataAction">
  <result type="stream">
    <param name="contentType">application/json</param>
  </result>
</action>

And finally, we get to create our .jsp view:

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>

<html>
  <head>
    <sx:head />
  </head>
  <body>
    <s:url id="cityList" action="AutocompleteField" method="getCities" />
    <sx:autocompleter name="city" theme="ajax" href="%{cityList}" size="24" />
  </body>
</html>

For any of the Dojo plug-in features that you use, you will need to include the <sx:head /> tag, which includes the relevant JavaScript tags into your page’s head tag.  Then you can see that the autocompleter’s name attribute aligns with the city field in our Action and the url’s method attribute aligns with our Action’s getCities method.  Now, when you visit your new page and start typing in the text box, you should see the field autocompleting.  There are some other cool built-in Dojo tags that you should look at as well like the datetimepicker, which provides a really nice calendar.

Struts 2 Tutorial – Validation and Error Handling

09/20/2008

Validation is an important part of any web framework because it is one of the most painfully repetitive things to have to continually recreate.  In Struts 2, validation is handled by creating an Action-validation.xml file.  So, if we have an AddUser Action then we would create an AddUser-validation.xml file:

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
    "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

<validators>
  <field name="userId">
    <field-validator type="requiredstring">
      <message>Username is required</message>
    </field-validator>
  </field>
</validators>

It is important that you have both a getter and setter for the field you are validating.  For example, AddUser would need both of the following method signatures for validation to work properly: “public String getUserId()” and “public void setUserId(String userId)”.  You can get more information about the other types of validators from the Struts 2 documentation.

You can also control error handling from your Action class.  For example, let’s say that the user picks a username which is already registered.  We wouldn’t be able to determine this with validation alone, but would need to check the database in our Action class.  So when we determine that we can call “addActionError(String message)” from our class.  Then when we want to print any errors out on our page, we simply use the tag:

<s:actionerror />

Struts 2 Tutorial – Struts Configuration

09/20/2008

The struts.xml file defines the relationship between actions and .jsp views, the inclusion of interceptors, and possible result types.  It’s essential that you be able to handle at least basic configuration changes, so we’ll demonstrate the most important and frequently used below.

Packages: Extension, Namespaces, and More

For more complex applications, you will likely want to split the configuration between multiple packages.  In Struts 2.0.x, the the default package is struts-default.  Most of the time, extending this package is a pretty good place to start from.

<package name="example" extends="struts-default">
  <interceptors>
    <interceptor name="authorization" class="com.lumidant.tutorial.struts2.interceptor.AuthorizationInterceptor">

    <interceptor-stack name="authStack">
      <interceptor-ref name="authorization" />
      <interceptor-ref name="defaultStack" />
    </interceptor-stack>
  </interceptors>

  <global-results>
    <result name="error">/WEB-INF/pages/error.jsp</result>
  </global-results>

</package>

In the package above, we did not define any action mappings, but rather we defined some more general properties that we can share between other packages.   We can now extend the package we just defined when creating new packages.  This package and any that extend it will have all Action.ERROR results go to error.jsp.  We also defined a new interceptor and interceptor stack that our actions and packages can make use of.  In this example, you could imagine that we would have two other packages extending this one.  One package would contain pages that any user could visit.  The other package could contain pages requiring the user to be logged in, so we would set the default-interceptor-ref to “authStack” as shown below:

<package name="admin" namespace="/admin" extends="example">
  <default-interceptor-ref name="authStack"/>

  <action name="DeleteSomething_*" method="{1}" class="com.lumidant.tutorial.struts2.action.DeleteSomething">
    <result name="input">/WEB-INF/pages/deleteSomething.jsp</result>
    <result>/WEB-INF/pages/deleteSuccessful.jsp</result>
  </action>
</package>

We also introduced additional new topics in this example.  Firstly, you’ll see that we created a namespace.  That means that all the actions in this package will be accessed through a spearate URL.  In this example it is an “/admin” URL.  Also, you’ll see that we put the “*” wildcard in the action name.  What this is doing is taking what was located in the * and replacing the “{1}” with that string.  Since the “{1}” is located in the method attribute, we will be calling a method on our action.  So let’s say the user accesses /admin/DeleteSomething_input.action.  Then the input method of the DeleteSomething.java action will be called.  Assuming we did not define our own input method, then we will inherit the input method from ActionSupport, which will take us to the result named “input”, which in this example is the deleteSomething.jsp page.

Better URL Structure

By adding the following a child of the struts tag, you no longer need to append .action to your URLs.

<constant name="struts.action.extension" value="action,,"/>

Action Chaining

<action name="LogIn_*" method="{1}" class="com.lumidant.tutorial.struts2.action.LogIn">
  <result type="chain">ShowHomepage</result>
</action>

What happens when we don’t want to show a .jsp page as the result of our action, but instead want to continue on directly to another action?  We simply use the result type “chain” to forward to another action.

Redirect After Post

<action name="SubmitOrder_*" method="{1}" class="com.lumidant.tutorial.struts2.action.SubmitOrder">
  <result name="redirect" type="redirect-action">
    <param name="actionName">DisplayOrder</param>
    <param name="namespace">/user</param>
  </result>
</action>

Very often, after an action is taken, we want to redirect the user to a separate action.  For example, let’s say the user submits an order.  If we leave the URL in their browser as SubmitOrder.action and they hit refresh, then the order could be submitted a second time.  So after they submit an order, it’s probably better if we redirect them to another action, such as one displaying the order they just placed with a thank you message.

Struts 2 Tutorial – Interceptors

09/19/2008

Interceptors are my favorite aspect of Struts 2. They inspect and/or act on a user’s request. There are three main uses cases that I’ll discuss here: intercepting before the action, between the action and view, and after the view. At the end, I’ll show you how to add your brand spankin’ new interceptor to your struts.xml file so that it is called on each request.

Intercepting Before the Action:

package org.lumidant.tutorial.struts2.interceptor;

import java.util.Map;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthorizationInterceptor extends AbstractInterceptor {

	private static final String USER_KEY = "user";

	public String intercept(ActionInvocation invocation) throws Exception {
		Map session = invocation.getInvocationContext().getSession();
		if(session.get(USER_KEY) == null) {
			addActionError(invocation, "You must be authenticated to access this page");
			return Action.ERROR;
		}

		return invocation.invoke();
	}

	private void addActionError(ActionInvocation invocation, String message) {
		Object action = invocation.getAction();
		if(action instanceof ValidationAware) {
			((ValidationAware) action).addActionError(message);
		}
	}

}

You can see that we check to see if the user is authorized and present an error message if he/she is not logged in. We do this by adding an ActionError, which can be displayed on your view. If the user is logged in, then he/she proceeds normally.

Intercepting Between the Action and Generation of View:

package org.lumidant.tutorial.struts2.interceptor;

import java.util.Map;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultInterceptor;
import org.apache.struts2.ServletActionContext;

public class WirelessInterceptor extends AbstractInterceptor {

	private static final String RESULT_CODE_SUFFIX_WIRELESS = "Wireless";
	private static final String REQUEST_HEADER_ACCEPT = "Accept";
	private static final String ACCEPT_HEADER_WIRELESS = "vnd.wap";

	public String intercept(ActionInvocation invocation) throws Exception {

		invocation.addPreResultListener()(new PreResultListener() {
			public void beforeResult(ActionInvocation invocation, String resultCode) {

				// check if a wireless version of the page exists
				// by looking for a wireless action mapping in the struts.xml
				Map results = invocation.getProxy().getConfig().getResults();
				if(!results.containsKey(resultCode + RESULT_CODE_SUFFIX_WIRELESS)) {
					return;
				}

				// send to wireless version if wireless device is being used
				final String acceptHeader = ServletActionContext.getRequest().getHeader(REQUEST_HEADER_ACCEPT);
				if(acceptHeader != null && acceptHeader.toLowerCase().contains(ACCEPT_HEADER_WIRELESS)) {
					invocation.setResultCode(resultCode + RESULT_CODE_SUFFIX_WIRELESS);
				}
			}
		});

		return invocation.invoke();
	}
}

In this example, the action has already been taken, but we have not generated the view that the user will see yet. This allows us to send the user to a separate page if he/she is using a Blackberry. We check the Accept HTML header to see if the string “vnd.wap” is present and if so then we append “Wireless” to the result code. So if the action was going to send the user to the result “Success” then we will instead show them the result “SuccessWireless”. We could easily adopt this technique for a Google Android or an iPhone as well.

Intercepting after View Creation:

package org.lumidant.tutorial.struts2.interceptor;

import java.util.Map;

import org.hibernate.Transaction;
import org.marketcharts.data.dao.util.HibernateSessionFactory;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class HibernateInterceptor extends AbstractInterceptor {

	public String intercept(ActionInvocation invocation) throws Exception {
		try {
			return invocation.invoke();
		} catch(Exception e) {
			Transaction tx = HibernateSessionFactory.getSession().getTransaction();
			if(tx != null && tx.isActive()) {
				tx.rollback();
			}
			return Action.ERROR;
		} finally {
			HibernateSessionFactory.getSession().close();
		}
	}

}

This example uses Hibernate, an Object Relational Mapper (ORM), which is library for easy database access. Hibernate needs an open session when the view is created (Open Session in View Pattern) because it lazily accesses the database. If the session is closed and we go to access the database, then Hibernate will throw an Exception. So this interceptor provides a method to close the session after the view has already been created and all the database calls have been made.

Adding an Interceptor to your struts.xml:

<package name="example" extends="struts-default">
  <interceptors>
    <!-- 
      One possible use of an interceptor is to send the user to special version of the .jsp
  	view if they are using a Blackberry as shown above.
    -->
    <interceptor name="wireless" class="com.lumidant.tutorial.struts2.interceptor.WirelessInterceptor"/>

    <interceptor-stack name="wirelessStack">
      <interceptor-ref name="exception" />
      <interceptor-ref name="servlet-config" />
      <interceptor-ref name="i18n" />
      <interceptor-ref name="chain" />
      <interceptor-ref name="params" />
      <interceptor-ref name="wireless" />
    </interceptor-stack>
  </interceptors>

  <default-interceptor-ref name="wirelessStack" />
</package>

In this example, we’ve change the default interceptor stack to be a custom stack which we’ve created. You can also extend existing stacks.

Struts 2 Tutorial – Creating Views

09/09/2008

One of the first things you’ll want to do after getting started with Struts 2 is create more complex user interfaces. We call the UI a view because Struts2 makes use of the Model-View-Controller (MVC) design pattern.  For this example, let’s assume we have an action that returns a list of all the employees at our company:

package com.lumidant.tutorial.struts2.action;

import com.opensymphony.xwork2.ActionSupport;
import com.lumidant.tutorial.struts.dao.*;
import com.lumidant.tutorial.struts.model.*;

public class ExampleAction extends ActionSupport {

    private static final long SerialVersionUID = 1L;
    private List<Employee> employees;

    public String execute() throw Exception {
        EmployeeDao dao = new EmployeeDao();
        employees = dao.getAllEmployees();
        return SUCCESS;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

}

Now let’s create a page that iterates through our list of employees and prints each employee’s name in alternating row colors:

<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
  <head>
    <title>Struts 2 Tutorial Example</title>
    <style type="text/css">
      .odd td { background-color: #fff; }
      .even td { background-color: #eee; }
    </style>
  </head>
  <body>
    <h2>All Employees:</h2>
    <table>
      <s:iterator value="employees" status="rowstatus">
        <tr <s:if test="#rowstatus.odd">class="odd"</s:if><s:else>class="even"</s:else>>
          <td class="fn n">
            <span class="given-name"><s:property value="firstName" /></span>
            <span class="additional-name"><s:property value="middleName" /></span>
            <span class="family-name"><s:property value="lastName" /></span>
          </td>
        </tr>
      </s:iterator>
    </table>
  </body>
</html>

What we just demonstrated is a fairly common use case and should provide a good example for doing the same in the future.  Also notice our use of the if tag and the rowstatus variable. If we wanted to put a comma after every item except the last we could do something like:

<s:if test="!#rowstatus.last">,</s:if>

Another common use case is to access the session from within the view.  For example, you may want to show a link only to logged in users.  Again, we will use OGNL to accomplish this.

<s:if test="%{#session.user.isAdmin()}">
  <a href="Admin.action">Only Admins Should See This Link</a>
</s:if>

That should give you a pretty good idea of the basics, so we’ll move onto an internationalization example.  You’ll want to create a package.properties file in the directory where your actions reside:

form.label.firstName=First Name:
form.label.lastName=Last Name:

This file contains text for the default translation of your site.  All static text on your site should be contained in an internationalization file.  You can then include the text in your JSP with the Struts 2 text tag in the following manner: <s:text name=”form.label.firstName” />.  You can create a package.properties file for each translation and append the suffix for each translation language (and optionally country – eg. en_US). For example, below is a package_pt.properties file, which contains a Portuguese translation of our example site:

form.label.firstName=Primeiro Nome:
form.label.lastName=Último Nome:

Now when the user visits the site, Struts will detect their browser language and serve the appropriate page to them.

That should give you pretty decent idea of how to create views for your site.  Please check out our other Struts 2 tutorials for information on other aspects of Struts 2.

Newer Posts
Older Posts