Reputation: 2087
I would like to set an applicationScope variable as a person opens the application. The values set will not change for the duration of the current session. Could do it a s a sessionScope but have the same question how do you set it on a session open.
Upvotes: 1
Views: 890
Reputation: 8086
The "right" way to do this is to define a managed bean.
Add a Java design element to your application. Because your question doesn't indicate what business requirement you're attempting to solve, the following is a completely generic example that hopefully illustrates the pattern:
package com.example.bean;
import java.io.Serializable;
public class ExampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private Object exampleProperty;
public ExampleBean() {
}
public Object getExampleProperty() {
if (exampleProperty == null) {
/*
* This is where you would auto-initialize the property value
*/
}
return exampleProperty;
}
public void setExampleProperty(Object exampleProperty) {
this.exampleProperty = exampleProperty;
}
}
The crucial bit in the above example is the block comment within getExampleProperty()
. In your real class, you would replace that comment with whatever logic is used to determine what the property's current value is, and assign exampleProperty
to the result of that logic. That code would only run once per scope instance, because each additional time the property is accessed, its value will no longer be null, so the current value will simply be returned.
It might seem more intuitive to place the logic for auto-initializing exampleProperty
within the class constructor (public ExampleBean() {...
), but don't. There are three reasons to follow this "lazy loading" pattern. The first is that it's conceivable that the user may never trigger a need to know the value of the property. Again, I don't know why you're wanting to store a value immediately when the app is accessed, so in your specific use case, the user may always need the property value immediately. But suppose the goal is to store some application setting, but the user never does anything in the app that requires that setting to be known... you'd be retrieving the setting without ever getting any benefit from having done so. With the above pattern, you don't retrieve it until the user "asks" for it, but the instant they do, it's there, and stays until the scope expires.
The second reason is because, as your class becomes more complex over time (as you store more and more property values this way), if you place all the logic for initializing properties in the constructor, that method would become huge, and it would be difficult to immediately see which code is used for initializing which property. By placing the initialization logic for each property in its associated "getter" method, you're isolating the logic for each; the class remains as legible as possible, easing ongoing maintenance.
But perhaps the most important reason is because, as you move more and more of your application logic to beans (and all of us should), you'll identify use cases for defining viewScope
(page specific) beans. Unless you explicitly set your app settings to store all pages in memory, viewScope
beans are automatically serialized to disk whenever the app thinks they should be (hence the indication in the class declaration that the class implements Serializable
. They are loaded back into memory later on as needed. This triggers the class constructor, and then sets the property values back to what they were before. In other words, if you initialize exampleProperty
in the constructor, then the code to do so will be executed when the page loads, then again during any event executed against the page... but if its value changes at some point, it will keep re-initializing on every event, only to be overridden with whatever its value had later become. This almost completely defeats the purpose of storing the value in scope. It's also a good idea to make all beans serializable, regardless of scope, because if IBM ever adds true cluster support to XPages, then application and session beans will need to be serialized to disk as well in order to replicate them to other servers, so just make all your beans serializable now, and you won't need to worry about your apps suddenly breaking when you upgrade to a version of Domino that includes cluster support.
In summary, just remember that the "lazy loading" pattern is "better" for beans. :)
Once you've defined your Java class, "register" it within faces-config.xml
. This file is stored in WebContent/WEB-INF
, which you can find via Package Explorer, but in Domino 9 it is also surfaced as a design element under the "Application Configuration" category.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<managed-bean>
<managed-bean-name>exampleBean</managed-bean-name>
<managed-bean-class>com.example.bean.ExampleBean</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<!--AUTOGEN-START-BUILDER: Automatically generated by IBM Domino Designer. Do not modify.-->
<!--AUTOGEN-END-BUILDER: End of automatically generated section-->
</faces-config>
The example class I included above is named ExampleBean
, and I gave it a package name of com.example.bean
, so the "canonical" name of the class is com.example.bean.ExampleBean
, which you can see in the above XML. The name is the variable that you want to refer to from anywhere in your app. The scope section of the XML defines which of the four scopes you want your bean to reside in. What's particularly convenient about this is that, if you decide later that this is a better fit for storing in sessionScope
, then you can just go back into faces-config
and change application
to session
... and immediately all code that is referencing it will now be accessing a sessionScope
variable instead of an applicationScope
variable. You don't have to change any of your XPage code at all.
Speaking of which, here's how you'd actually reference this now that it's defined as a managed bean:
From anywhere within SSJS, you can call the public methods of the bean by treating the bean name as an object:
return exampleBean.getExampleProperty();
...or...
exampleBean.setExampleProperty(someNewValue);
Treat the bean like a data source. You can bind any component
property (i.e. value
, title
, rendered
, etc.) directly to any
property of the bean:
#{exampleBean.exampleProperty}
This causes an automatic evaluation of whether the property is being accessed in a "read" context or a "write" context. If what the user is currently doing is causing Domino to "ask" what the property value is, then that's a read. As a result, it will automatically know to call the "getter" method (in this case, getExampleProperty()
). If, however, you bind an editable component property (i.e. the value
attribute of an edit box, radio button, etc.) to a bean property, and the user is now posting data to that component, Domino automatically knows to call the "setter" (setExampleProperty()
) instead, and pass it the new value.
So if this is an application setting, you could include a settings page that only process owners have access to, and make this property editable to them. In the setter method, in addition to just updating the internal property value, you could write the new value back to wherever you got it from (i.e. a configuration document). The new value is now accessible to all users without having to retrieve it from disk again, but the new value is also stored, so if the bean ever falls out of scope, the updated value is automatically retrieved again the next time the bean is loaded.
If, instead, it's a user setting (i.e. stored in sessionScope
), then you could, similarly, access the property from anywhere in the app, but also give them a way to modify the setting. As with an application setting, the current value would always be in memory, but if you write it back to disk whenever it's modified, then the value in memory will always be current, but when that scope expires, the value can be retrieved (once) again when needed.
All of this behavior is technically also possible just via SSJS. But, as I mentioned at the beginning of this answer, the most correct way to store data in scope is via managed beans.
Upvotes: 8