Getting Started with Induction
Welcome to Induction! Induction is a powerful, high-performance framework for developing web applications using the Java™ programming language. Induction belongs to the class of web frameworks usually referred to as request-based frameworks. Induction deploys as a servlet into a Java™ servlet container. Induction promotes the Model-View-Controller (MVC) approach to web application development. Some of the key design goals of Induction are:
- eliminate the need for web application reloads to test changes to models, views and controllers (we believe that a simple page refresh should suffice)
- bindings between controllers, views and models in declared in such way that they can be analyzed using any IDE
- enable tracking of the data used in user interface templates (typically HTML templates)
- file upload handling should be simple to the point of being unremarkable
- make it simple to use arrays and dictionary types in an HTML form
- use XML wisely (Induction requires only one XML file per application)
- an extensible architecture to support a range of usage patterns
- high performance
- compatibility with JDK versions 1.4 through 1.6 (both inclusive)
About this tutorial
The goal of this tutorial is to get you running quickly using Induction. Induction requires a servlet container, in this tutorial we will use the Apache Tomcat servlet container, but you are free to use any standards compliant servlet container.
Installing
We will assume that we have a Tomcat 6 installation in c:/dev/Tomcat6. Induction does not require any installation steps. The jar files for Induction can simply be included in your web application.
Writing our first application - Hello World
In this tutorial we will write a simple "Hello World" application. This application will allow us to illustrate many important concepts in Induction.
Writing a controller
We will start our first tutorial by writing a controller since controllers are a typical entry point into a web application. Let's start by writing a controller that simply prints a string to the browser.
package demoapp.helloworld1_app;
import com.acciente.induction.controller.Controller;
import com.acciente.induction.controller.Response;
import java.io.IOException;
/**
* A very simple controller that does the customary "Hello World"
*/
public class HelloWorldController implements Controller
{
public void handler( Response oReponse ) throws IOException
{
oReponse.setContentType( "text/plain" );
oReponse.out().println( "Hello World, using a simple println()" );
}
}
Before we run this controller let's take a closer look. First we note that the HelloWorldController class implements the Induction Controller interface. The Controller interface is simply a marker interface and as such does not enforce any methods on a class that implements it. When Induction receives a request to activate a controller implementation, such as our HelloWorldController class, Induction first verifies that the class implements the Controller interface. So currently the only reason for the Controller interface is security (it is under consideration to make this requirement to implement the Controller interface configurable). Since Controller is a marker interface our handler method is not bound to an interface defined signature.
Next, note that the handler method has a Response input parameter. The Response class extends the javax.servlet.http.HttpServletResponse interface and is simply a facade to the servlet container's response object. The value for the Response parameter is injected into the handler method by Induction. The values for the rest of the parameters declared in our handler method are also automatically injected by Induction since the parameters are from the following supported types:
Type | Description |
Request | facade to the servlet request, extends javax.servlet.http.HttpServletRequest |
Response | facade to the servlet response, extends javax.servlet.http.HttpServletResponse |
Form | provides access to the HTML form (if any) submitted with this request |
<model_class_name> | instance of a developer provided model class, the instance is managed to conform to a user specified lifecycle (also referred to as scope) |
ControllerResolver.Resolution | provides access to the controller resolution object (useful for writing generic parameterized controllers) |
For brevity the class names above are not fully qualified, the Request, Response and Form classes are in the com.acciente.induction.controller package. For a comprehensive list of the types available to a controller see the param injection quick reference.
Deploying the application
Before we can run our controller it needs to be compiled. We can use javac or any IDE to do this. Next we need to tell Tomcat about our web application. To do this we will create a .war file and deploy it to Tomcat by copying it to c:/dev/Tomcat6/webapps.
Our .war file will contain the following files in the /WEB-INF folder:
web.xml
induction-demoapp.xml
The following Induction .jar files in the /WEB-INF/lib folder:
acciente-induction-1.x.xb.jar
acciente-commons-1.x.xb.jar
And finally the following supporting .jar files also in the /WEB-INF/lib:
apache-bcel-5.2.jar
apache-commons-collections-3.2.1.jar
apache-commons-digester-1.8.jar
apache-commons-fileupload-1.2.1.jar
apache-commons-io-1.4.jar
apache-commons-logging-1.1.1.jar
apache-freemarker-2.3.12.jar
The respective .jar files are from the following projects: The Apache Commons libraries and the Apache BCEL library are from the Apache Software Foundation. The Freemarker library is from the Visigoth Software Society.
Note that the code for our application classes (in this case our single controller class) is not stored inside the .war file, instead we are going to point to the location of the compiled application code in the Induction application config file induction-demoapp.xml. Whenever we recompile an application class, on the next access of that class Induction will detect that the class file changed and will "hot" reload the class into the JVM, therefore there is no need to recreate/redeploy the .war when we change/compile the application classes.
Induction also supports putting the application classes inside the .war file in /WEB-INF/classes or as a .jar in /WEB-INF/lib or any other location in the classpath that Tomcat sees. Storing the application classes outside the .war file is recommended during development to improve productivity by leveraging the "hot" class reloading. The application classes can then be repackage into the .war file, if desired, for final delivery. Next let's take a look at the contents of the .war file.
Let's start with a look at the web.xml file. All we need to do in the web.xml is to define the Induction dispatcher servlet as the handler for the requests to our application:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<description>DemoApp</description>
<servlet>
<servlet-name>demoapp</servlet-name>
<servlet-class>com.acciente.induction.dispatcher.HttpDispatcher</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demoapp</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Now let's look at the contents of induction-demoapp.xml, assuming that our compiled application classes are stored in c:/project/demoapp/classes, the contents of induction-demoapp.xml would be as shown below:
<!-- this file contains the configuration for the Induction dispatcher -->
<config>
<!-- The following section allows you to specify locations from which
Induction will automatically reload a class if the file has changed -->
<java-class-path>
<compiled-directory>
<directory>c:/project/demoapp/classes</directory>
</compiled-directory>
</java-class-path>
<controller-mapping>
<url-to-class-map>
<url-pattern>/(\w+)(?:\.(\w+))?\.action</url-pattern>
<class-packages>demoapp</class-packages>
<class-pattern>(?:.*\.)?(\w*)Controller</class-pattern>
</url-to-class-map>
<default-handler-method>handler</default-handler-method>
<ignore-handler-method-case>true</ignore-handler-method-case>
</controller-mapping>
</config>
TIP: if you find that the classes you configured in the <java-class-path> are not reloading, the most likely cause is that your servlet container's standard class loader is loading the classes instead of the Induction classloader.
You must ensure that any class files that you want Induction to dynamically reload are not accessible to any other classloaders used by your servlet container. For example our Tomcat6 classloader should not have access to the classes in directory c:/project/demoapp/classes via any classpath. Be especially careful when you run your servlet container from within an IDE since your IDE may put your class files in the classpath for the servlet container.
This is because Induction uses a well-behaved classloader, and as such it gives its parent classloader the first opportunity to load a class, if the parent classloader is unable to load the the class then Induction's reloading classloader attempts to load the class from the location(s) specified under<java-class-path> section of the Induction configuration.
After creating the .war as described above, deploy the .war file to Tomcat. Now it's time to run our application.
Running the application
Assuming your Tomcat is running on localhost:8080, and the war file file you deployed was named induction-demo.war, pointing your browser to the URL:
http://localhost:8080/induction-demo/helloworld1.action
should cause the following string to output to the browser:
Hello World, using a simple println()
Controlling the URL resolution
Now let's take a closer look at the URL we used to run our controller:
http://localhost:8080/induction-demo/helloworld1.action. The first part http://localhost:8080/induction-demo is dictated by Tomcat and points to the dispatcher servlet, so this part of the URL is not Induction related. The part of the URL processed by Induction is /helloworld1.action.
In Induction the above URL path is resolved to a fully qualified controller class name and method name. The resolution mapping from a URL to a controller class name and method name is controlled by <controller-mapping> section of the Induction configuration. For more on how controller resolvers work see the Using Resolvers tutorial.
Dynamic reloading
To have some fun, go ahead and change the string printed out by the controller, now compile your change and just refresh the browser. You should see your changes take effect instantly, no more of the "create war file > redeploy > wait" cycle!
Conclusion
This concludes our first tutorial. We discussed several key concepts in this tutorial. Next the Views Tutorial introduces how to use views in Induction. Models are introduced subsequently in the Models Tutorial