Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Monday, April 21, 2008

Multi-threaded Socket Server in Java

I have always been curious about how to make a multi-threaded server in Java, and it turns out to be pretty simple to get something basic up and running.

My goal was to create a program that would listen for incoming connections from a client, and repeat whatever is sent to that server. You can see this server work using a telnet session on port 5000.


telnet localhost 5000


Then, whatever you type will be echoed back to you. If you want to quit, just type exit.

Here's the code, consisting of 2 classes.


package sockets;
import java.io.*;
import java.net.*;

/**
* Includes main method to get things going
* Otherwise, this just prepares a new ServerSocket that
* listens for new connections
* and starts a new thread for each connection
*/
public class EchoServer {
public void go(){
try{
ServerSocket serverSocket = new ServerSocket(5000);
while(true){
System.out.print("Listening for connections on port 5000... ");
Socket client = serverSocket.accept();
Thread t = new Thread(new EchoClientHandler(client));
t.start();
System.out.println("Connected - "+client.getInetAddress());
}
}catch(Exception e){
e.printStackTrace();
}
}

public static void main(String[] args) {
EchoServer server = new EchoServer();
server.go();
}
}

/**
* The Runnable job that takes in the new Socket
*/
class EchoClientHandler implements Runnable{

private final BufferedReader reader;
private final PrintWriter output;
private final Socket socket;
private static final String MESSAGE = "ECHO... [?]\r\n";
private static final String EXIT_MESSAGE = "Sad to see you go. Goodbye.\r\n";
private static final String WELCOME_MESSAGE = "Welcome to the Echo Server. Type something to see it echoed back to you!\r\n";

public EchoClientHandler(Socket incomingSocket) throws IOException{
socket = incomingSocket;
output = new PrintWriter(incomingSocket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}

public void run(){
try{
output.write(WELCOME_MESSAGE);
output.flush();
String line = null;
while((line = reader.readLine()) != null){
boolean quit = false;
output.write(MESSAGE.replaceAll("\\?", line));
if(line.trim().equalsIgnoreCase("exit")){
output.write(EXIT_MESSAGE);
quit = true;
}
output.flush();
if(quit){
break;
}
}
}catch(Exception e){
System.err.println("OUCH! "+e.getMessage());
}finally{
try{socket.close();}catch(Exception ee){}
}
}
}


Here's how it works.

When the EchoServer starts up, it opens a server socket that listens for incoming connection requests. When it gets a request, it creates a new handler instance, taking the Socket as an argument. The handler is a Runnable class, and used to start a new Thread. The result is that the EchoServer can handle more than one connection at a time -- just like a good socket server should.

To expand on this, it would be a good idea to implement some means of managing the threads and connections. Using the java.util.concurrent API would be a good thing to do to implement some means of thread pooling in the server instead of using naked Threads. Thread pooling is an important thing to implement because each new persistent connection requires a new thread. Too many connections would cause some real resource problems. However, if there are limits to the amount of connections the server will allow, you need to manage connections. Therefore, some intelligence might need to be added to a Client program, so that a connection need only stay alive for a certain amount of time before becoming disconnected. Then, a well designed client might seamlessly create a new connection to the server.

Tuesday, January 22, 2008

Leopard for the Web Developer - Running Tomcat as a Service

In my experimentation with configuring multiple virtualhosts with SSL, I stumbled upon the magic of launchd within mac os x. I use it to make a call to ifconfig when I start up to create a new loopback interface, and it works splendidly. Knowing that it would work on startup, I thought I'd apply the principles to Tomcat.

The process of setting up Tomcat as a service in Leopard is extremely simple. It goes something like this:

  1. Download Tomcat
  2. Unpack Tomcat where you want it (/usr/local/tomcat is a good spot)
  3. Create a lauchd plist configuration to point to the tomcat executable
  4. Place that configuration file someplace where launchd will find it.
  5. invoke launchdctl to install the new service
Once you do steps 1 and 2, it's time to create the launchd plist file. Here's what mine looks like -- I'll explain what's going on following

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

<key>Disabled</key>
<false/>

<key>EnvironmentVariables</key>
<dict>
<key>JAVA_HOME</key>
<string>/System/Library/Frameworks/JavaVM.framework/Home</string>
</dict>

<key>Label</key>
<string>org.apache.tomcat.tomcat6</string>

<key>ProgramArguments</key>
<array>
<string>/usr/local/tomcat6/bin/catalina.sh</string>
<string>run</string>
</array>

<key>RunAtLoad</key>
<true/>

<key>ServiceDescription</key>
<string>Tomcat 6 Server</string>

</dict>
</plist>

OK, here's what's happening. The plist defines a dictionary data structure instructing launchd how it should work. You'll see a <key> followed by a value of some sort. I'll go key by key.

  1. Disabled - a boolean value.
    If you want to have this active, set the value to false, as it is here. If you want to disable the service without uninstalling it, just change the disabled value to true.
  2. EnvironmentVariables - a (sub)dictionary value.
    This is just a set of name value pairs defining environment variables that you might need. In our case, Tomcat needs to know what JAVA_HOME is. You can set more if you want by adding more <key> and <string> pairs
  3. Label - a String value.
    This one is really important. It needs to be a unique identifier for the launchd process. launchd keeps track of all the things it starts and stops with this Label value. Just make sure that it's unique. It's common practice to use the reverse-domain syntax that you see here (org.apache.tomcat.tomcat6). Incidentally, it's also common practice to name this plist file with the same label (org.apache.tomcat.tomcat6.plist)
  4. ProgramArguments - an array of strings.
    Think of this as what you would type at the command line. Basically, anything you can do at the command line can be placed in a ProgramArguments value. The first string in the array is the executable program that you wish to run. Anything following is an argument that you would pass to that command. In our case, this merely does the following command:
    /usr/local/tomcat6/bin/catalina.sh run
  5. RunAtLoad - boolean value.
    This is what makes this a service, telling OS X that this should run when starting up.
  6. ServiceDescription - String value.
    This helps in the console to figure out what's running. It should just be some sort of description identifying the process. In this case, Tomcat 6 Server.
And that's it for the configuration file. Go ahead and save it as org.apache.tomcat.tomcat6.plist on your desktop, or somewhere else obvious. We'll move it to where it needs to reside next.

Launchd looks in a couple of places for these kinds of configuration files. From the man pages of launchd:

~/Library/LaunchAgents         Per-user agents provided by the user.
/Library/LaunchAgents Per-user agents provided by the administrator.
/Library/LaunchDaemons System wide daemons provided by the administrator.
/System/Library/LaunchAgents Mac OS X Per-user agents.
/System/Library/LaunchDaemons Mac OS X System wide daemons.


Since I want tomcat to start up as a machine process, not a user one, I place it in /Library/LaunchDaemons. If I wanted to make it a user process, I put it in ~/Library/LaunchAgents. A couple notes about this. First, if you put your file in /Library/LaunchDaemons, the plist file needs to be owned by root. The easiest way to do this is to open Terminal and do a "sudo cp org.apache.tomcat.tomcat6.plist /Library/LaunchDaemons/". This will make root own the file so it can start up as a system process. Second, if you decide to place the file in ~/Library/LaunchAgents, you don't need root ownership, but you do need to make sure the directory exists. By default, Leopard does not have a "LaunchAgents" folder in the user Library. So, you might have to make that directory before you copy your file there.

Final step, you'll need the Terminal. It's install time.

If you placed the plist in /Library/LaunchDaemons, do this

sudo launchctl load /Library/LaunchDaemons/org.apache.tomcat.tomcat6.plist


If you placed the plist in ~/Library/LaunchAgents, do this

launchctl load ~/Library/LaunchAgents/org.apache.tomcat.tomcat6.plist


That should do it. You now have Tomcat installed as a service on Leopard.

Friday, January 18, 2008

JSON Recursion -- Making a Self Reference

I have been working on a simple JSON library in Java which will allow me to serialize a JSON string. I'm pretty satisfied with the results so far, and I intend to release the code, however, until today, there was one thing that threw me for a loop. Writing JSON including a self reference. Here's the problem

When you write the following JSON syntax:

{"me":this}

You expect that when you access the 'me' property, it would give a reference to that object. However, what you get instead is a reference to the Window object. This happens because the closures that javascript has assigns the 'this' to the Window because during the construction of the new object, the new object doesn't yet exist, and 'this' still refers to the Window. Not what we wanted.

Here's the fix:
{
"me":function(){
this.me = this;
return this;
}
}.me()

Instead of assigning the 'me' attribute to 'this' right away, we construct the object, setting the 'me' attribute to a function. The 'me' function re-assigns its own value, and in effect, kind of self-destructs. Finally, we make the 'me' function return a reference to 'this'. 'me' is now assigned a reference to itself.

This method works within an eval as well. It also nests pretty well too. Here's a more complicated example, with a sub-object containing a self reference, as well as multiple self-references:
{
"me":function(){this.me = this; return this;},
"alsoMe":function(){this.alsoMe = this; return this;},
"subObject":{
"subMe":function(){this.subMe = this; return this;}
}.subMe()
}.me().alsoMe()

Monday, October 29, 2007

Leopard for the Web Developer - installing mod_jk

Today I was pleased to get a few comments from a fellow programmer anxious to set up his Mac so Apache could connect with Tomcat with the help of mod_jk. He came looking for info, found none, and kindly came back later to reveal his findings. Here's billo's instructions about how to install mod_jk on Mac OS X Leopard (for an intel Mac).



He explains in some pretty good detail why it fails out of the tar-box, so I won't go into it. However, what I will do is give you a line-by-line for how to make it work. I had to fill in a couple gaps after finding out what the problem was. Here's what you need to do.



1. Download and unpack the source of mod_jk (I installed version 1.2.25)



2. Make your way into the source directory (tomcat-connectors-[version]-src/native)


$ cd tomcat-connectors-1.2.25-src/native


3. Edit the apache-2.0/Makefile.apxs.in file with billo's fix. This is the solution that fixes the build. What you need to do is replace these lines:


mod_jk.la:
$(APXS) -c -o $@ -Wc,"${APXSCFLAGS} ${JK_INCL}" "${JAVA_INCL}" "${APXSLDFLAGS}" mod_jk.c ${APACHE_OBJECTS}

with these:


mod_jk.la:
$(APXS) -c -o $@ -Wc,"${APXSCFLAGS} -arch x86_64 ${JK_INCL}" "${JAVA_INCL}" "${APXSLDFLAGS} -arch x86_64 " mod_jk.c ${APACHE_OBJECTS}

The tab at the beginning of the $(APXS) line is very important! Don't remove it.



4. While you're in the [src]/native directory, configure the build files


$ ./configure --with-apxs=/usr/sbin/apxs


5. Change directory into apache-2.0 and get ready to build.


$ cd apache-2.0


6. Make the module using apxs as your compiler


$ make -f Makefile.apxs


7. Install the module


$ sudo make install


From there, you're on your own getting it configured for Apache. But the documentation for configuring mod_jk is  abundant.

Wednesday, October 24, 2007

Leopard for the Web Developer

I did it. I pre-ordered Mac OS X Leopard. I'm looking forward to getting it for lots of reasons, but primarily because I plan on setting up a system that's just super righteous for a web developer (mostly java and php related).

In the posts to come, I'll list the steps involved in installing and configuring various features. My ultimate goal is to set up a disk image that contains the righteous set-up, so that I can restore it to my hard drive when I need to. I'll do this by installing Leopard onto an external drive before I actually install it on my Mac. This will afford me the luxury of making a disk image that's totally clean and uncluttered of the personal stuff, and allow me to continue work as usual while I perfect the Leopard install.

Stay tuned! Among the things I plan on doing include:

  • Configuring Apache Virtualhosts to handle custom domains under development.
  • Setting up the hosts file for those domains
  • Running PHP4 and PHP5 on the same apache install - both as a module!
  • Install Tomcat 5.5 and Tomcat 6 - maybe to run as a service
  • Connect Apache web server to Tomcat installations with modjk
  • Create at least one self-signed SSL Certificate for testing Secure domains in Apache
  • Install Eclipse as well as various plugins
  • Install Maven
  • Install Subversion
  • Install MySQL
  • I haven't played with it yet, but it seems like a glaring omission to leave out a Ruby on Rails set up. I may do that if I get inspired.
  • Install virtualization software. I have Parallels 2 now, but I'm considering a move to VMWare's Fusion. It's really a matter of dedicating the dollars. An upgrade to Parallels 3 is almost the same as buying Fusion outright.
So there it is! I expect that I'll get rolling with the details this weekend.

Wednesday, July 25, 2007

Struts 2 Security Update

A couple of weeks ago, a remote exploit was demonstrated for applications using Struts 2.0.8 and below. It's a scary one. Like System.exit(0) scary. In some ways I can't believe that it got this far because it's such a simple one.

Anyway, if you're using Struts 2 below verson 2.0.9, or if you're using WebWork below version 2.0.4, do yourself a favor and UPDATE your jars.

Easy way: just update your xwork jar file (download the full lib here)
Better way: update to Struts2.0.9

Thursday, April 12, 2007

I know why a Struts 2 File Upload fails

Yesterday I began some work on using Struts2 to do a file upload. The way it works is pretty slick. At least when it works.

The problem was that when uploading files, I'd be surprised with null values. However, when pressing the back button and re-submitting the form, it would work fine. Odd behavior indeed, but I'm not alone. Other people have had the same problem uploading files with Struts 2.0.6.

I dug into it a little bit. To quite a deep level actually, and I found the bug. The good news is that it seems to be related specifically to Struts 2.0.6. There's a point in the Filter, which serves as the starting point of the whole framework, where the HttpServletRequest is supposed to be wrapped by the appropriate class for later data retrieval.

In the case of the mysterious broken file upload, the interceptor (FileUploadInterceptor) that handles populating the struts action with the values checks to see if the Request object is an instance of the MultiPartRequestWrapper. If it is, the interceptor attempts to pull out the multipart data; that is, the actual file bytes. If it's not, it does nothing. When the file uploads don't work it means that the Request has not been wrapped in the MultiPartRequestWrapper. Therefore the FileUploadFilter doesn't do anything, even though the data is actually there.

The reason why it doesn't get wrapped relates to the FilterDispatcher (org.apache.struts2.dispatcher package) and the prepareDispatcherAndWrapRequest method. In that method, there's a logic block which deals with the Dispatcher instance. When the instance is null, it sets the Dispatcher instance to one that will work. The peculiar thing about this block, is that the part of the method that does the request wrapping is also in that logic block. Therefore, the request doesn't get wrapped in the MultiPartRequestWrapper unless the Dispatcher instance is null. When the file uploads failed, this instance was NOT null, causing the Request to push through as the RequestFacade. The FileUploadInterceptor doesn't work with that data type.

The fix? Just put the code that does the wrapping outside of the logic block that deals with the dispatcher instance.

To the Struts team's credit, it looks like this bug has been fixed. The code in the repository after the 2.0.6 tag does the wrapping correctly.

Here's some code for those who care.

First, the code that comes with Struts 2.0.6

    protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {

Dispatcher du = Dispatcher.getInstance();

// Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
// configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,
// static instance of Dispatcher should be null.
if (du == null) {

Dispatcher.setInstance(dispatcher);

// prepare the request no matter what - this ensures that the proper character encoding
// is used before invoking the mapper (see WW-9127)
dispatcher.prepare(request, response);

try {
// Wrap request first, just in case it is multipart/form-data
// parameters might not be accessible through before encoding (ww-1278)
request = dispatcher.wrapRequest(request, getServletContext());
} catch (IOException e) {
String message = "Could not wrap servlet request with MultipartRequestWrapper!";
LOG.error(message, e);
throw new ServletException(message, e);
}
}
else {
dispatcher = du;
}
return request;
}

Code that works:


    protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {

Dispatcher du = Dispatcher.getInstance();

// Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
// configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,
// static instance of Dispatcher should be null.
if (du == null) {

Dispatcher.setInstance(dispatcher);

// prepare the request no matter what - this ensures that the proper character encoding
// is used before invoking the mapper (see WW-9127)
dispatcher.prepare(request, response);

}
else {
dispatcher = du;
}


//Note that this wrapping code is where it should be
try {
// Wrap request first, just in case it is multipart/form-data
// parameters might not be accessible through before encoding (ww-1278)
request = dispatcher.wrapRequest(request, getServletContext());
} catch (IOException e) {
String message = "Could not wrap servlet request with MultipartRequestWrapper!";
LOG.error(message, e);
throw new ServletException(message, e);
}
return request;
}