★ wanayoo — archive 1999 http://developer.iplanet.com/viewsource/kadel_ssjava/kadel_ssjava_p.htmlNouvelle recherche | Portail wanayoo
         downloads
 technologies
 support
 
Server-Side Java: Extending Web Servers Through Java
Part 1: Client-Server Programming With Java Applications

By Rich Kadel


Send comments and questions about this article to View Source.

Java has matured to the point where it should now be considered for serious application development.  To be honest, I probably wouldn't have said that a year ago, about the time I started trying some serious things. But Java and I have matured together, at about the same pace. Now that I do serious things with Java, I have less of a fear that Java might not do what I want it to do.

Don't get me wrong. I know there's still lots of room for improvement. But I know where the boundaries are, I know how to avoid stepping over them, and I constantly see those boundaries expanding with each new Java implementation. Java is actually quite powerful, and it has a number of advantages over other languages that, in my opinion, make Java a better choice for almost all programming tasks:

  • Java has a powerful feature set, including built-in threads, exceptions, security, and an excellent object model.
  • Java is actually quite simple to use, considering its feature set. Although some might disagree, I think Java is much simpler than C++.
  • Java source code and compiled class files work, automatically, across a multitude of platforms. All of my applications run, without modification, on Windows, Mac OS, and UNIX systems.
  • Java is inherently network-aware, with objects that map to sockets (TCP/IP network connections), server sockets, URLs, and other useful networking capabilities.

I have written client/server applications in Java, C++, C, and other languages for more than 10 years now. I have to say that, when I started my first Java client/server application, I was stunned at how easy it was to develop; it's been even easier to maintain and extend. This is due, primarily, to the fundamental features of the Java language itself, in that Java is object-oriented, multithreaded, and network-aware.

With the data streaming interfaces we have now in Java 1.0.2, and the object serialization we will have in future Java 1.1 implementations, Java is an excellent choice for developing client-server applications. Java is often thought of as the client-side language for developing "thin-client" applications. But if Java is also used to develop the server, the same objects, and therefore the same source code and algorithms, can be used on both sides of the client/server connection. I'll spend the rest of the article going into each of these advantages in detail.

Developing server-side software for the Web

There used to be few choices when it came to developing for the Web. HTML was about it at first. Then the Common Gateway Interface (CGI) came along, but the language choices were still usually limited to PERL or C, for lack of examples or tools in other languages. Then came the server application programming interfaces, or APIs, such as the Netscape Server Application Programming Interface (NSAPI), and more client-side (browser-side) programming choices including Java and JavaScript as well as server-side JavaScript. Now the choices are much broader, and they include Java on both the client and the server.

This is the first of a two-part article on developing server-side programs in Java. If you're not sold on the idea, read on. I will first talk about why Java is an excellent choice for server-side programming. Then I'll get into some of the nuts-and-bolts of Java development on the server, using Java applications. In part two, I'll dive head-first into Netscape Server Applets (Web server-embedded Java programs) and LiveConnect for the server, which is an important part of the new features of Netscape Enterprise Server 3.0, now available in beta. This technology is very powerful, even though it's still very new, and it allows you to actually extend the server using Java applets that are web server-aware.

Difference between Java Applets and Java Applications

Java first gained its wide-spread popularity from several simple, yet interesting, Java applets running under Netscape Navigator 2.0. These examples showed users that Web pages could actually be very dynamic, and even interactive, using Java. But most of these initial applets demonstrated little more than spinning heads, moving text, and 3D molecules. Because of the strict security constraints placed on Java applets, these examples were cute, but not very useful. (This is changing with the release of Java 1.1 and Netscape Communicator; see the sidebar "Object Signing to the Rescue.")

I'm sure somebody is going to disagree with me here, but from my point of view, the typical Java applet has a lot of flash, but very little function. Without the ability to interact with its environment - for example, by saving a new file, or communicating information with another program - a Java applet puts the end-user in a very passive, "Look, but don't touch" role. Most popular software applications - word processors, spread sheets, databases - are centered on the "data" and how the user manipulates the data. Stand-alone Java applets have built-in functions to download images, sound files, and even some static data. But for the sake of security (an important, albeit suffocating cause) Java can't, for example, drop that file into, say, a Microsoft Word document (except through some really basic, unformatted cut-and-paste), and it can't read an image you created in Corel Draw 7.

Object Signing to the Rescue

The new version of Java, Java 1.1, introduces some new security features. In particular, applets and the data they produce can be digitally "signed." Netscape Communicator supports this feature, and Netscape has released a product called the JAR Packager to make it easier for developers to sign their applets. User can set up Communicator to "trust" applets that are signed by a particular person or company.

Signed applets can then, at the user's option, have access to local system resources - like the local disk - previously restricted to Java applets.

One of the more recent concepts, as I understand it, will allow an applet to save and load signed data. Through the browser, you should be able to restrict an applet to saving and restoring only its own files. This mechanism will allow applets like word processors to save documents to a user's hard disk.

This is not a solution for applications that should be client-server oriented, and will not help thin clients (like a Network Computer) that have no local disk device. However, it's definitely a big win for developers that want to use the applet concept for application distribution, and would like to save user documents or state files from applets.

I'll be the first to admit that my first impressions of Java were not very positive. Like lots of people, I thought that Java was limited to these restricted applets. (Boy, was I missing the point!)

Then I found out how to write Java applications. Java applications (as I use the term) are Java programs that run outside of a browser. They have the same basic capabilities as a C or C++ program, but run inside a stand-alone virtual machine (VM), which makes them work with almost any operating system. (Applets run in a VM that is embedded in the browser.) A Java application may or may not have a window or GUI. If it does have a GUI, it creates its own window (or windows) and behave just like all other windows on your desktop, whether under Windows, Macintosh, or UNIX Motif.

A Java application can also, of course, run on your UNIX, NT, or other server. The great thing about these Java applications is that there are no Java-enforced security constraints! I could actually write an application that would wipe my entire hard disk, if I wanted to! (Well, I probably wouldn't want to, but hey, now there were possibilities.) What I did want to do was to write Java applications that could, for example, write to and read from the server's hard disk. With this kind of capability, I could use Java to develop simple (or not-so-simple) database servers and proxies.

By this time, I was hooked. Java was all the buzz, and I was experiencing the advantages of Java as a language and development platform daily, if not hourly. (I outlined some of these advantages at the beginning of this article; if you wan't more detail, you can find it at JavaSoft's web site.)

Java is an Excellent Choice for Both Clients and Servers

You could develop a server in any language, and it would be transparent to the client, as long as the server honored the specified protocol. However, Java has become my first choice for many reasons. One of the biggest advantages is that you can use the same or similar classes on both the client and the server side for reading and writing the "objects" related to your business problem. Even at the lowest level, Java's Data classes guarantee that the numbers and strings you send will be translated correctly even among heterogeneous platforms.

I already referred to the tremendous programming benefits of Java for applications, but the portability of a server written in Java is a strong selling point, worth a second look. My company, DTAI, developed the Interactive Org Chart as a client/server product. Our original version, developed for Netscape AppFoundry, included a very simple, hand-coded database and a tailored protocol.

We had to be able to distribute our product on several client and server platforms. That meant that we had to produce servers for both Windows NT and UNIX. Most programmers dread the thought of writing a C or C++ application that has to be ported to two or more platforms, each with completely different programming environments, and system interfaces. But with Java, we wrote each server only once and it worked perfectly on both UNIX and NT.

Even if you think you have no multiplatform server requirements today, take a moment and try to recall the type of hardware and operating system you or your company were using 5 or 10 years ago. Did it change? I would venture to say that almost 100 percent of you would have to answer "Yes." And chances are that the system you develop for in the next 5 or 10 years will be different from the one you use today.

Taking Advantage of Java's Built-in Network and Data Classes

Another of Java's strengths as a programming environment is that it is very network-aware. And, in fact, despite all of the other security restrictions on applets, an applet can open a TCP/IP socket connection, as long as it is only to the host from which the applet was downloaded.

This is an important point. When a Web page containing an applet is downloaded to a browser, that applet is not allowed to open a socket connection to the local system (where the browser is running). Nor is it allowed to connect to other systems (for example, computers inside the client's firewall). Therefore, even though it can open a connection back to the Web server computer, it will not be able to extract any potentially private data from the client to send back to the server.

Through the socket, the client can both send and receive data. This permits the client to use the server as a proxy to the server's local resources, and even to other network resources accessible by the server. Some Java developers have even implemented a virtual file system for applets. With such systems, a user can select, open, and save files from an applet, but the data is actually read from and written to the server's local file system, and the data is transferred between the client and the server through sockets.

There are several Java classes that make it easy to set up network connections, using TCP/IP, and socket I/O. Java also has a powerful I/O stream model that allows you to combine I/O stream classes together - with sockets, files, or other stream targets - to meet any of several possible configurations. It's easy to use Java to open a connection to any kind of server program on another computer and set up buffered data streams for reading and writing information between the two programs. The following code shows just how easy that is:

    Socket socket = new Socket( "some.host.com", 80 ); 

    DataInputStream instream =
        new DataInputStream(
        new BufferedInputStream(
        socket.getInputStream() ) ); 

    DataOutputStream outstream =
        new DataOutputStream(
        new BufferedOutputStream(
        socket.getOutputStream() ) ); 

The Socket class only requires two pieces of information - passed to the "constructor" function above - in order to make its connection: the target computer's host name and the TCP/IP port number. Unless the constructor throws an exception (which may happen if, for instance, the program on the other end doesn't respond), the first line in the above example will cause a solid connection to be made between this Java process and another process, possibly on another machine, anywhere on the network.

The example just shown calls only two methods on the Socket object, getInputStream() and getOutputStream(). I could use these stream objects, as is, to send data to the program on the other end of the socket. But these generic input and output streams are unbuffered. This means that every time I write even the smallest piece of data to the stream, an entire TCP/IP packet will be created and sent across the socket. They're also byte-oriented, which means I'd have to convert all of my data into pure bytes, send it across, and convert it back on the other end. Keep in mind that a byte-oriented protocol is called for in some instances, but for most of my applications, I think it's simpler to think of sending data in a stream. That is, if I have a Java object "Car" in my program, when I send it across a stream, I want to send a "Car", not just a bunch of bytes.

I can enable buffering by wrapping a BufferedInputStream or BufferedOutputStream around my simple streams. Then I can wrap the DataInputStream and DataOutputStream classes around the buffered streams. The "DataXxxStream" classes offer me an API that is much broader. Instead of requiring me to convert my integers into bytes, I can simply call outstream.writeInt() with my integer, and DataOutputStream converts that Java int into the bytes. Then on the other end, another Java program can use the DataInputStream method instream.readInt(), which returns the value as an int. Simple!

And there's something even more important here. Hidden in this elegant API is the fact that the Java library has implemented a standard method for reading and writing primitive data types so that they are consistent between the two processes. Strings are written and read using the functions writeUTF() and readUTF(). (UTF stands for Unicode Transfer Format and is a standard, compact format for transferring the Unicode strings that Java uses). And all numeric data is written in a consistent byte order (called network byte order) so that Java programs running on any type of machine can communicate according to a universally adopted low-level protocol. Therefore, using Java on both the client and the server will guarantee that these primitive data objects are converted and interpreted correctly, on both ends.

The next implementation of Java, version 1.1, includes an extremely attractive new feature for client-server implementations: Object Serialization. This feature will seamlessly encode your Java objects into a stream of bytes for transmission across the socket (or to a file), and then decode and reconstruct the objects as they are received. Object Serialization will greatly simplify Java communication patterns.

Design or Choose a Protocol

When most of us write more than the simplest of object-oriented or even structured programs, we don't just create a whole bunch of ints, floats, doubles, and strings, and toss them around. We create classes or structures and collect the relevant data in encapsulated objects. Similarly, when you pass data from one program to another, you have to design a protocol that is usually a little more sophisticated than something that says "pass a bunch of numbers to the server."

A higher-level protocol defines the standard by which the client and server will communicate. It's the "rules of engagement," including how a client introduces itself, how the server and client will make requests and or respond to one another, and how they will terminate the transaction. The great thing about the openness of the Internet is that there are a number of open standards for Internet communication. Depending on your application, you may want - or need - to adopt one of these standards.

For instance, if you were to write an email client, you would want to get familiar with protocols such as SMTP, POP3, and IMAP4. These are TCP/IP-based standards for talking to existing mail servers. (In fact, you could be looking at implementing a mail server in Java, and you would need one or more of these protocols to talk to any email client.) Some other client/server protocol examples include JDBC and LDAP. These may also be appropriate for your application, depending on your needs.

My point here is that you can choose an existing standard protocol - whether it's open, or some company or industry standard - and implement the protocol in Java.You can also design your own protocol. Now the sky's the limit. If you are designing a new client/server capability, you can decide what data is transferred and how.

An Example Client/Server Application

As an example, suppose you work for one of the "Big Three" auto manufacturers, and you are directed to implement a new thin-client strategy. Your first assignment is to develop a Java applet that would be used by hundreds or thousands of people who have input into the development of new vehicles. This applet will show the current parts inventory for one of a selection of new car designs, and it will allow the user to add parts to the inventory. It might look something like the following do-nothing applet:

When you design the protocol for the application, you might come up with a number of message types, including the following:

  • Client-to-server: "Give me the inventory, one part at a time, for car X"
  • Server-to-client: "Here is a part for car X"
  • Client-to-server: "Add, remove, or change this part for car X"

Write Methods that Support Your Protocol

Once you decide exactly what commands and data you want to transfer, and when you want to transfer the data, you can start to develop Java methods to read and write the data. Below are some sample methods that could support our parts inventory protocol. To send a new part to the server, the client applet could call writeAddPart, which writes a command requesting the server to add a part with the given data elements:

public void writeAddPart( DataOutput outstream, Part part )
  throws IOException {
    outstream.writeUTF( "Add Part" );
	 // this is the command the server will respond to
    outstream.writeUTF( part.getName() );
    outstream.writeInt( part.getQuantity() );
    outstream.writeUTF( part.getManufacturer() );
}

The next method, readFromServer, reads the next response from the server (a string that could be one of possibly many different response codes), and performs an appropriate action. In this example, only a Part response is accepted. The function then calls readPart to generate a Part object from the server, and adds it to its own internal list:

public void readFromServer( DataInput instream ) throws IOException {
    // this goes into an infinite loop, so
    // you will want to run this in a Thread
    while ( true ) {
        String command = instream.readUTF();
        if ( command.equals( "Part" ) ) {
          Part part = readPart( instream );
          partsList.addPart( part );
          // partsList is an application
          // object referenced by this class
        }
        else { // add more commands here, if any
            throw new IOException( "Unexpected command: "+command );
        }
    }
}

The next method is readPart, which simply creates a new Part object and reads the attributes from the stream. Note that this function could be used on both the client - to read the part from the server, as shown above - or on the server - to read a part that the client added. The function should probably be put in a utility library for the parts inventory application, and used by both client and server:

public Part readPart( DataInput instream ) throws IOException {
    Part part = new Part();
    part.setName( instream.readUTF() );
    part.setQuantity( instream.readInt() );
    part.setManufacturer( instream.readUTF() );
    return part;
}

Selecting the JVM Environment for Your Server

All Java programs run in what is referred to as a Java virtual machine (JVM). This is typically a single process that includes a byte-code interpreter or just-in-time (JIT) compiler, loads and executes Java classes, and manages the threads, memory allocation, and garbage collection. (Interpreters and JITs read and execute Java byte-cod, the Java equivalent of machine code, which is portable to any platform. JITs can actually compile some of the byte-code to much faster native machine code as the application loads.) Netscape Navigator includes an embedded JVM in which Java applets, downloaded across the Internet, are loaded and run.

The latest Web servers - including Netscape Enterprise Server - now include their own embedded JVMs. Instead of loading client applets over the Internet, the server loads server applets from its local file system in response to URL requests. One of the advantages to this approach is that - in the case of Netscape Enterprise Server - server applets have access to the Web server's environment and capabilities. They can also be used to respond to HTTP protocol requests from Web browsers in a flexible and dynamic way. The Netscape server Java API adds a number of convenience classes that make HTTP transactions simple and painless. (I'll devote much more detail to the topic of Netscape server applets and their capabilities in Part 2 of this article.)

When you write a Java server, one of the things you have to decide is what your Java environment will be. If your Java server is not embedded in a Web server or similar environment, then you can run your Java application using one of the JavaSoft Java interpreters or JITs, one from your system vendor, or one from a third party. These JVMs are usually distributed as part of the Java Developer's Kit (JDK) or an integrated development environment (IDE) like Symantec Café or Sun's JavaWorkshop. (Enough with the acronyms!)

Loading a Stand-alone Java Application

The stand-alone interpreters typically run under the name java (easy enough to remember!). And to run a stand-alone Java application from a DOS or UNIX shell command line, you can type java MyApp, where MyApp.class is accessible through your CLASSPATH. Without getting into a too much of a Java tutorial, stand-alone Java applications start from a "main" function (very similar to the way you'd start applications written in C and C++). When you run the interpreter on a Java class file, the interpreter scans that class file for a static function with the exact signature public static void main( String args[] ), and executes that function.

From here, I create a Server object that, when constructed, will open a Java ServerSocket. ServerSocket is a class in the standard java.net package, and it opens a specific TCP/IP port on the server to which clients can connect. The main responsibility of the server is to loop indefinitely, and call the ServerSocket accept() method, which returns (with a new Socket object) only when a new client has connected. In the Java model, as each client connects, you have one of two choices:

  1. If your client-server protocol implements client/server dialogs of very short duration, you may want to simply respond to the client at this point in your loop, then drop the client (that is, close the socket) and go back to accepting new clients.
  2. If you don't want to force your other potential clients to wait for the server to respond to another client before they are accepted, or if the client/server dialog is going to persist for an indefinite period, then you will want to create a thread for each new client. The server object should start the thread and then return in the loop to accept more clients.

A Useful Package of Java Client/Server Utilities

I've developed a small package of classes - dtai.net - that provide a generic framework for almost any client/server architecture. We have used this numerous times at DTAI as a foundation for many of our client/server applications. I've included the source code with this article. Feel free to use these classes in your own client/server implementations.

The classes include NetServer and NetPort; NetPort is subclassed into two versions, NetPortToClient (used by the server) and NetPortToServer (used by the client). These classes are meant to be subclasses that you can extend to meet your specific needs. They handle most of the thread, socket, and data streaming code, and they allow you to concentrate on the issues that are specific to your application.

As a client/server developer, you would subclass NetServer and NetPortToClient for your server-side program. For simple servers, I often do this in one .java file, with two classes. To give you an example, I've also included 45-line server program with this article, called Sample2.java.

Sample2.java doesn't really do anything useful. It simply reads a command string from the client and responds to the client by writing a new string with the value I did it!. But it's very easy to make this program more functional, simply by adding code to the readInput() function. Some aspects of the program to pay attention to are the following:

  1. The server starts listening for clients as soon as the NetServer constructor is invoked, through the call super( portnum ). NetServer is a subclass of Thread. The NetServer constructor not only opens the ServerSocket for the given port number, but it also starts a thread in which to loop and wait for incoming client connections. This way, the superclass returns, and you could, potentially, create another server, perhaps of a different class.
  2. When a client connects, NetServer calls createClientPort(). This abstract method is overridden in the sample to create the appropriate type (the programmer's subclass) of a NetPortToClient. NetPortToClient also extends Thread, and here in createClientPort() we also start this Thread. (Optionally, we could have coded this for quick transient connections, but the code I've provided is a more general approach.)
  3. In the NetPort object , the run() method (invoked when starting a Thread) loops and calls readInput(). Here is where I placed the server-side of the client/server dialog. Each time the server calls this function, the function should read the next command, or request, from the client; it should block, that is, hang on this line until something is available to read, if the client hasn't sent anything yet. When the request is received, the NetPortToClient subclass should perform the requested action, and return the appropriate response, if any. This dialog is entirely flexible and can be altered to suit any needs. (For example you could write a POP3 mail server, or the parts inventory server shown above.)
  4. Also notice that the reads and writes refer to the NetPort object itself. This useful shorthand is possible because NetPort implements both the DataInput and DataOutput interfaces, and delegates the calls to the buffered DataInput and DataOutput streams associated with the socket to the client.
  5. Finally, because we use buffered streams, we need to do a flush to ensure that the data that's been written to the client socket gets sent immediately.

The Client Side

The code for the client side is even simpler. NetPortToServer also subclasses NetPort, and it has the advantages of the DataInput and DataOutput implementations as well as being able to subclass Thread. (Again, use of the Thread and the readInput() model is optional.) The constructor for NetPortToServer takes the server's host identifier (hostname or Internet address) and the server's port number. I often use the NetPortToServer class as-is, without subclassing.

Here is a short, three-line example of how easy it is to invoke a simple client-to-server transaction:

port = new NetPortToServer( host, portnum );
port.writeUTF( "Do something for me" );
String response = port.readUTF();

Obviously, in this example, the protocol was predetermined. The server should be waiting for a command, in the form of a UTF String. It should also respond with one UTF String. If anything goes wrong, the server could send an error message back in that string or simply shut down the connection. What I haven't shown is that you will have to wrap these lines inside of a try/catch block to catch any I/O exceptions, such as if the server shuts down the connection.

Starting the Server

I've found that one of the trickier parts of client/server programming is to figure out how to start the server in the first place. During most of my programming, I just assume that the server is running. This is easy to do in the beginning, because I usually start the server myself. But when I deploy my applications, the production version has to have a way to start the server automatically. There are several ways to do this under UNIX:

  1. Start the server from an rc - UNIX run command - file. These scripts are executed at system boot time, and there is always a best place to put server programs like these. (Typically, they'd either be embedded in an existing rc file, like rc.local, or placed in a separate file in a designated rc directory.)
  2. Start the server from inetd (the Internet daemon). This is quite a bit different from our background-process approach, because inetd handles the client connections and spawns a separate process for each one. Data on the socket is routed to the process's standard input and standard output. I don't recommend this solution, particularly because inetd has to spawn a separate JVM for each process, and that consumes quite a bit of resources.
  3. Start the server as a background process from a CGI script. This is a good approach, and I use it in Interactive Org Chart. Using this approach, when clients attempt to connect to the server, if there is an exception, the server is probably not running. I create a Java URL object to the CGI program, use the URL openConnection() method, and read a few bytes to get the CGI program running. The CGI program sets up its environment to run Java, and then invokes the JVM with the server class as a UNIX background process. The server should be invoked only once, the first time it's accessed, unless the machine is rebooted or the server is otherwise abnormally terminated.

Starting the server under Windows NT is a little more difficult. Unlike UNIX, Windows NT does not treat background processes as "first-class citizens." I developed a small C program called spawn.c (which compiles to spawn.exe) to start a program in the background. It usually works. (Well, I'm not much of a Windows programmer, but even the Windows experts I know seem to get road-blocked on Windows background processes!) When it does work, it is consistent. Using this program, you can write a DOS batch file as a CGI program and spawn the server as a background process.

Another alternative for Windows NT, similar to the rc file approach, is to start the Java server as a Windows NT Service. Microsoft has a utility program that comes with its Java Software Developer's Kit (SDK) that will register a class as an NT Service. You have to create a small "wrapper" class that subclasses a Microsoft class file (which is included with the SDK). It is relatively simple to use, but I still prefer the straightforward and debuggable UNIX rc scripts. (Such is life.)

Conclusion

Java is an extremely powerful language for client/server programming. And although Java is already seen as an applicable client-side software tool, don't overlook Java for other applications, including server-side development. Java is maturing rapidly, and there are even promises that Java will perform as fast as any other modern programming language by year-end. In my opinion, Java is the best choice for most programming tasks, even today. In Part 2 of this article, I'll discuss Netscape's Server Applets, and new features in Enterprise Server 3.0, including server-side LiveConnect, that allow Java programs more control and interoperability with other parts of the Netscape Web server.



Technical reviewer for this article was Junhe Liu of Netscape Communications' developer support engineering team.



View Source wants your feedback!
Write to us and let us
know what you think of this article.


Rich Kadel is a senior software engineer and lead engineer in charge of Java development at DTAI Incorporated, a progressive technology company based in San Diego, CA. DTAI develops advanced software products and provides custom computer solutions to a number of market areas. Rich currently leads the development of DTAI's Interactive Org Chart, a 100 percent Java client/server Intranet application first developed for Netscape's AppFoundry program. During the production of Interactive Org Chart 1.0, he developed the Gadget Windowing Toolkit (GWT), a powerful, portable, robust (and now free!) replacement for Java's AWT. He also participates in the development of DTAI's JustDBC suite of JDBC (Java Database Connectivity) drivers, proxies, and servers. He was the primary developer of PAL++, a large C++ software library with almost 300 C++ classes for building graphic- and data-oriented applications.

(5.97)


Related Reading:


Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use

© 2001 Sun Microsystems, Inc.