I am trying to create an UI component that allow the user to customize the timeout of sessions. So I created a servlet as below:
public class SessionTimeoutServlet extends AbstractBaseServlet {
private static final long serialVersionUID = 2567293974465204729L;
public static final String REQUEST_TIMEOUT_PARAMETR_NAME = "timeout";
private static final String TIMEOUT_TYPE_INIT_PARAMETER_NAME = "timeoutType";
private static final String WEB_TYPE_TIMEOUT = "web";
private static final String WEBSERVICE_TYPE_TIMEOUT = "webService";
#EJB(mappedName = SessionSettingsRemote.BEAN_NAME)
private SessionSettingsRemote sessionSettingsBean;
#PostConstruct
public void initTimeout() {
try {
String timeoutType = getServletContext().getInitParameter(TIMEOUT_TYPE_INIT_PARAMETER_NAME);
if (WEBSERVICE_TYPE_TIMEOUT.equals(timeoutType)) {
setCustomTimeout(sessionSettingsBean.getSessionSettingsDTO().getWebServiceSessionTimeoutInterval());
} else if (WEB_TYPE_TIMEOUT.equals(timeoutType)) {
setCustomTimeout(sessionSettingsBean.getSessionSettingsDTO().getWebSessionTimeoutInterval());
} else {
setCustomTimeout(30);
}
} catch (ApplicationException e) {
setCustomTimeout(30);
}
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int timeout = Integer.parseInt(request.getParameter(REQUEST_TIMEOUT_PARAMETR_NAME));
setCustomTimeout(timeout);
}
public static void setCustomTimeout(int customTimeout) {
SessionManagerListener.setCustomTimeout(customTimeout);
}
}
However, when I deploy this on GlassFish, I get below exception.
Caused by: java.lang.IllegalStateException: ServletConfig has not been initialized
at javax.servlet.GenericServlet.getServletContext(GenericServlet.java:199)
at com.accedian.ems.uiapplication.server.servlets.SessionTimeoutServlet.initTimeout(SessionTimeoutServlet.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl$3.run(InjectionManagerImpl.java:766)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.invokeLifecycleMethod(InjectionManagerImpl.java:760)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.inject(InjectionManagerImpl.java:531)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.injectInstance(InjectionManagerImpl.java:141)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.injectInstance(InjectionManagerImpl.java:127)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.createManagedObject(InjectionManagerImpl.java:347)
at com.sun.enterprise.web.WebContainer.createServletInstance(WebContainer.java:991)
at com.sun.enterprise.web.WebModule.createServletInstance(WebModule.java:2130)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1404)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1381)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5732)
I have used getServletConfig() before getServletContext() but it was null. So why is my config and context are not properly set ?
Servlets predate managed beans and annotations and all the fancy stuff you see since Java EE 6. Previously, you have to explicitly implement a predefined abstract/template method in order to perform a task at a certain moment in the lifecycle.
For servlets, in order to hook on its initialization the same way you'd use #PostConstruct on a "normal" managed bean, you have to override the predefined GenericServlet#init() method.
#Override
public void init() {
// ...
}
The getServletContext() will be available there.
If you pay attention to the GenericServlet javadoc, you'll notice that there's also an init(ServletConfig). It's however strongly recommended to not use that method, but use init() instead. The default implementation of init(ServletConfig) namely takes care that the ServletContext is properly set. You'd have to mind calling super.init(config) to not make the same mistake. As a historical note, see that the canonical name of a #PostConstruct method as you see on managed beans, "init", is inherited from exactly this Servlet API.
In case you wonders, the #PreDestroy equivalent is the GenericServlet#destroy() method.
#Override
public void destroy() {
// ...
}
Related
I'm getting InvocationTargetException and NullPointerException when attempting to bind to custom class run by Task. I have working examples of binding to library classes ObeservableList, Long, Integer etc but now need to bind to values of custom class. I created TaskOutput class that includes StringProperty for binding purposes as follows:
public class TaskOutput {
private final StringProperty textValue = new SimpleStringProperty();
public TaskOutput(String textValue) {
this.textValue.set(textValue);
}
public String getTextValue() {
return textValue.get();
}
public void setTextValue(String textValue) {
this.textValue.set(textValue);
}
public final StringProperty nameProperty() {
return this.textValue;
}
}
This was tested successfully as follows:
TaskOutput newTaskOutput = new TaskOutput("Text of TaskOutput");
value.textProperty().bind(newTaskOutput.nameProperty());
System.out.println(value.getText());
Now I'm attempting to run a Task that provides a TaskOutput as its Value Property. The class extending Task is as follows:
public class NameGeneratorTask extends Task<TaskOutput> {
private int counter;
TaskOutput taskOutput;
public NameGeneratorTask() {
this.counter = 10;
taskOutput = new TaskOutput("Test String from output");
}
#Override
protected TaskOutput call() {
this.updateTitle("Name Generator");
do {
if (this.isCancelled())
{
break;
}
updateValue(taskOutput);
counter--;
}
while (counter > 0);
return taskOutput;
}
}
The Application class instantiates a Task Object and then passes it to the WorkerState class as follows:
public class FxConcurrentBespokeObjectVersion2 extends Application
{
NameGeneratorTask task;
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(final Stage stage)
{
task = new NameGeneratorTask();
WorkerState pane = new WorkerState(task);
}
}
The WorkerState class attempts to bind to the nameProperty of the TaskOutput being run by the Task as follows:
public class WorkerState
{
private final TextArea value = new TextArea("");
public WorkerState(Worker<TaskOutput> worker)
{
value.textProperty().bind(worker.valueProperty().get().nameProperty());
}
}
Program compiles at this point but this exception is generated at runtime:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:830)
Caused by: java.lang.NullPointerException
at OriginalExBespokeObjectVersion2.WorkerState.<init>(WorkerState.java:21)
at OriginalExBespokeObjectVersion2.FxConcurrentBespokeObjectVersion2.start(FxConcurrentBespokeObjectVersion2.java:29)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
Exception running application OriginalExBespokeObjectVersion2.FxConcurrentBespokeObjectVersion2
As I mention, I've already managed to bind to library classes so don't see why I can't bind to my custom class in same scenario (though I can bind to it fine in standalone example above). Can anyone suggest please?
In case anyone wonders, 'Why not use Task (String) ?'... The objective of my program is to return several properties from the same Task (String was just 1 example), hence needing to encapsulate the properties in a custom class.
Yes, both worker.valueProperty() and worker.valueProperty().get() return null at this point. Earlier I tried this:
value.textProperty().bind(
new When(worker.valueProperty().isNull()).then("Unknown")
.otherwise(worker.valueProperty().get().nameProperty()));
...but the outcome was the same. It get that being null is a problem but I can't see how it's different to before when I managed to bind fine with a library type such as Long. In that case this line worked fine:
value.textProperty().bind(worker.valueProperty().asString());
I checked and in the case of property type being Long worker.valueProperty() was also null at time of bind and it wasn't an issue.
Update:
I checked the worker.valueProperty().asString() used in the working example and saw it was returning StringBinding so I modified my custom class to do the same as follows:
public class TaskOutput {
private final String textValue = new String("Start value");
public TaskOutput() {
}
public StringBinding getStringBinding() {
return new StringBinding() {
#Override
protected String computeValue() {
return textValue;
}
};
}
}
I've deployed this in WorkerState class as follows:
value.textProperty().bind(worker.valueProperty().get().getStringBinding());
But still it's the same result, InvocationTargetException and Runtime Exception. Not sure what else I can try?
Currently I am trying to get my script to run on a tomcat server by using the basic web container guidelines for spring-batch-boot from the documentation https://docs.spring.io/spring-batch/reference/html/configureJob.html
The script was working correctly as a jar file before modifications to the main class but when I try converting it to a servlet I am having issues with my #PostConstruct starting only on server startup. This code sets application.properties to spring.batch.job.enabled=false and has a controller of
#Controller
public class JobLauncherController {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job job;
#RequestMapping("/jobLauncher.html")
public void handle() throws Exception{
jobLauncher.run(job, new JobParameters());
}
With The main Application to start the servlet for tomcat as
#SpringBootApplication
#EnableBatchProcessing
public class BatchApplication extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BatchApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
The problem is that my job uses custom item readers and writers that initializes it before running it using #PostConstruct. It runs the #PostConstruct at server startup which is what helps initialize the beans for writing.
My item readers/writers look like this
public class CustomReader extends ItemStreamSupport implements ItemReader<Acct>, ResourceAwareItemReaderItemStream<Acct> {
//basic autowiring
private int nextAcctIndex;
private List<Acct> acctsList = new ArrayList();
#PostConstruct
private void initialize() throws IOException {
//logic to parse files
acctsList = Collections.unmodifiableList(acctsList);
nextAcctIndex = 0;
}
#Override
public Acct read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
// System.out.println("Start Read");
Acct nextAcct = null;
if (nextAcctIndex < acctsList.size()) {
nextAcct = acctsList.get(nextAcctIndex);
nextAcctIndex++;
//System.out.println(nextAcct);
}
The BatchConfiguration calls everything like most examples as
#Bean public
IteamReader<Acct> CustomReader(){ return new CustomReader();}
My question is am I going about this the wrong way or is there a way to make it so the #PostConstruct is able to be called only when the Controller request for it?
you need to use
#BeforeStep
public void beforeStep(StepExecution stepExecution) {
init();
}
#PostConstruct is used to initialize once after applicationContext is loaded.
In your case you want to run this initialization every time job is running (you don't want data to be leaked across different jobs, right?)
Environment:
WildFly 9.0.1/9.0.2
Java EE 7 Full profile
Weld CDI environment.
Because of the number of requests I am expecting, I want to implement async request on top of the FacesServlet, and did this:
public class AsyncFacesServlet extends HttpServlet {
private static final long serialVersionUID = 111966573758921845L;
private FacesServlet delegate;
#Inject
private BeanManager beanManager;
#Inject
private ServletContext servletContext;
#Override
public void init(final ServletConfig servletConfig) throws ServletException {
delegate = new FacesServlet();
delegate.init(servletConfig);
}
#Override
public void destroy() {
delegate.destroy();
}
#Override
public ServletConfig getServletConfig() {
return delegate.getServletConfig();
}
#Override
public String getServletInfo() {
return delegate.getServletInfo();
}
#Override
public void service(final ServletRequest request, final ServletResponse response) throws ServletException, IOException {
final AsyncContext asyncContext = request.isAsyncStarted()
? request.getAsyncContext() : request.startAsync(request, response);
final Runnable runnable = () -> {
try {
delegate.service(request, response);
} catch (final IOException | ServletException ex) {
throw Throwables.propagate(ex);
}
};
asyncContext.start(runnable);
}
}
And then updated my web.xml as:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>com.mycompany.service.faces.AsyncFacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
When I deploy, I get the following exceptions, which indicates that Weld was not correctly initialized inside the asynccontext.
SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (default task-38) Error Rendering View[/login.xhtml]: org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:708)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:95)
at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.get(ContextualInstanceStrategy.java:178)
at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:761)
at org.jboss.weld.el.AbstractWeldELResolver.lookup(AbstractWeldELResolver.java:107)
at org.jboss.weld.el.AbstractWeldELResolver.getValue(AbstractWeldELResolver.java:90)
at org.jboss.as.jsf.injection.weld.ForwardingELResolver.getValue(ForwardingELResolver.java:46)
at javax.el.CompositeELResolver.getValue(CompositeELResolver.java:188)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
at com.sun.el.parser.AstIdentifier.getValue(AstIdentifier.java:116)
at com.sun.el.parser.AstValue.getBase(AstValue.java:151)
at com.sun.el.parser.AstValue.getValue(AstValue.java:200)
at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:226)
at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182)
at javax.faces.component.UIOutput.getValue(UIOutput.java:174)
at javax.faces.component.UIInput.getValue(UIInput.java:291)
at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:205)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:355)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:164)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:920)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1863)
at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:890)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:458)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:134)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:659)
at com.mycompany.service.faces.servlet.AsyncFacesServlet.lambda$service$17(AsyncFacesServlet.java:61)
Whats happening is that, Weld is not properly initialized or context is not propagated to the async context execution.
How can I be able to propagate or recreate the weld context upon async-request call?
I'll prefix my answer with a note that there is an open CDI spec issue to make async behavior more portable, Specify that web scoped (request, session, application) beans are injectable in async servlets
What you can do, in a Weld specific way, is start the context. Apache DeltaSpike has some utilities that will make this container in-specific as well, http://deltaspike.apache.org/documentation/container-control.html#ContextControlUsage
If your runnable is a managed bean, you can use BoundRequestContext in weld to start a request context for that thread. More here. The downside is that its a new context, not a bridged context.
How does a web container know that a particular servlet hasn't been initialized. Is there any particular flag which shows the status of the servlet or anything else managed by the servlet
Container initial servlet during start up ( or restart ) one by one as mentioned in web.xml. You can also find if a servelt is initiated or destroyed by override init and destroy methods.
public class PrecompressedResourceServlet extends HttpServlet {
private static final Logger LOG = LoggerFactory
.getLogger(PrecompressedResourceServlet.class);
#Override
public void init() throws ServletException {
LOG.info("The PrecompressedResourceServlet initialized...");
super.init();
}
}
I wish to set up a few application wide variables with servletContext.setAttributes on servlet context initialization phase .How can I achieve this.
Implement javax.servlet.SevletContextListener which gets a callback when javax.servlet.ServletContext is initialized.
Here is the example:
public class MyServletContextListener implements ServletContextListener
{
public void contextInitialized(ServletContextEvent sce)
{
ServletContext sc = sce.getServletContext();
//do your initialization here.
sc.setAttribute(.....);
}
public void contextDestroyed(ServletContextEvent sce)
{
ServletContext sc = sce.getServletContext();
//do your cleanup here
}
}
If you would like to tie your logic closer to the servlet (and not use a listener), you can override the servlets init method. Like so:
#Override
public void init() throws ServletException {
ServletContext sc = getServletContext();
// Store our attribute(s)!
// Check first to make sure it hasn't already been set by another Servlet instance.
if (sc.getAttribute("key") == null)
sc.setAttribute("key", "value");
}
And you don't have to call through to super.init(config). See docs.