Spring Frameworkmapping

A library for building overridable Spring Web endpoints

License

License

Categories

Categories

Leaf Data Databases
GroupId

GroupId

org.broadleafcommerce
ArtifactId

ArtifactId

spring-frameworkmapping
Last Version

Last Version

0.9.0-GA
Release Date

Release Date

Type

Type

jar
Description

Description

Spring Frameworkmapping
A library for building overridable Spring Web endpoints
Project URL

Project URL

https://github.com/BroadleafCommerce/spring-frameworkmapping
Source Code Management

Source Code Management

https://github.com/BroadleafCommerce/spring-frameworkmapping

Download spring-frameworkmapping

How to add to project

<!-- https://jarcasting.com/artifacts/org.broadleafcommerce/spring-frameworkmapping/ -->
<dependency>
    <groupId>org.broadleafcommerce</groupId>
    <artifactId>spring-frameworkmapping</artifactId>
    <version>0.9.0-GA</version>
</dependency>
// https://jarcasting.com/artifacts/org.broadleafcommerce/spring-frameworkmapping/
implementation 'org.broadleafcommerce:spring-frameworkmapping:0.9.0-GA'
// https://jarcasting.com/artifacts/org.broadleafcommerce/spring-frameworkmapping/
implementation ("org.broadleafcommerce:spring-frameworkmapping:0.9.0-GA")
'org.broadleafcommerce:spring-frameworkmapping:jar:0.9.0-GA'
<dependency org="org.broadleafcommerce" name="spring-frameworkmapping" rev="0.9.0-GA">
  <artifact name="spring-frameworkmapping" type="jar" />
</dependency>
@Grapes(
@Grab(group='org.broadleafcommerce', module='spring-frameworkmapping', version='0.9.0-GA')
)
libraryDependencies += "org.broadleafcommerce" % "spring-frameworkmapping" % "0.9.0-GA"
[org.broadleafcommerce/spring-frameworkmapping "0.9.0-GA"]

Dependencies

compile (3)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter jar 2.0.9.RELEASE
org.springframework : spring-webmvc jar
org.springframework.boot : spring-boot-autoconfigure-processor Optional jar 2.0.9.RELEASE

provided (2)

Group / Artifact Type Version
javax.servlet : javax.servlet-api jar 3.1.0
com.google.code.findbugs : jsr305 jar 3.0.2

test (3)

Group / Artifact Type Version
org.junit.jupiter : junit-jupiter jar
org.springframework.boot : spring-boot-starter-test jar 2.0.9.RELEASE
org.springframework.boot : spring-boot-test-autoconfigure jar 2.0.9.RELEASE

Project Modules

There are no modules declared in this project.

Spring Frameworkmappings

badge

Spring Frameworkmappings is a support library for developers distributing libraries to their users. This enables library developers to distribute overridable Spring MVC request mappings that will not collide with user-defined @RequestMapping, allowing for customizability and extensibility. Spring-frameworkmappings is tested for compatibility with Spring 5.0+ and Spring Boot 2.0+ but would likely work on older versions of Spring/Spring Boot.

Here’s an example:

@FrameworkController
public class LibraryController {

  @ResponseBody
  @FrameworkGetMapping("/test")
  public String test() {
    return "library";
  }
}
...
...
...
@Controller
public class UserController {

  @ResponseBody
  @GetMapping("/test")
  public String test() {
    return "user";
  }
}

With both the @Controller and @FrameworkController active, hitting the /test endpoint will return user. But if a corresponding /test @RequestMapping was not defined, the value returned would be library.

Usage

Add the dependency with Maven:

<dependency>
  <groupId>org.broadleafcommerce</groupId>
  <artifactId>spring-frameworkmapping</artifactId>
  <version>$currentversion</version>
</dependency>

Or Gradle

dependencies {
  compile 'org.broadleafcommerce:spring-frameworkmapping:$currentversion'
}

Then use @FrameworkController and the corresponding @FrameworkMapping just like you would an @Controller and @RequestMapping. Example:

@FrameworkController
public void DefaultedController {

    @FrameworkRequestMapping("/test")
    public String getAString() {
        return "path/to/template";
    }
}

Then activate the controllers just like you would an @ComponentScan, but with @FrameworkControllerScan

@Configuration
@FrameworkControllerScan(basePackageClasses = DefaultedController.class)
public void ControllerConfig {
}
@FrameworkControllerScan utilizes a composition of @ComponentScan and as such cannot be specified alongside a class that is annotated with another @ComponentScan or an annotation that is composed of @ComponentScan (such as @SpringBootApplication). Instead, created a static nested class inside of that @Configuration class that instruments the scan

If you do not want to do a scan, you can annotate individual @Bean methods:

@Configuration
public class LibraryAutoConfiguration {

  @Bean
  @FrameworkController
  public DefaultedController defaultController() {
    return new DefaultedController();
  }
}

Convenience Annotations

Convenience annotations also exist for specific request methods:

@FrameworkController
@RequestMapping("/test")
public void DefaultedController {

    @GetMapping("/get")
    public @ResponseBody String getAString() {
        return "Success!";
    }
}

Or as a correlary to @RestController-, use @FrameworkRestController:

@FrameworkRestController
@FrameworkRequestMapping("/test")
public void DefaultedController {

    @FrameworkGetMapping("/get")
    public @ResponseBody String getAString() {
        return "Success!";
    }
}

Building URIs

To build a URI

@FrameworkRestController
public class DefaultTestController {

  @FrameworkGetMapping("/test/{pathvar}")
  public ResponseEntity test(@PathVariable("pathvar") String variable) {
    return ResponseEntity.of("Success!");
  }
}

Build a URI with the same sort of patterns of MvcUriComponentsBuidler:

FrameworkMvcUriComponentsBuilder.fromMethodCall(
    on(DefaultTestController.class).test("value"))
        .build()
        .toUri()

Test Support with @WebMvcTest

Since the use case for this library is for distributing other libraries, make sure that you have a spring.factories entry that corresponds to the @AutoConfigureWebMvc test slice:

org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc=\
    com.mycompany.mylibrary.package.MyControllerAutoConfiguration

If you are scanning your framework controllers @WebMvcTest, the controller might not be available in your ApplicationContext from the component scan. This needs to be manually enabled in an @WebMvcTest.

ℹ️
Using manual @Bean methods annotated with @FrameworkController eliminates this issue

To enable a single controller, use the controllers attribute of @WebMvcTest:

@WebMvcTest(controllers = TestController.class)
@ExtendWith(SpringExtension.class)
public class ControllerTest {

    @Configuration
    class Config {
        @Bean
        public TestController testController() {
            return new TestController();
        }
    }

    @FrameworkController
    public class TestController {

        @GetMapping("/test")
        public String test() {
            return "Success!";
        }
    }

    @Autowired
    MockMvc mockMvc;

    @Test
    public void controllersWork() throws Exception {
        mockMvc.perform(get("/test"))
            .andExpect(status().isOk());
    }

}

If you want to enable a group of @FrameworkMapping-annotated controllers use includeFilters:

@WebMvcTest(includeFilters = @Filter(FrameworkController.class))
@FrameworkControllerScan
@ExtendWith(SpringExtension.class)
public class ControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void controllersWork() throws Exception {
        mockMvc.perform(get("/test"))
                .andExpect(status().isOk());
    }

}

@FrameworkController
public class TestController {

  @GetMapping("/test")
  public String test() {
    return "Success!";
  }
}
ℹ️
@FrameworkController`s that are scanned using `@FrameworkControllerScan within a test class will not be picked up. This is because of the exclusions within TestTypeExcludeFilter. Remediations are to either move your @FrameworkController to a class outside of a test class, or manually create it with @Bean

Other use Cases

Excluding Controllers in a Scan

In the event you want to enable framework controllers, but want to exclude particular framework controllers, you can leverage the excludeFilters property of the @FrameworkControllerScan. For example:

@FrameworkControllerScan(basePackages = "com.mypackage.packagewithcontrollers",
  excludeFilters = {
    @Filter(value = DefaultCustomerController.class, type = FilterType.ASSIGNABLE_TYPE),
    @Filter(value = DefaultOrderController.class, type = FilterType.ASSIGNABLE_TYPE)
})

Including a Single Controller

If you only want a small number of framework controllers enabled, it would be easier to declare the ones you want as beans instead of listing a large number of controllers using excludeFilters.

For example, you can activate a single framework controller in an @Configuration class like so:

@Bean
public DefaultCartController defaultCartController() {
    return new DefaultCartController();
}

Alternatively, you may utilize includeFilters of @FrameworkControllerScan and override its value to include just a few controllers:

@FrameworkControllerScan(basePackages = "com.mypackage.packagewithcontrollers",
  includeFilters = {
    @Filter(value = DefaultCustomerController.class, type = FilterType.ASSIGNABLE_TYPE),
    @Filter(value = DefaultOrderController.class, type = FilterType.ASSIGNABLE_TYPE)
})

Extending default mappings

Or if you want to call super, you could extend the default framework controller as well like so:

@RestController
@RequestMapping("/cart")
public class MyCartController extends DefaultCartController {
    @RequestMapping(path = "/get", method = RequestMethod.GET)
    public MyCart getActiveCart() {
        Cart cart = super.getActiveCart();
        return doCustomThingsToCart(cart);
    }
}

Changing a Mapping

If you want to alter the URL for some mapping, you can do so by defining your own mapping and calling super.

For example, given the framework controller:

@FrameworkRestController
@FrameworkMapping("/cart")
public class DefaultCartController {
    @FrameworkMapping(path = "/get", method = RequestMethod.GET)
    public Cart getActiveCart() {
        return cartService.getActiveCart();
    }
}

You can change the mapping by extending the framework controller, and calling super with a new mapping:

@RestController
@RequestMapping("/cart")
public class MyCartController extends DefaultCartController {
    @RequestMapping(path = "/retrieve", method = RequestMethod.GET)
    public Cart getActiveCart() {
        return super.getActiveCart();
    }
}

Now we’ve created a new mapping /cart/retrieve, but note that /cart/get will still be registered.

Changing a Mapping and Functionality

This is achieved by simply applying both patterns above.

Removing a Mapping

If you want to remove (disable) particular a @FrameworkMapping then you’ll need to create a @RequestMapping method with the same URL that returns a 404 error.

For example, to disable /cart/get:

@RestController
@RequestMapping("/cart")
public class MyCartController extends DefaultCartController {
    @RequestMapping(path = "/get", method = RequestMethod.GET)
    public ResponseEntity getActiveCart() {
        return new ResponseEntity(HttpStatus.NOT_FOUND);
    }
}

Appendix

Supporting Classes

Class Name Description

FrameworkControllerHandlerMapping

Component that registers controllers annotated with @FrameworkController and @FrameworkRestController

FrameworkMvcUriComponentsBuilder

Copied from MvcUriComponentsBuilder in order to provide URI building functionality for @FrameworkMapping annotations. It replicates the functionality of MvcUriComponentsBuilder

Snapshots

Snapshots are deployed to the Maven Central Snapshots repository and is deployed on every commit. Add it to your <repositories> like so:

<repositories>
  <repository>
    <id>mavencentral-snapshots</id>
     <url>https://oss.sonatype.org/content/repositories/snapshots</url>
     <snapshots>
       <enabled>true</enabled>
     </snapshots>
     <releases>
       <enabled>false</enabled>
     </releases>
  </repository>
</repositories>
org.broadleafcommerce

Broadleaf Commerce

Versions

Version
0.9.0-GA
0.1.0-GA