14 Replies Latest reply on Jul 29, 2011 5:14 AM by rushead

    Load on startup is ignored - JaxWS webservice is always initialized first

    rushead

      I am trying to port my war file from JBoss AS 5.1 to JBoss AS 7.

      To provide a correct resource management I have to initialize my specific environment when the war file is deployed, before the Servlets are instantiated.

      I already tried 2 ways:

      1. Implementation of the environment initialization in a ServletContextListener
        • The #contextInitialized is called when all Servlet are instantiated --> little too late...
      2. Implementation of the environment initialization in another Servlet (in #init), use "<load-on-startup>"
        • The implemented servlet is always initialized after the normal Servlet (<load-on-startup> is ignored?!)
        • The only difference between the two servlets is that the StartupServlet is implementing Servlet the "old" way and the real Servlet is using JaxWS (@WebService)

       

      And now my questions:

      • Why is the ServletContextListener called when all Servlets are instantiated?
      • Why is "<load-on-startup>" ignored when the implementation differ only in the WS implementation?
      • Last but not least: What is the best and usual way to initialize a custom environment for several servlets in a war file?

       

      I also added an example-project for reproduction.

        • 1. Re: Load on startup is ignored - JaxWS webservice is always initialized first
          jim.ma
          • Why is the ServletContextListener called when all Servlets are instantiated?

          You probably misundertood by the following webservice deployer output:

          12:13:26,227 INFO  [org.jboss.wsf.stack.cxf.metadata.MetadataBuilder] (MSC service thread 1-2) Add Service

          id=HelloServlet

          address=http://localhost:8080/helloServlet/HelloServlet

          implementor=servlet.hello.helloservlet.HelloServletImpl

          invoker=org.jboss.wsf.stack.cxf.InvokerJSE

          serviceName={http://helloservlet.hello.servlet/}HelloServletImplService

          portName={http://helloservlet.hello.servlet/}HelloServletImplPort

          wsdlLocation=null

          mtomEnabled=false

          12:13:26,228 INFO  [org.jboss.ws.common.management.DefaultEndpointRegistry] (MSC service thread 1-2) register: jboss.ws:context=helloServlet,endpoint=HelloServlet

          12:13:29,986 INFO  [stdout] (MSC service thread 1-2) HelloServlet is instantiated!

          12:13:29,991 INFO  [org.apache.cxf.service.factory.ReflectionServiceFactoryBean] (MSC service thread 1-2) Creating Service {http://helloservlet.hello.servlet/}HelloServletImplService from class servlet.hello.helloservlet.HelloServlet

          12:13:30,023 INFO  [org.apache.cxf.endpoint.ServerImpl] (MSC service thread 1-2) Setting the server's publish address to be http://localhost:8080/helloServlet/HelloServlet

          12:13:30,024 WARN  [org.jboss.wsf.stack.cxf.resolver.JBossWSResourceResolver] (MSC service thread 1-2) Cannot resolve resource: cxf

          12:13:30,030 INFO  [org.jboss.wsf.stack.cxf.deployment.WSDLFilePublisher] (MSC service thread 1-2) WSDL published to: file:/home/jimma/x1/code/git-client/maerqiang-work/jboss-as/build/target/jboss-as-7.1.0.Alpha1-SNAPSHOT/standalone/data/wsdl/helloServlet.war/HelloServletImplService.wsdl

          12:13:31,517 INFO  [org.jboss.as.webservices.service.EndpointService] (MSC service thread 1-10) Starting service jboss.ws.endpoint.helloServlet.HelloServlet

          As you see the HelloServletImpl.class (configured in web.xml  <servlet-class>servlet.hello.helloservlet.HelloServletImpl</servlet-class>) is not a real servlet class, the webservie deployer will modify the web.xml and start the endpoint impl before the real servlet - org.jboss.wsf.stack.cxf.CXFServletExt which servers the servlt transport initialize and start to work.  These webservice deployers which output above message are executed before the web deployer to create WebContext . That's why you see it is executed after the HelloServlet is Initialized. But ServletContextListener is actually executed as expected before the real servlets are initialized. Here in your war file is after  CXFServletExt and Startup are initialized.

           

          • Why is "<load-on-startup>" ignored when the implementation differ only in the WS implementation?

          It is not ignored. The <load-on-startup> configured to HelloServlet is passed to org.jboss.wsf.stack.cxf.CXFServletExt.  I tried with your example it works properly.

          rushead wrote:

          • Last but not least: What is the best and usual way to initialize a custom environment for several servlets in a war file?

          I did not get what you want to initialize from the source code(maybe I missed something), so I am not sure if do the initialize work in a real servlet and inject something with WebServiceContext can help. Here is some documentation about how to access MessageContext and/or Http request and response:http://cxf.apache.org/docs/servlet-transport.html

          1 of 1 people found this helpful
          • 2. Re: Load on startup is ignored - JaxWS webservice is always initialized first
            rushead

            Thank you for your response.

             

            But I have some more questions:

            Jim Ma schrieb:

            • Why is "<load-on-startup>" ignored when the implementation differ only in the WS implementation?

            It is not ignored. The <load-on-startup> configured to HelloServlet is passed to org.jboss.wsf.stack.cxf.CXFServletExt.  I tried with your example it works properly.

            I think I misunderstood the difference between the "real" servlets and the "fake" servlets.

            Both mechanisms (<load-on-startup> and ServletContextListener#contextInitialized) worked for me with JBoss AS 5.1 so that the HelloServlet (JaxWS implementation) was instantiated after the ServletContextListner was called / after the StartupServlet was initialized.

            In my opinion it should not make a difference if I implement a "real" servlet (using "implements Servlet") or using JaxWS to build a Webservice.

             

            Jim Ma schrieb:

            rushead wrote:

            • Last but not least: What is the best and usual way to initialize a custom environment for several servlets in a war file?

            I did not get what you want to initialize from the source code(maybe I missed something), so I am not sure if do the initialize work in a real servlet and inject something with WebServiceContext can help. Here is some documentation about how to access MessageContext and/or Http request and response:http://cxf.apache.org/docs/servlet-transport.html

            I am trying to initialize a custom application context and resource management which is used in all my servlets and webservices (sometimes even in the constructor to load the configuration).

            The problem is, that i have to initialize the context/resource management before the servlets and webservices are instantiated (of course).

            What is the common way to solve this problem?

            And why did it work in JBoss AS 5.1 and does not work anymore in the current AS 7?

            • 3. Re: Load on startup is ignored - JaxWS webservice is always initialized first
              jim.ma

              As7 is built from scratch and there are a lot of changes in deployer and service container. I understood your problem and will look at if we can change it to the same behavior as in AS 5.1.

              • 4. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                rushead

                This would be great, thanks.

                 

                But is there any other way to solve my problem?

                Or what is the common way to call a custom environment initialization before the initialization of the webservices and servlets has started?

                • 5. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                  jim.ma

                  If you do not have to call the custom initialization before the webservice constructor , this could help:

                  • call custom environment initialization in a ServletContextListener
                  • Get servletContext from injected webServiceContext like:

                  @WebService

                  public class HelloServletImpl implements HelloServlet {

                      @Resource

                      public WebServiceContext wsContext;

                     

                      public HelloServletImpl() {

                          System.out.println("HelloServlet is instantiated!");

                      }

                     

                      @Override 

                      public String newOperation(final String in) {

                          // TODO Auto-generated method stub

                          ServletContext servletContext =

                              (ServletContext) wsContext.getMessageContext().get(MessageContext.SERVLET_CONTEXT);

                          System.out.println(servletContext.getAttribute("TEST"));

                          return (String)servletContext.getAttribute("TEST");

                      }

                   

                  }

                  • 6. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                    rushead

                    Unfortunately that's exactly my problem.

                    I want to load the configuration (which is stored in an xml file) when the webservice is constructed.

                    Therefore I need the already initialized custom environment...

                    • 7. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                      asoldano

                      Hi rushead,

                      the WS endpoints is actually started during the deployment phase, which ends with rewriting (for POJO endpoints) [1] or creation (for EJB3 endpoints) of the webapp web.xml descriptor and start of the web transport layer. The servlet stuff is indeed just the transport layer/view of the webservice endpoint and is just the mean for serving HTTP requests to the webservice endpoints. The endpoint is thus created regardless of / independently from the servlet layer and the jbossws endpoint servlet delegates to the actual ws endpoints at runtime.

                      Expecting the servlet context to be available when the POJO endpoint bean is instanciated if unfortunately a wrong assumption here. Similarly, I bet you would not expect to have the servlet stuff available when an EJB3 WS endpoint is created in the case of EJB3 endpoints.

                      So unless there's a specification requirement (and I really believe there's not such req) mandating actually creating the endpoint after the web transport is up, this is not going to be modified, as this is a design decision and it granted multiple benefits. Btw, AS6 was already based on this afair.

                      This said, you can probably achieve the behaviour you need differently, below are some idea:

                      - you can modify your ws endpoint impl to call a given method when the first invocation comes; if your endpoint is being served on HTTP transport only, the servlet context and such will for sure be available

                      - you could also provide a configuration lookup mechanism that does not rely on the servlet init params (for instance lookup/read a conf or prop file in the deployment, use the ServiceLoader API, etc) and read your configuration in a @PostConstruct annotated method of the endpoint implementation

                      - if you still need to bind your endpoint creation to the servlet, you can have a custom servlet that programmatically starts a WS endpoint, either by the JAXWS Enpdoint.publish(..) API or through the EndpointPublisher API that is included in the current AS 7.1.0.Alpha1-SNAPSHOT version (that's still to be released, sorry)

                       

                       

                       

                      [1] your web.xml is rewritten by replacing the reference to the ws endpoint bean with the actual jbossws endpoint servlet; the endpoint bean class is passed to the endpoint servlet as a parameter

                      • 8. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                        rushead

                        Thank you very much.

                        Now I understand the behavior, and the differences between 5.1 and 7

                         

                        I'm using the @PostConstruct annotation now, which works fine for me.

                        My only problem now is (in my special case), that I think the webservice should be destroyed if the configuration can not be loaded in the @PostConstruct method.

                         

                        Is there any way to invoke that the webservice should be distroyed (within the @PostConstruct method)?

                        • 9. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                          asoldano

                          rushead wrote:

                           

                          Thank you very much.

                          Now I understand the behavior, and the differences between 5.1 and 7

                          No problem :-)

                           

                           

                          I'm using the @PostConstruct annotation now, which works fine for me.

                          My only problem now is (in my special case), that I think the webservice should be destroyed if the configuration can not be loaded in the @PostConstruct method.

                           

                          Is there any way to invoke that the webservice should be distroyed (within the @PostConstruct method)?

                          mmh... not really. Basically, you're deploying a war unit, so the lifecycle of the stuff in there is controlled by that unit being deployed and undeployed. You might throw an exception in the @PostConstruct annotated method, but I guess that would cause the whole unit deployment to fail (not sure though, this should be verified).

                           

                          That might come with some additional complexity, but this requirement would suggest trying the Endpoint publish API approach; it seems you really need to programmatically control the ws endpoint lifecycle.

                          • 10. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                            rushead

                            I now throw an Exception in the @PostConstruct.

                            It does not causes the failure of the whole deployment, which is now a good solution for me.

                             

                            The only thing which irritates me is that I am only allowed to declare that the method throws "Exception", I'm not allowed to declare a custom implementation of Exception (in my case "ConfigurationException)".

                             

                            {quote}

                            Caused by: org.jboss.ws.common.injection.InjectionException: Method annotated with @interface javax.annotation.PostConstruct annotation cannot throw checked exceptions: public void servlet.hello.helloservlet.HelloServletImpl.initSettings() throws servlet.hello.helloservlet.exceptions.ConfigurationException

                                at org.jboss.ws.common.injection.finders.ReflectionUtils.assertNoCheckedExceptionsAreThrown(ReflectionUtils.java:185)

                                at org.jboss.ws.common.injection.finders.AbstractPostConstructPreDestroyAnnotatedMethodFinder.validate(AbstractPostConstructPreDestroyAnnotatedMethodFinder.java:67)

                                at org.jboss.ws.common.injection.finders.PostConstructMethodFinder.validate(PostConstructMethodFinder.java:46)

                                at org.jboss.ws.common.injection.finders.AbstractPostConstructPreDestroyAnnotatedMethodFinder.validate(AbstractPostConstructPreDestroyAnnotatedMethodFinder.java:35)

                                at org.jboss.ws.common.reflection.AbstractClassProcessor.process(AbstractClassProcessor.java:54)

                                at org.jboss.ws.common.injection.InjectionHelper.callPostConstructMethod(InjectionHelper.java:169)

                                at org.jboss.ws.common.invocation.InvocationHandlerJAXWS.onEndpointInstantiated(InvocationHandlerJAXWS.java:71)

                                at org.jboss.ws.common.invocation.AbstractInvocationHandlerJSE.init(AbstractInvocationHandlerJSE.java:63)

                                at org.jboss.ws.common.invocation.AbstractInvocationHandlerJSE.invoke(AbstractInvocationHandlerJSE.java:97)

                                at org.jboss.wsf.stack.cxf.AbstractInvoker._invokeInternal(AbstractInvoker.java:169)

                                ... 32 more

                            {quote}

                             

                            That's no problem, I just don't know why "Exception" is not a catched exception...

                            • 11. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                              asoldano
                              • 12. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                                rushead

                                I know that I'm not allowed to throw a checked exception.

                                What is irritating me is that I am allowed to declare "throws Exception" - I thought only Exceptions which extend RuntimException are unchecked exceptions. Therefore the superclass "Exception" should be a checked exception too.

                                • 13. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                                  asoldano

                                  Well, this is not really an issue then ;-) I mean, you're not allowed to throw checked exception, so I don't see anything wrong in you not being allowed to throw your ConfigurationException unless it's a RuntimeException.

                                  We need to check if you can actually declare "throws Exception" and go with that, in any case that's an edge case. The code for the validation in any case is as follows:

                                   

                                  public static void assertNoCheckedExceptionsAreThrown(final Method method, Class<? extends Annotation> annotation)

                                     {

                                        Class<?>[] declaredExceptions = method.getExceptionTypes();

                                        for (int i = 0; i < declaredExceptions.length; i++)

                                        {

                                           Class<?> exception = declaredExceptions[i];

                                           if (!exception.isAssignableFrom(RuntimeException.class))

                                           {

                                              throw new InjectionException(BundleUtils.getMessage(bundle, "METHOD_CANNOT_THROW_CHECKED_EXCEPTIONS", new Object[]{ getAnnotationMessage(annotation) ,  method}));

                                           }

                                        }

                                     }

                                  • 14. Re: Load on startup is ignored - JaxWS webservice is always initialized first
                                    rushead

                                    Alessio Soldano schrieb:

                                     

                                    We need to check if you can actually declare "throws Exception" and go with that, in any case that's an edge case.

                                    Yes, it works.

                                     

                                    That's the way I implemented it now (which does not cause an Exception when the war is deployed):

                                    @PostConstruct

                                        public void initSettings() throws Exception {

                                              // here I am throwing my ConfigurationException if the xml cannot be read/parsed

                                        }

                                     

                                    If i declare my custom (catched) Exception it throws the InjectionException I posted above:

                                    @PostConstruct

                                        public void initSettings() throws ConfigurationException {

                                              // here I am throwing my ConfigurationException if the xml cannot be read/parsed

                                        }

                                     

                                    That's what I ment when I said it irritates me a little bit, because I think "java.lang.Exception" is also a catched Exception.