ServletContext
When the servlet container (like Apache Tomcat) starts up, it will deploy and load all its web applications. When a web application is loaded, the servlet container creates the ServletContext
once and keeps it in the server's memory. The web app's web.xml
and all of included web-fragment.xml
files is parsed, and each <servlet>
, <filter>
and <listener>
found (or each class annotated with @WebServlet
, @WebFilter
and @WebListener
respectively) is instantiated once and kept in the server's memory as well. For each instantiated filter, its init()
method is invoked with a new FilterConfig
.
When a Servlet
has a <servlet><load-on-startup>
or @WebServlet(loadOnStartup)
value greater than 0
, then its init()
method is also invoked during startup with a new ServletConfig
. Those servlets are initialized in the same order specified by that value (1
is 1st, 2
is 2nd, etc). If the same value is specified for more than one servlet, then each of those servlets is loaded in the same order as they appear in the web.xml
, web-fragment.xml
, or @WebServlet
classloading. In the event the "load-on-startup" value is absent, the init()
method will be invoked whenever the HTTP request hits that servlet for the very first time.
When the servlet container is finished with all of the above described initialization steps, then the ServletContextListener#contextInitialized()
will be invoked.
When the servlet container shuts down, it unloads all web applications, invokes the destroy()
method of all its initialized servlets and filters, and all ServletContext
, Servlet
, Filter
and Listener
instances are trashed. Finally the ServletContextListener#contextDestroyed()
will be invoked.
HttpServletRequest
and HttpServletResponse
The servlet container is attached to a web server that listens for HTTP requests on a certain port number (port 8080 is usually used during development and port 80 in production). When a client (e.g. user with a web browser, or programmatically using URLConnection
) sends an HTTP request, the servlet container creates new HttpServletRequest
and HttpServletResponse
objects and passes them through any defined Filter
in the chain and, eventually, the Servlet
instance.
In the case of filters, the doFilter()
method is invoked. When the servlet container's code calls chain.doFilter(request, response)
, the request and response continue on to the next filter, or hit the servlet if there are no remaining filters.
In the case of servlets, the service()
method is invoked. By default, this method determines which one of the doXxx()
methods to invoke based off of request.getMethod()
. If the determined method is absent from the servlet, then an HTTP 405 error is returned in the response.
The request object provides access to all of the information about the HTTP request, such as its URL, headers, query string and body. The response object provides the ability to control and send the HTTP response the way you want by, for instance, allowing you to set the headers and the body (usually with generated HTML content from a JSP file). When the HTTP response is committed and finished, both the request and response objects are recycled and made available for reuse.
HttpSession
When a client visits the webapp for the first time and/or the HttpSession
is obtained for the first time via request.getSession()
, the servlet container creates a new HttpSession
object, generates a long and unique ID (which you can get by session.getId()
), and stores it in the server's memory. The servlet container also sets a Cookie
in the Set-Cookie
header of the HTTP response with JSESSIONID
as its name and the unique session ID as its value.
As per the HTTP cookie specification (a contract any decent web browser and web server must adhere to), the client (the web browser) is required to send this cookie back in subsequent requests in the Cookie
header for as long as the cookie is valid (i.e. the unique ID must refer to an unexpired session and the domain and path are correct). Using your browser's built-in HTTP traffic monitor, you can verify that the cookie is valid (press F12 in Chrome / Firefox 23+ / IE9+, and check the Net/Network tab). The servlet container will check the Cookie
header of every incoming HTTP request for the presence of the cookie with the name JSESSIONID
and use its value (the session ID) to get the associated HttpSession
from server's memory.
The HttpSession
stays alive until it has been idle (i.e. not used in a request) for more than the timeout value specified in <session-timeout>
, a setting in web.xml
. The timeout value defaults to 30 minutes. So, when the client doesn't visit the web app for longer than the time specified, the servlet container trashes the session. Every subsequent request, even with the cookie specified, will not have access to the same session anymore; the servlet container will create a new session.
On the client side, the session cookie stays alive for as long as the browser instance is running. So, if the client closes the browser instance (all tabs/windows), then the session is trashed on the client's side. In a new browser instance, the cookie associated with the session wouldn't exist, so it would no longer be sent. This causes an entirely new HttpSession
to be created, with an entirely new session cookie being used.
In a nutshell
- The
ServletContext
lives for as long as the web app lives. It is shared among all requests in all sessions.
- The
HttpSession
lives for as long as the client is interacting with the web app with the same browser instance, and the session hasn't timed out at the server side. It is shared among all requests in the same session.
- The
HttpServletRequest
and HttpServletResponse
live from the time the servlet receives an HTTP request from the client, until the complete response (the web page) has arrived. It is not shared elsewhere.
- All
Servlet
, Filter
and Listener
instances live as long as the web app lives. They are shared among all requests in all sessions.
- Any
attribute
that is defined in ServletContext
, HttpServletRequest
and HttpSession
will live as long as the object in question lives. The object itself represents the "scope" in bean management frameworks such as JSF, CDI, Spring, etc. Those frameworks store their scoped beans as an attribute
of its closest matching scope.
Thread Safety
That said, your major concern is possibly thread safety. You should now know that servlets and filters are shared among all requests. That's the nice thing about Java, it's multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise be too expensive to recreate, init()
and destroy()
them for every single request.
You should also realize that you should never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's not thread-safe! The below example illustrates this:
public class ExampleServlet extends HttpServlet {
private Object thisIsNOTThreadSafe;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object thisIsThreadSafe;
thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}
}
See also:
Using MVC
Most of this answer focuses on a direct call to pass a parameter from a calling class to the controller.
If instead, you want to decouple the caller and controller and use a more general architecture involving a model class with settable and listenable properties to achieve inter-controller communication, see the following basic overview:
Recommended Approach
This answer enumerates different mechanisms for passing parameters to FXML controllers.
For small applications I highly recommend passing parameters directly from the caller to the controller - it's simple, straightforward and requires no extra frameworks.
For larger, more complicated applications, it would be worthwhile investigating if you want to use Dependency Injection or Event Bus mechanisms within your application.
Passing Parameters Directly From the Caller to the Controller
Pass custom data to an FXML controller by retrieving the controller from the FXML loader instance and calling a method on the controller to initialize it with the required data values.
Something like the following code:
public Stage showCustomerDialog(Customer customer) {
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
"customerDialog.fxml"
)
);
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(
new Scene(loader.load())
);
CustomerDialogController controller = loader.getController();
controller.initData(customer);
stage.show();
return stage;
}
...
class CustomerDialogController {
@FXML private Label customerName;
void initialize() {}
void initData(Customer customer) {
customerName.setText(customer.getName());
}
}
A new FXMLLoader is constructed as shown in the sample code i.e. new FXMLLoader(location)
. The location is a URL and you can generate such a URL from an FXML resource by:
new FXMLLoader(getClass().getResource("sample.fxml"));
Be careful NOT to use a static load function on the FXMLLoader, or you will not be able to get your controller from your loader instance.
FXMLLoader instances themselves never know anything about domain objects. You do not directly pass application specific domain objects into the FXMLLoader constructor, instead you:
- Construct an FXMLLoader based upon fxml markup at a specified location
- Get a controller from the FXMLLoader instance.
- Invoke methods on the retrieved controller to provide the controller with references to the domain objects.
This blog (by another writer) provides an alternate, but similar, example.
Setting a Controller on the FXMLLoader
CustomerDialogController dialogController =
new CustomerDialogController(param1, param2);
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
"customerDialog.fxml"
)
);
loader.setController(dialogController);
Pane mainPane = loader.load();
You can construct a new controller in code, passing any parameters you want from your caller into the controller constructor. Once you have constructed a controller, you can set it on an FXMLLoader instance before you invoke the load()
instance method.
To set a controller on a loader (in JavaFX 2.x) you CANNOT also define a fx:controller
attribute in your fxml file.
Due to the limitation on the fx:controller
definition in FXML, I personally prefer getting the controller from the FXMLLoader rather than setting the controller into the FXMLLoader.
Having the Controller Retrieve Parameters from an External Static Method
This method is exemplified by Sergey's answer to Javafx 2.0 How-to Application.getParameters() in a Controller.java file.
Use Dependency Injection
FXMLLoader supports dependency injection systems like Guice, Spring or Java EE CDI by allowing you to set a custom controller factory on the FXMLLoader. This provides a callback that you can use to create the controller instance with dependent values injected by the respective dependency injection system.
An example of JavaFX application and controller dependency injection with Spring is provided in the answer to:
A really nice, clean dependency injection approach is exemplified by the afterburner.fx framework with a sample air-hacks application that uses it. afterburner.fx relies on JEE6 javax.inject to perform the dependency injection.
Use an Event Bus
Greg Brown, the original FXML specification creator and implementor, often suggests considering use of an event bus, such as the Guava EventBus, for communication between FXML instantiated controllers and other application logic.
The EventBus is a simple but powerful publish/subscribe API with annotations that allows POJOs to communicate with each other anywhere in a JVM without having to refer to each other.
Follow-up Q&A
on first method, why do you return Stage? The method can be void as well because you already giving the command show(); just before return stage;. How do you plan usage by returning the Stage
It is a functional solution to a problem. A stage is returned from the showCustomerDialog
function so that a reference to it can be stored by an external class which may wish to do something, such as hide the stage based on a button click in the main window, at a later time. An alternate, object-oriented solution could encapsulate the functionality and stage reference inside a CustomerDialog object or have a CustomerDialog extend Stage. A full example for an object-oriented interface to a custom dialog encapsulating FXML, controller and model data is beyond the scope of this answer, but may make a worthwhile blog post for anybody inclined to create one.
Additional information supplied by StackOverflow user named @dzim
Example for Spring Boot Dependency Injection
The question of how to do it "The Spring Boot Way", there was a discussion about JavaFX 2, which I anserwered in the attached permalink.
The approach is still valid and tested in March 2016, on Spring Boot v1.3.3.RELEASE:
https://stackoverflow.com/a/36310391/1281217
Sometimes, you might want to pass results back to the caller, in which case you can check out the answer to the related question:
Best Answer
You can only show one scene in a
Stage
, but you can create multiple stages. If you want to use fxml for your secondary window, you should get your hands on the controller instance and design the controller in a way that allows you to access the user's input. You can useStage.showAndWait
to "wait for the user to complete the input".Example
Application start method
Note that here it's just a button that opens the new window, but you could use similar logic in the
onAction
event handler of a menu item. (You need to usesomeNode.getScene().getWindow()
to get access to the parent window forStage.initOwner
in this case;someNode
is a arbitraryNode
in the parent window; you could get the node from the event (((Node)event.getTarget())
) or use a node that you know is in the scene; inInputController.submit
mealField
is used for this purpose)controller
fxml