Deploying Spring MVC Based Web Applications to OSGi Using Apache ServiceMix

The OSGi specification defines appropriate bundle manifest headers and mechanisms for the deployment of servlet-based WAR packaged applications into an OSGi container. As with most APIs, programming a modern application using the Servlet specification requires copious amounts of boilerplate code that any number of Web application frameworks can reduce. The Spring MVC framework is among these Web application frameworks. As with many of the capabilities of an OSGi container, the hooks for integrating the WAR with the capabilities of the container, such as OSGi services; modularity; configuration; and class loading, requires programming copious amounts of boilerplate code to OSGi APIs. Unlike the world of Web applications, there are far fewer frameworks for simplifying deployment in an OSGi container. The Spring DM framework is among this small group. This article discusses how to leverage the Spring MVC framework together with the capabilities of Spring DM in order to take full advantage of the value added features of both frameworks. This article uses Apache ServiceMix as the target platform.

Update September 4, 2013: The source code tagged 1.0 works with ServiceMix 4.3.  The source code tagged 1.1 works with ServiceMix 4.5.2.  The source code is largely the same except for differences in the Karaf feature descriptor needed to install the correct version of Spring MVC and Spring DM for the targeted ServiceMix container.  The article provides instructions for obtaining the correct source code for the targeted ServiceMix container as needed.

The Project and Build Structure

One of the easiest ways to assemble an OSGi ready WAR is to use Maven in conjunction with the WAR and Felix Bundle Plug-ins. The WAR Plug-in allows the build to produce a WAR artifact while the Bundle Plug-in simplifies the creation of the OSGi manifest. The following Maven POM snippet illustrates the key points of the Maven build. The complete POM can be found in the source code repository.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
  ...
  <packaging>war</packaging>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
          <!-- Make a skinny WAR -->
          <packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
          <archive>
            <manifestFile>${basedir}/target/bnd/MANIFEST.MF</manifestFile>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>2.3.5</version>
        <executions>
          <execution>
            <id>bundle-manifest</id>
            <phase>process-classes</phase>
            <goals>
              <goal>manifest</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <supportedProjectTypes>
            <supportedProjectType>war</supportedProjectType>
          </supportedProjectTypes>
          <manifestLocation>target/bnd</manifestLocation>
          <instructions>
            <Webapp-Context>spring-mvc-smx</Webapp-Context>
            <Web-ContextPath>spring-mvc-smx</Web-ContextPath>
            <Export-Package>!*</Export-Package>
            <Import-Package>
              javax.servlet; version="[2.4.0, 4.0.0)",
              javax.servlet.http; version="[2.4.0, 4.0.0)",
              javax.servlet.jsp.jstl.core; version="[1.2,2.0)",
              javax.servlet.resources; version="[2.4.0, 4.0.0)",
              <!-- Just import EVERYTHING from Apache standard JSTL impl. -->
              org.apache.taglibs.standard; version="[1.2.0,2)",
              org.apache.taglibs.standard.extra.spath; version="[1.2.0,2)",
              org.apache.taglibs.standard.functions; version="[1.2.0,2)",
              org.apache.taglibs.standard.lang.jstl; version="[1.2.0,2)",
              org.apache.taglibs.standard.lang.jstl.parser; version="[1.2.0,2)",
              org.apache.taglibs.standard.lang.jstl.test; version="[1.2.0,2)",
              org.apache.taglibs.standard.lang.jstl.test.beans; version="[1.2.0,2)",
              org.apache.taglibs.standard.lang.support; version="[1.2.0,2)",
              org.apache.taglibs.standard.resources; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.common.core; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.common.fmt; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.common.sql; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.common.xml; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.el.core; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.el.fmt; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.el.sql; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.el.xml; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.rt.core; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.rt.fmt; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.rt.sql; version="[1.2.0,2)",
              org.apache.taglibs.standard.tag.rt.xml; version="[1.2.0,2)",
              org.apache.taglibs.standard.tei; version="[1.2.0,2)",
              org.apache.taglibs.standard.tlv; version="[1.2.0,2)",
              org.springframework.beans.factory.config,
              org.springframework.osgi.web.context.support; version="[1.2,2.0)",
              org.springframework.stereotype,
              org.springframework.web.bind.annotation,
              org.springframework.web.servlet,
              org.springframework.web.servlet.view,
              *
            </Import-Package>
            <Bundle-ClassPath>
              WEB-INF/classes,
              <!-- Have to use this for PAX Web 0.7.4 to find JSPs since it uses classpath. -->
              .
            </Bundle-ClassPath>
          </instructions>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>
</project>
  • Line 4 – This line is the standard way in which you instruct Maven to construct a WAR artifact instead of the default JAR packaging.
  • Line 13 – This line instructs the WAR Plug-in to exclude all dependencies from the WAR. The resulting WAR imports all of its dependencies from the OSGi class loader and is therefore significantly smaller than a traditional WAR.
  • Line 15 – This line instructs the WAR Plug-in to use an externally generated manifest file instead of the one that is automatically generated by default.
  • Line 28 – This line instructs the Felix Bundle Plug-in to generate a manifest file on the file system when the plug-in is executed. Normally, the plug-in injects the manifest directly into the artifact; however, this behavior conflicts with that of the WAR plug-in so we configure this plug-in to write the file to disk and we configure the WAR Plug-in to use the manifest file from the file system.
  • Line 34 – This line instructs the Felix Bundle Plug-in to execute for projects with the WAR packaging type. The plug-in will not execute for this type of packaging by default.
  • Line 36 – This line indicates where the Felix Bundle Plug-in should write the manifest file. Note that this location aligns with the location that the WAR Plug-in reads the manifest file from.
  • Lines 38-39 – These lines provide the context path for the Web application when it is deployed. The context path is the portion of the path preceding the application specific path. In this case, all application URLs will start with http://HOST:PORT/spring-mvc-smx. This property is specified twice in order to support a wider range of PAX Web Extender versions. Web-ContextPath is the official OSGi header while Webapp-Context is the PAX Web Extender specific header supported in earlier versions of PAX Web. PAX Web is the implementation of the WAR related OSGi capabilities that is used in ServiceMix. This library is responsible or connecting the WAR to the underlying OSGi framework as well as publishing the WAR to the OSGi HTTP Service.
  • Lines 42-45 – These lines indicate that the OSGi container, through its class loading mechanism, should provide the standard Servlet API classes. Much like standard WAR deployments, the container frequently provides these API classes to the class path.
  • Lines 46-69 – These lines indicate that the OSGi container should provide the Apache implementation of the JSTL to the WAR. In this case, we simply import all of the packages that the Apache “standard” JSTL implementation library exports. These package imports are required if your application includes JSPs that leverage the JSTL tags, but may vary if you choose to use a different implementation. Providing these packages from the container instead of WEB-INF/lib means that your WAR size can be reduced and the implementation library can be shared across multiple WARs. As these packages are not directly referenced from compiled code, they are not automatically detected by the plug-in and therefore must be manually specified.
  • Line 70-75 – These lines indicate that the OSGi container should provide the necessary Spring MVC classes from the OSGi class loader and not from WEB-INF/lib. As with the Apache JSTL implementation, these packages are not directly referenced from compiled code and are therefore not detected by the Felix Bundle Plug-in. These packages must be imported from the OSGi container to ensure a consistent class space with the Spring DM OsgiBundleXmlWebApplicationContext class.
  • Lines 79-81 – These lines indicate that the bundle/WAR class path should also include the contents of specific locations within the WAR. The WEB-INF/classes folder provides the implementation classes for the WAR while the “.” entry is required to enable the PAX Web Extender to locate JSPs within the WAR. Some versions of the PAX Web Extender search for the JSP path using the WAR/bundle class path in lieu of searching the actual bundle/WAR contents. As such, including the root folder of the bundle/WAR on the class path ensures that JSPs can be resolved over a broader range of PAX Web versions.

The project folder structure is as follows:

  • /src/main/java – This folder contains the source code for the WAR.
  • /src/main/features – This folder contains the source file for the project Karaf Feature Descriptor.
  • /src/main/webapp – This folder contains the contents that are copied into the root of the WAR. This folder contains JSPs, web.xml, and the Spring MVC application context file.

Spring MVC In an OSGi World

Spring MVC typically uses an XmlWebApplicationContext instance with the DispatcherServlet. Together, these two classes provide a means to bootstrap a Servlet container aware application context and to wire the Servlet container to the Spring MVC controllers. XmlWebApplicationContext is however, unaware of the OSGi container. For this reason, Spring DM provides an OsgiBundleXmlWebApplicationContext that can be substituted for XmlWebApplicationContext when deploying the WAR in an OSGi container. OsgiBundleXmlWebApplicationContext makes the application context in the WAR aware of the OSGi container and provides access to the capabilities provided by the container as well as the BundleContext of the deployed bundle/WAR. One specifies that the DispatcherServlet should use the OSGi aware version of the application context in the web.xml file of the bundle/WAR. The following web.xml snippet illustrates the relevant configuration. After this small change, the standard features of Spring and Spring MVC, including annotation based controllers and component scanning, are available within the OSGi deployed WAR. The complete web.xml can be found in the source code repository.

…
  <servlet>
    <servlet-name>spring-mvc-smx</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
    </init-param>
  </servlet>
…
  • Lines 5-8 – Illustrate using an init-param for the DispatcherServlet to override the implementation class for the application context used by the servlet.

Tag Libraries in an OSGi World

Normally, tag libraries can be auto-detected from the WEB-INF/lib folder or specified in the web.xml file. As this example does not specify any JAR files from WEB-INF/lib in the Bundle-Classpath manifest header, the tag library implementation must be provided by the OSGi container class loader through a package import. While this approach does not require the tag library implementation to be included in the WAR itself, it does create a small issue with auto-discovery of the .tld files for the tag library. Tag library descriptor files reside in the META-INF folder of the JAR. When this JAR is not in the WEB-INF/lib folder and its packages are instead supplied by OSGi package imports, these files are not detected. The workaround for this limitation is to include the tag library descriptor files directly in the WAR and to reference them from web.xml. The following web.xml snippet illustrates the relevant configuration settings for including the JSTL .tld files from withing the WAR itself. The complete web.xml can be found in the source code repository.

…
  <jsp-config>
    <taglib>
      <taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
      <taglib-location>/WEB-INF/tld/c.tld</taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>http://java.sun.com/jsp/jstl/fmt</taglib-uri>
      <taglib-location>/WEB-INF/tld/fmt.tld</taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>http://java.sun.com/jsp/jstl/fmt</taglib-uri>
      <taglib-location>/WEB-INF/tld/fmt.tld</taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>http://java.sun.com/jsp/jstl/functions</taglib-uri>
      <taglib-location>/WEB-INF/tld/fn.tld</taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>http://java.sun.com/jsp/jstl/xml</taglib-uri>
      <taglib-location>/WEB-INF/tld/x.tld</taglib-location>
    </taglib>
  </jsp-config>
…
  • Lines 2-23 – These lines provide the location for the TLD files of the JSTL. The files were copied from the implementation JAR that the bundle/WAR imports the JSTL implementation packages from. Alternatively, the JSTL implementation JAR can be included in the WAR and added to the Bundle-Classpath manifest header; however, the bundle/WAR will then need to import the package dependencies from the implementation JAR or also add those dependencies to the Bundle-Classpath manifest header.

Bridging the OSGi WAR Gap

So far this article has discussed how to get Spring MVC to act like Spring MVC in an OSGi deployed WAR. If there were nothing more, the keener reader’s out there would be asking, “why would I go through all this trouble when Servlet container XXX will do just fine?” Don’t worry if you didn’t ask this question, you are already researching this topic and reading a blog. That makes you more keen than not. Because the WAR is using OsgiBundleXmlWebApplicationContext from Spring DM, all of the OSGi goodness that Spring DM brings to Spring is also available to the Spring MVC based applications. Service implementations for Spring MVC controllers can be looked up in the OSGi service registry, configuration options can be managed and updated through the OSGi Configuration Admin service, and previously insurmountable class space collisions can be overcome. The following Spring application context snippet illustrates one of the many convenient integration points that Spring DM can provide to the Web application. More examples of the Spring DM capabilities are available in the Spring DM documentation. The complete context file can be found in the source code repository.

...
<osgi:reference id="configurationAdmin"
                  interface="org.osgi.service.cm.ConfigurationAdmin"/>
...
  • Lines 2-3 – This line illustrates how to retrieve a service from the OSGi registry. In this case, the service is the Configuration Admin service. The controller in the example application uses this service to list all of the configurations available in the OSGi container.

Once available in the application context, the bean can be used just as any other locally defined bean. The following Java snippet illustrates how the bean is injected into a controller and used to render the view.  The complete controller source is available in the source repository.

...

@Controller
@RequestMapping(value = &quot;/&quot;)
public class HomeController {

    @Autowired
    BundleContext bundleContext;

    @Autowired
    ConfigurationAdmin configurationAdmin;

    RequestMapping(method = {RequestMethod.GET})
    public ModelAndView home() throws Exception {

        ModelAndView mav = new ModelAndView(&quot;home&quot;);

        Bundle[] bundles = bundleContext.getBundles();
        mav.addObject(&quot;bundles&quot;, bundles);

        Configuration[] configurations = configurationAdmin.listConfigurations(null);
        mav.addObject(&quot;configurations&quot;, configurations);
        return mav;
    }

    public BundleContext getBundleContext() {
        return bundleContext;
    }

    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }

    public ConfigurationAdmin getConfigurationAdmin() {
        return configurationAdmin;
    }

    public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
        this.configurationAdmin = configurationAdmin;
    }
}
  • Line 11 – This line declares an instance of the Configuration Admin interface as an instance variable that is set using Spring’s auto-wiring.
  • Lines 14-23 – These lines query the Configuration Admin service and add the results to the model for rendering.

Deploying in Apache ServiceMix

In order to deploy a WAR using Spring MVC and the Apache JSTL implementation, several additional OSGi bundles must be provisioned in an out-of-the-box ServiceMix instance. The easiest and most repeatable way to provision these bundles is to use a Karaf feature descriptor to describe the bundles to be provisioned. The following feature descriptor snippet illustrates the additional provisioning activities that are needed. The complete feature descriptor can be found in the source code repository.

<?xml version="1.0" encoding="UTF-8"?>
<features name="spring-mvc-smx-${project.version}">
  <feature name="spring-mvc-smx" version="${project.version}">
    <feature>spring</feature>
    <feature>spring-dm</feature>
    <feature>war</feature>

    <bundle>mvn:${project.groupId}/spring-mvc-smx/${project.version}/war</bundle>

    <bundle>mvn:org.springframework.osgi/spring-osgi-web/1.2.0</bundle>
    <bundle>mvn:org.springframework/spring-web/3.0.5.RELEASE</bundle>
    <bundle>mvn:org.springframework/spring-webmvc/3.0.5.RELEASE</bundle>

    <bundle>mvn:javax.servlet/com.springsource.javax.servlet.jsp.jstl/1.2.0.v20110728</bundle>
  </feature>
</features>
  • Lines 4-6 – These lines ensure that the core Spring bundles, the core Spring DM bundles, and the PAX Web bundles are provisioned in the container.
  • Lines 10-12 – These lines provision the additional Spring DM and Spring MVC bundles required to deploy a WAR using Spring MVC.
  • Line 14 – These lines provision the bundle related to the Apache implementation of the JSTL.

Building and Deploying the Example Code

Build Pre-requisites

Deploy Pre-requisites

Building

  1. For deploying in ServiceMix 4.3, checkout the source code from https://davidvaleri.googlecode.com/svn/projects/examples/spring-mvc-smx/tags/spring-mvc-smx-1.0 using your Subversion client.  For deploying to ServiceMix 4.5.2, checkout the source code from https://davidvaleri.googlecode.com/svn/projects/examples/spring-mvc-smx/tags/spring-mvc-smx-1.1 using your Subversion client.  Note that the code from each tag will likely work with other releases of ServiceMix using the same version of Spring.  See the comments for different user experiences with different releases of ServiceMix.
  2. Build the source code by executing the following command from the checkout folder:
    mvn clean install

Deploying

  1. Start Apache ServiceMix
  2. In the ServiceMix shell, add the feature descriptor for the example application.
    1. Execute the following command on ServiceMix 4.3:
      features:addurl mvn:valeri.blog.examples/spring-mvc-smx/1.0/xml/features
    2. Execute the following command on ServiceMix 4.5.2:
      features:addurl mvn:valeri.blog.examples/spring-mvc-smx/1.1/xml/features

    NOTE: If you did not build the example locally, you will need to add the Maven repository for this blog to the ServiceMix configuration. Edit SERVICEMIX_HOME/etc/org.ops4j.pax.url.mvn.cfg and add http://davidvaleri.googlecode.com/svn/repo/ to the org.ops4j.pax.url.mvn.repositories property.

  3. Provision the example WAR by executing the following command in the ServiceMix shell:
    features:install spring-mvc-smx
  4. Confirm the installation by executing the following command in the ServiceMix shell:
    osgi:list
  5. The output should resemble the following:
    [ 202] [Active ] [ ] [ ] [ 60] David Valeri's Blog :: Examples :: Spring MVC In ServiceMix (1.0.0)
    [ 203] [Active ] [ ] [ ] [ 60] spring-osgi-web (1.2.0)
    [ 204] [Active ] [ ] [ ] [ 60] Spring Web (3.0.5.RELEASE)
    [ 205] [Active ] [ ] [ ] [ 60] Spring Web Servlet (3.0.5.RELEASE)
    [ 206] [Active ] [ ] [ ] [ 60] JavaServer Pages (TM) TagLib API and Implementation (1.2.0.v20110728)
  6. Navigate to http://localhost:8181/spring-mvc-smx/ using the browser of your choice.
  7. The displayed page contains information retrieved from the OSGi bundle context as well as from an OSGi service retrieved from the OSGi service registry. If you receive an error message or no page is displayed, try refreshing the WAR bundle as it may have loaded before the other newly provisioned bundles. Execute the refresh in the ServiceMix shell using the ID of the bundle with the name “David Valeri’s Blog :: Examples :: Spring MVC In ServiceMix (1.0.0)”. If you still receive an error message, refer to the ServiceMix log files and feel free to post a comment below.
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

40 Responses to Deploying Spring MVC Based Web Applications to OSGi Using Apache ServiceMix

  1. junkiest says:

    Nice tutorial…. thanks !!!

  2. sekaijin says:

    Hi,
    Nice but I’ve somme problems
    first I’m using eclipse indigo and M2e plugin
    mvn compile dose not run on eclipse :
    Warnings :
    Description Resource Path Location Type
    maven-enforcer-plugin (goal “enforce”) is ignored by m2e. pom.xml /spring-mvc-smx line 5 Maven Project Build Lifecycle Mapping Problem
    maven-remote-resources-plugin (goal “process”) is ignored by m2e. pom.xml /spring-mvc-smx line 5 Maven Project Build Lifecycle Mapping Problem

    [ERROR] Failed to execute goal org.apache.felix:maven-bundle-plugin:2.3.5:manifest (bundle-manifest) on project spring-mvc-smx: Execution bundle-manifest of goal org.apache.felix:maven-bundle-plugin:2.3.5:manifest failed: An API incompatibility was encountered while executing org.apache.felix:maven-bundle-plugin:2.3.5:manifest: java.lang.NoSuchMethodError: java.lang.String.getBytes(Ljava/nio/charset/Charset;)[B
    [ERROR] —————————————————–
    [ERROR] realm = plugin>org.apache.felix:maven-bundle-plugin:2.3.5
    [ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy

    no problem in command line for compile and package.

    I’m using Servicemix 4.3.0 on MacOS
    no pb for deplyng the war.
    but blank page on http://localhost:8181/spring-mvc-smx/
    I’ve trying telnet localost 8181
    GET /spring-mvc-smx/
    Problem accessing /spring-mvc-smx/. Reason:

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/mvc]
    Offending resource: URL [bundle://207.0:0/WEB-INF/spring-mvc-smx-servlet.xml]
    Caused by:

    javax.servlet.UnavailableException: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/mvc]
    Offending resource: URL [bundle://207.0:0/WEB-INF/spring-mvc-smx-servlet.xml]
    .. stacktrace ..
    Powered by Jetty://
    Bye
    PS: sorry for my approximative english

  3. sekaijin says:

    Hi
    features:install spring-dm
    simply load spring feature before installing the war

    Bye

  4. sekaijin says:

    Hi
    I tried to uninstall and reinstall the war
    I found a problem.
    during the first install ServiceMix start the war and then the Spring bundles. hence the message “Unable to locate Spring for XML schema namespace NamespaceHandler …”
    if I stop and restart the war, no problems.

    I’ll try to make two packages. one with only spring. the other with the war.

    for the blank page the pb comes from jetty http 413 error on some browsers (no pb with chrome or safari)

    thank you A + JYT

  5. David Valeri says:

    The issue with m2e is expected as the example was developed before m2e, or at least before I was using Indigo and m2e, so the required configuration in the POM to make it play well with m2e is missing. It will work with m2Eclipse or you can use the information at http://wiki.eclipse.org/M2E_plugin_execution_not_covered to add the configuration needed to fix the first two issues with m2e. Eclipse should also highlight those plug-ins in the POM and have a quick-fix action to add the configuration to the POM for you. As for the third issue with the bundle plug-in, is Eclipse trying to use Java 1.5?

    For the issue with the missing NamespaceHandler, step 7 in the deploy instructions resolves the issue with the bundle loading like you said. It is indeed a bundle loading order. Unfortunately, the Spring NamespaceHandler stuff is outside the scope of OSGi’s usual dependency resolution mechanisms and the container doesn’t know that the WAR bundle can’t be started until after the spring-osgi-web bundle is loaded. Possible solutions are to 1) use two features, one with the Spring bundle in it and the other with the WAR bundle in it, 2) deal with the failure and realize that you may have to restart/refresh the WAR bundle, or 3) (mis)use OSGi start-levels to force the WAR bundle startup to happen after the spring-osgi-web bundle start-up.

    For the 413 issue, what is a “pb” and is there any information provided in the logs? It may be an issue in Jetty, pax-web, or your browser may be doing something weird. Have you checked what the browser is actually sending to the server when the 413 occurs?

  6. sekaijin says:

    Hi,
    I adapted to work with the POM M2e.

    For the error “HTTP 413” This is a request too large.
    on my usual browser I test another development that uses large cookies. the request to the server are as big and cookies are associated with the host are also sent even if it is not the same port. (localhost:80 and localhost:8181)
    I have discoveries that a problem with jetty in ServiceMix 4.3.0.

    I usually work with version apache-servicemix-4.4.1-fuse-01-06
    I adaped to run this tutorial on ServiceMix 4.4.0

    I just changed mvn:org.springframework.osgi/spring-osgi-web/1.2.1 with version 1.2.1

    for the namespace problem I found a simple solution
    I put mvn:${project.groupId}/spring-mvc-smx/${project.version}/war at last position in the file features.xml
    now in my dev platform spring-mvc-smx start after all others.
    but I think that OSGi Start level is the best solution

    Thank for all

    A+JYT
    PS: sorry “pb” = problem

  7. David Valeri says:

    Nice work and thanks for providing the feedback. I have also noticed that the order of the bundles in the feature descriptor dictates installation order; however, I am not sure if this is an intentional design feature or if it is just a result of how it is currently implemented. Like you said, start level will always work while the behavior of Karaf Features may change in the future.

    Good luck with your project!

  8. sekaijin says:

    Hi

    the solution to resolve HTTP 413 error (header too large) in jetty comme from
    Freeman Fang of FuseSource

    1) add org.ops4j.pax.web.config.file=etc/jetty.xml
    in etc/org.ops4j.pax.web.cfg file.

    2) edit jettty.xml file (create it)

    <?xml version=”1.0″?>
    <!DOCTYPE Configure PUBLIC “-//Mort Bay Consulting// DTD Configure//EN” “http://jetty.mortbay.org/configure.dtd”>
    <Configure class=”org.eclipse.jetty.server.Server”>
    <Call name=”addConnector”>
    <Arg>
    <New class=”org.eclipse.jetty.server.nio.SelectChannelConnector”>
    <Set name=”host”><SystemProperty name=”jetty.host” /></Set>
    <Set name=”port”><SystemProperty name=”jetty.port” default=”8181″/></Set>
    <Set name=”maxIdleTime”>30000</Set>
    <Set name=”Acceptors”>2</Set>
    <Set name=”statsOn”>false</Set>
    <Set name=”confidentialPort”>8443</Set>
    <Set name=”lowResourcesConnections”>5000</Set>
    <Set name=”lowResourcesMaxIdleTime”>5000</Set>
    <Set name=”requestHeaderSize”>8192</Set>
    <Set name=”responseHeaderSize”>8192</Set>
    <!–Set name=”headerBufferSize”>8192</Set–>
    <Set name=”useDirectBuffers”>false</Set>
    </New>
    </Arg>
    </Call>
    </Configure>

    Bye

  9. Rick says:

    Is it possible to run ServiceMix 4.x in an app server like JBoss?
    My problem is that I am stuck using JBoss 5 and would like to deploy a spring dm app to it.

    I’m hoping that I can host servicemix on jboss and take your documented approach from there.
    thanks

    • David Valeri says:

      You will likely need to wait until ServiceMix 5 [1] for additional deployment options.

      [1] http://comments.gmane.org/gmane.comp.java.servicemix.user/28794

      • Rick says:

        Do you happen to know of any other relatively painless ways to deploy a spring dm or gemini blueprint app to jboss?

      • David Valeri says:

        Painless… no.

        JBoss does have some support for OSGi these days. You could, in theory, add the Spring DM bundles in there and then they would start your OSGi application when you deploy your application bundles. This is of course pure conjecture since I have never tried it.

        If you are stuck on an older JBoss version, you could try starting an OSGi framework inside of a Web application and then configure that framework with Spring DM and your application bundles. Atlassian’s plug-in framework runs as an embedded OSGi framework inside of a Web application so this approach is definitely possible.

        Third, if the application is not heavily tied to some OSGi feature, you could port it back to a flat classloader and deploy it using traditional app server packaging.

  10. sujatha says:

    I got the below error while http://localhost:8181/spring-mvc-smx/

    Problem accessing /spring-mvc-smx/. Reason:

    org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
    PropertyAccessException 1: org.springframework.beans.TypeMismatchException: Failed to convert property value of type ‘java.lang.String’ to required type ‘java.lang.Class’ for property ‘contextClass’; nested exception is java.lang.IllegalArgumentException: Cannot find class [org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext]

    • David Valeri says:

      Are you using the same version of SMX as the article? If so, do you have the package imports from lines 70-75 in the example POM in your POM? If so, is the bundle from mvn:org.springframework.osgi/spring-osgi-web/1.2.0 installed and resolved correctly in the container?

      Check your bundle imports to make sure that the package containing the missing class is imported. If it is, make sure that it is not optional and that it is being resolved.

      • sujatha says:

        iam able to run this by using same version of smx as the article says,but when iam trying to run it on another version(4.4.0)iam getting the error as i said

      • David Valeri says:

        I would guess that it has to do with the version of Spring DM or some other dependency in the mix. I’ll try to take a look some time in the next week to see if anything pops out at me.

      • David Valeri says:

        See the comment below from David Ovington. Perhaps his solution for 4.4.1 will solve your issue as well.

      • sujatha says:

        now iam able to run .
        thanks a lot

  11. David Ovington says:

    David Ovington says:

    March 16, 2012 at 7:15 pm

    Thanks for a great article, saved me a lot of time.
    I’ve deployed in the FuseSource build of ServiceMix 4.4.1 (apache-servicemix-4.4.1-fuse-01-13).
    There were some extra lines required in the features xml:

     <feature>spring-dm-web</feature>
     
    <bundle>mvn:org.springframework.osgi/spring-osgi-core/1.2.0</bundle>
    <bundle>mvn:org.springframework.osgi/spring-osgi-io/1.2.0</bundle>
    

    So I now have what should be simple request, but I cannot find the answer to… how to load a simple static image in home.jsp ?
    A standard tag of:

    <img src="image.jpg" alt="some_text"/> 
    

    fails to load the image, i’ve tried putting it at just about every level in the war with no luck ?
    At first I thought the servlet mapping was the problem, so tied the HomeController down to a specific html, and ensured that *.jpg were handled by the default (as below), but with no luck ! any ideas ?
    Thanks.

      <servlet-mapping>
        <servlet-name>spring-mvc-smx<servlet-name>
        <url-pattern>*.html</url-pattern>
      </servlet-mapping>
     
      <servlet-mapping>
        <servlet-name>default<servlet-name>
        <url-pattern>*.jpg</url-pattern>
      </servlet-mapping>
     
  12. In my case, i receive: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘configurationAdmin’: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Required ‘bundleContext’ property was not set.
    I guess it’s a sort of NPE, autowiring doesnt work. I tryed a lot of such examples, results the same. Could anyone give a hand.
    Thanks

  13. kso77 says:

    Great job. Tested on on Karaf 2.3.0.

  14. Alex says:

    I tried this on the latest 4.5.1 of servicemix and I get the following error when I try to do a feature:install

    Error executing command: Could not start bundle mvn:valeri.blog.examples/spring-mvc-smx/1.0/war in feature(s) spring-mvc-smx-1.0: Unresolved constraint in bundle valeri.blog.examples.spring-mvc-smx [190]: Unable to resolve 190.0: missing requirement [190.0] package; (&(package=org.springframework.osgi.web.context.support)(version>=1.2.0)(!(version>=2.0.0))) [caused by: Unable to resolve 191.0: missing requirement [191.0] package; (&(package=org.springframework.osgi)(version>=1.2.0)(version<=1.2.0))]

    Any idea ?

    • David Valeri says:

      Looks like the Spring DM packages are not resolving. Try executing “exports | grep org.springframework.osgi”. This command will tell you which bundles are exporting that package and which version is being exported. You can also run “imports | grep org.springframework.osgi” to see who is importing the package. My guess from the error message is that you have Spring DM 1.2.1+, but the example or one of its dependencies is looking for Spring DM 1.2.0. You can also execute “headers ” to see the imports and exports of the bundles in question, 190 I think. Missing imports should be highlighted in red.

  15. Chirag Patel says:

    This example works fine with Apache Service Mix 4.3.0.
    When I tried to deploy same example to deploy in 4.5.1 It gives me following error

    karaf@root> features:install spring-mvc-smx
    Error executing command: Could not start bundle mvn:valeri.blog.examples/spring-mvc-smx/1.0/war in feature(s) spring-mvc
    -smx-1.0: Unresolved constraint in bundle valeri.blog.examples.spring-mvc-smx [203]: Unable to resolve 203.0: missing re
    quirement [203.0] package; (&(package=org.springframework.osgi.web.context.support)(version>=1.2.0)(!(version>=2.0.0)))
    [caused by: Unable to resolve 204.0: missing requirement [204.0] package; (&(package=org.springframework.osgi)(version>=
    1.2.0)(version

    My maven repository contains 1.2.0 jar.
    command “exports | grep org.springframework.osgi” shows that I have 1.2.1 version.

    Can anyone tell me what are the changes required to run this example in “Apache Service Mix 4.5.1”?

    • David Valeri says:

      I finally got a free moment to look into your issue with 4.5. I’ve pushed up a 1.1 of the example that resolves the issue on 4.5.2 by updating the Spring MVC bundle versions in the feature descriptor to match the version of Spring pre-installed in the container. This approach should translate to any release of ServiceMix. Just make sure the bundles in the feature descriptor align with the version of Spring and Spring DM pre-installed in your version of ServiceMix and things should work.

  16. Muthu says:

    This example is awesome, I tried to develop the OSGI based spring mvc application. I am using KARAF for deploying the bundle.

    I placed the dependency jar (org.springframework.*) into the deploy folder and placed my Spring MVC war file inside the deploy folder.

    I am getting the error like in UI,
    =====================
    HTTP ERROR 500

    Problem accessing /spring-kalees-mvc-host/osgi/osgi.htm. Reason:

    Failed properties: Failed to convert property value of type ‘java.lang.String’ to required type ‘java.lang.Class’ for property ‘contextClass’; nested exception is java.lang.IllegalArgumentException: Cannot find class [org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext]
    Caused by:

    org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessException details (1) are:
    PropertyAccessException 1:
    org.springframework.beans.TypeMismatchException: Failed to convert property value of type ‘java.lang.String’ to required type ‘java.lang.Class’ for property ‘contextClass’; nested exception is java.lang.IllegalArgumentException: Cannot find class [org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext]
    at org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:468)

    I opened the log file and I saw that ,
    ============================

    2013-09-11 00:14:47,187 | INFO | BundleWatcher: 2 | HttpServiceFactoryImpl | 90 – org.ops4j.pax.web.pax-web-runtime – 3.0.0.M3 | Binding bundle: [org.danielsoft.learn.spring-kalees-mvc-host [142]] to http service
    2013-09-11 00:14:47,218 | ERROR | BundleWatcher: 2 | RegisterWebAppVisitorWC | 103 – org.ops4j.pax.web.pax-web-extender-war – 3.0.0.M3 | Registration exception. Skipping.
    java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener not found by org.danielsoft.learn.spring-kalees-mvc-host [142]
    at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1460)[org.apache.felix.framework-4.0.3.jar:]
    at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:72)[org.apache.felix.framework-4.0.3.jar:]

    I am using the spring 3.2.2.RELEASE.

    My bundle information :
    ===================

    Bundle Classpath WEB-INF/classes/,.
    Exported Packages org.springframework.osgi.sample.web,version=0.0.1.SNAPSHOT
    Imported Packages javax.servlet,version=2.6.0 from org.apache.geronimo.specs.geronimo-servlet_3.0_spec (76)
    javax.servlet,version=3.0.0 from org.apache.geronimo.specs.geronimo-servlet_3.0_spec (76)
    javax.servlet.http,version=2.6.0 from org.apache.geronimo.specs.geronimo-servlet_3.0_spec (76)
    javax.servlet.http,version=3.0.0 from org.apache.geronimo.specs.geronimo-servlet_3.0_spec (76)
    org.springframework.stereotype,version=3.2.2.RELEASE from org.springframework.context (124)
    org.springframework.web.bind.annotation,version=3.2.2.RELEASE from org.springframework.web (119)
    org.springframework.web.servlet,version=3.2.2.RELEASE from org.springframework.web.servlet (116)
    Service ID 430 Types: javax.servlet.ServletContext
    Manifest Headers Archiver-Version: Plexus Archiver
    Bnd-LastModified: 1378838240046
    Build-Jdk: 1.6.0_26
    Built-By: rajeshkumar_ra
    Bundle-Classpath: WEB-INF/classes/, .
    Bundle-ManifestVersion: 2
    Bundle-Name: spring-kalees-mvc-host
    Bundle-SymbolicName: org.danielsoft.learn.spring-kalees-mvc-host
    Bundle-Version: 0.0.1.SNAPSHOT
    Created-By: Apache Maven Bundle Plugin
    Export-Package: org.springframework.osgi.sample.web; uses:=”javax.servlet.http, org.springframework.stereotype, org.springframework.web.bind.annotation, org.springframework.web.servlet”; version=”0.0.1.SNAPSHOT”
    Import-Package: javax.servlet, javax.servlet.http, org.springframework.stereotype; version=”[3.2, 4)”, org.springframework.web.bind.annotation; version=”[3.2, 4)”, org.springframework.web.servlet; version=”[3.2, 4)”
    Manifest-Version: 1.0
    Tool: Bnd-2.1.0.20130426-122213
    Webapp-Context: spring-kalees-mvc-host

    Any idea on this error ? Thanks.

    • David Valeri says:

      Likely cause is that you are missing some bundles. The example is designed to run against ServiceMix, a Karaf distribution with a lot of extra stuff pre-installed. It is likely that you are just missing some Spring JAR or have a version range incompatibility issue between what you dropped into the deploy folder and what is already in the container. Have you tried provisioning it in Karaf using the feature descriptor? Give that a try. If it works, compare that installation of Karaf to your current one and identify the differences in installed bundles.

  17. Muthu says:

    Great work. Application works fine. I am having a query,

    Step 2 : features:addurl mvn:valeri.blog.examples/spring-mvc-smx/1.1/xml/features
    Step 3 : features:install spring-mvc-smx

    Steps 2 and Step3, what it is doing and where it is pointing to ? Can you explain it little more ?

    • David Valeri says:

      Step 2 adds a Karaf feature descriptor for the example application. The descriptor is an XML file that Karaf uses to provision a collection of resources (bundles, JARs, WARs, other features) into the container as an a single action. See http://karaf.apache.org/manual/latest-2.3.x/users-guide/provisioning.html for more details.

      Step 3 provisions the example application into the container by installing the feature that describes the example application.

      If you build the application locally, mvn:valeri.blog.examples/spring-mvc-smx/1.1/xml/features resolves to a file in your local Maven repository. If you don’t build the code locally, no such feature descriptor exists in your local repository. In this latter case, Karaf/ServiceMix can locate the artifact in a public Maven repository if you configure it to do so. Add my public Maven repository URL, https://davidvaleri.googlecode.com/svn/repo/, to your Karaf/ServiceMix installation by adding the URL to the org.ops4j.pax.url.mvn.repositories property in the file located at etc/org.ops4j.pax.url.mvn.cfg under your Karaf/ServiceMix installation.

  18. kumar says:

    I am facing a problem of loading spring schemas . Error is “org.xml.sax.SAXParseException: cvc-elt.1.a: Cannot find the declaration of element ‘beans’.

    2013-09-12 12:25:36,267 | WARN | Executor: 3 | AbstractLifeCycle | ? ? | 90 – org.eclipse.jetty.util – 7.
    6.7.v20120910 | FAILED HttpServiceContext{httpContext=org.ops4j.pax.web.extender.war.internal.WebAppWebContainerContext@6db2c7}: org.springframework.beans.facto
    ry.xml.XmlBeanDefinitionStoreException: Line 15 in XML document from ServletContext resource [/WEB-INF/mvc-dispatcher-servlet.xml] is invalid; nested exception
    is org.xml.sax.SAXParseException: cvc-elt.1.a: Cannot find the declaration of element ‘beans’.
    org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 15 in XML document from ServletContext resource [/WEB-INF/mvc-dispatcher-servlet.xml
    ] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-elt.1.a: Cannot find the declaration of element ‘beans’.

  19. cristcost says:

    Hi,
    thank you for your precious example!

    I want you propose two changes:
    – The 1.1 example should use spring version 3.0.7.RELEASE as it is the version used in Servicemix 4.5.2.

    – Use Jetty’s JSTL as the dependency of JSTL from spring is not available on maven central repo. In pom.xml use:

    org.eclipse.jetty.orbit
    javax.servlet.jsp.jstl
    1.2.0.v201105211821

    and in the features.xml use:
    mvn:org.eclipse.jetty.orbit/javax.servlet.jsp.jstl/1.2.0.v201105211821

    Hope it is interesting,

    Regards,
    Cristiano

    • Ashish Arora says:

      Hi,
      I am getting following error when I am using a command via features:install spring-mvc-smx

      Error executing command: Could not start bundle mvn:org.springframework/spring-webmvc/3.0.7.RELEASE in feature(s) spring-m
      vc-smx-1.1: Unresolved constraint in bundle org.springframework.web.servlet [251]: Unable to resolve 251.0: missing requir
      ement [251.0] package; (&(package=org.springframework.beans)(version>=3.0.7)(!(version>=3.0.8)))

  20. Pingback: Authentication and authorization of a web application in Apache ServiceMix using JAAS | gusto77

  21. Martin Franz says:

    Hi David,

    Thanks for this excellent tutorial. I have worked with your code step by step an everything went fine. Even with a Spring update to 3.2.5.

    Just one question about the general concept behind spring and osgi:

    In my understanding of a modular application I would create:
    1. A parent spring war with containing the dispatcher servlet as well as security filter and so on.
    2. A bundel containing some mvc stuff
    3. Another bundle containing another mvc stuff

    Then I would access my modular controllers via:

    http://…/myparantwarcontect/urlMappingControllerA
    http://…/myparantwarcontect/urlMappingControllerB

    But this won’t work. Because the parent war lives in another context.

    Can you please point me in the right direction. I want to build a modular rest backend.

    Thanks an many regards, Martin

  22. Sulesuch123 says:

    I am getting below error on servicemix 4.5:

    HTTP ERROR 503

    Problem accessing /spring-mvc-smx/. Reason:

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/mvc]
    Offending resource: URL [bundle://209.0:0/WEB-INF/spring-mvc-smx-servlet.xml]
    Caused by:

    org.eclipse.jetty.servlet.ServletHolder$1: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/mvc]
    Offending resource: URL [bundle://209.0:0/WEB-INF/spring-mvc-smx-servlet.xml]

    at org.eclipse.jetty.servlet.ServletHolder.makeUnavailable(ServletHolder.java:417)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:463)
    at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:265)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:771)
    at org.eclipse.jetty.servlet.ServletHandler.updateMappings(ServletHandler.java:1221)
    at org.eclipse.jetty.servlet.ServletHandler.setServletMappings(ServletHandler.java:1280)
    at org.eclipse.jetty.servlet.ServletHandler.addServletMapping(ServletHandler.java:879)
    at org.ops4j.pax.web.service.jetty.internal.JettyServerImpl$1.call(JettyServerImpl.java:158)
    at org.ops4j.pax.web.service.jetty.internal.JettyServerImpl$1.call(JettyServerImpl.java:154)
    at org.ops4j.pax.swissbox.core.ContextClassLoaderUtils.doWithClassLoader(ContextClassLoaderUtils.java:60)
    at org.ops4j.pax.web.service.jetty.internal.JettyServerImpl.addServlet(JettyServerImpl.java:153)
    at org.ops4j.pax.web.service.jetty.internal.ServerControllerImpl$Started.addServlet(ServerControllerImpl.java:266)
    at org.ops4j.pax.web.service.jetty.internal.ServerControllerImpl.addServlet(ServerControllerImpl.java:107)
    at org.ops4j.pax.web.service.internal.HttpServiceStarted.registerServlet(HttpServiceStarted.java:248)
    at org.ops4j.pax.web.service.internal.HttpServiceStarted.registerServlet(HttpServiceStarted.java:219)
    at org.ops4j.pax.web.service.internal.HttpServiceProxy.registerServlet(HttpServiceProxy.java:100)
    at org.ops4j.pax.web.extender.war.internal.RegisterWebAppVisitorWC.visit(RegisterWebAppVisitorWC.java:206)
    at org.ops4j.pax.web.extender.war.internal.model.WebApp.accept(WebApp.java:583)
    at org.ops4j.pax.web.extender.war.internal.WebAppPublisher$HttpServiceListener.register(WebAppPublisher.java:170)
    at org.ops4j.pax.web.extender.war.internal.WebAppPublisher$HttpServiceListener.serviceChanged(WebAppPublisher.java:155)
    at org.ops4j.pax.web.extender.war.internal.WebAppPublisher$HttpServiceListener.serviceChanged(WebAppPublisher.java:119)
    at org.ops4j.pax.swissbox.tracker.ReplaceableService.setService(ReplaceableService.java:114)
    at org.ops4j.pax.swissbox.tracker.ReplaceableService.access$100(ReplaceableService.java:28)
    at org.ops4j.pax.swissbox.tracker.ReplaceableService$CollectionListener.serviceAdded(ReplaceableService.java:183)
    at org.ops4j.pax.swissbox.tracker.ServiceCollection$Tracker.addingService(ServiceCollection.java:181)
    at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:896)
    at org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:261)
    at org.osgi.util.tracker.AbstractTracked.trackInitial(AbstractTracked.java:184)
    at org.osgi.util.tracker.ServiceTracker.open(ServiceTracker.java:339)
    at org.osgi.util.tracker.ServiceTracker.open(ServiceTracker.java:273)
    at org.ops4j.pax.swissbox.tracker.ServiceCollection.onStart(ServiceCollection.java:139)
    at org.ops4j.pax.swissbox.lifecycle.AbstractLifecycle$Stopped.start(AbstractLifecycle.java:121)
    at org.ops4j.pax.swissbox.lifecycle.AbstractLifecycle.start(AbstractLife

  23. Sulesuch123 says:

    I have solved the problem by making the below changes in feature.xml:

    spring
    spring-dm-web
    mvn:org.springframework.osgi/spring-osgi-core/1.2.0
    http://repo1.maven.org/maven2/org/apache/servicemix/nmr/org.apache.servicemix.nmr.api/1.0.0-m2/org.apache.servicemix.nmr.api-1.0.0-m2.jar
    mvn:org.springframework.osgi/spring-osgi-io/1.2.0
    wrap:mvn:org.springframework/spring-beans/3.2.6.RELEASE$Bundle-SymbolicName=org.springframework&Bundle-Version=3.2.6
    mvn:org.apache.cxf/cxf-rt-transports-http/2.6.9
    wrap:mvn:org.springframework/spring-web/3.2.6.RELEASE$Bundle-SymbolicName=springframework-spring-web&Bundle-Version=3.2.6

    war
    mvn:org.springframework.osgi/spring-osgi-web/1.2.1
    mvn:org.springframework/spring-web/3.0.7.RELEASE
    mvn:org.springframework/spring-webmvc/3.0.7.RELEASE

    mvn:javax.servlet/com.springsource.javax.servlet.jsp.jstl/1.2.0.v20110728
    mvn:${project.groupId}/spring-mvc-smx/${project.version}/war

    and did a maven clean install .

  24. Yong.Li says:

    Hello, your code can not be downloaded from Google, but it is very important to me, you can send me a separate it? Thank you! my mail address is: 1079839968@qq.com

Leave a comment