Spring Boot MongoDB REST - Custom Repository Methods - spring

I'm using spring-boot-starter-data-rest and spring-boot-starter-data-mongodb as per the tutorial given here. I'm attempting to create a custom method for a MongoRepository, but having no success.
I've followed the steps given for adding custom behavior to single repositories, but I'm getting a 404 error when I attempt to access the custom method. Hitting the parent endpoint also shows no sign of the method, but I didn't expect it to do that.
Help me, please! What am I doing wrong!?
My code is as follows:
Application.java
#SpringBootApplication
public class Application extends AbstractMongoConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ItemRepositoryCustom.java
public interface ItemRepositoryCustom {
void customMethod();
}
ItemRepositoryImpl.java
public class ItemRepositoryImpl {
#Override
public void customMethod() {
...
}
}
ItemRepository.java
#RepositoryRestResource(collectionResourceRel="items", path="items")
public interface ItemRepository extends MongoRepository<Item, String>, ItemRepositoryCustom {
List<Item> findByName(#Param("name") String name);
}

Change your custom implementation class name from ItemRepositoryImpl to ItemRepositoryCustomImpl.
I followed spring documentation here.
Cheers!

Related

how can application yaml value inject at runtime in spring boot?

I want to change the value of application.yaml at loading time.
ex) application.yaml
user.name: ${name}
Here, I want to put this value by calling an external API such as a vault, rather than a program argument when the jar is executed with the name value.
First of all, I think I need to write code that implements EnvironmentPostProcessor and calls external API, but I don't know how to inject that value. can I get help?
public class EnvironmentConfig implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// API CAll
// how can inject yaml value??
}
}
I don't know which way to orient myself.
OPTION 1: doing it via EnvironmentPostProcessor:
assuming you have registered you EnvironmentPostProcessor in /resources/META-INF/spring.factories file:
org.springframework.boot.env.EnvironmentPostProcessor=package.to.environment.config.EnvironmentConfig
all you need is to add your custom PropertySource:
public class EnvironmentConfig implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
environment.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
}
}
public class CustomPropertySource extends PropertySource<String> {
public CustomPropertySource(String name) {
super(name);
}
#Override
public Object getProperty(String name) {
if (name.equals("name")) {
return "MY CUSTOM RUNTIME VALUE";
}
return null;
}
}
OPTION 2: doing it via PropertySourcesPlaceholderConfigurer:
A class that is responsible for resolving these palceholders is a BeanPostProcessor called PropertySourcesPlaceholderConfigurer (see here).
So you could override it and provide you custom PropertySource that would resolve your needed property like so:
#Component
public class CustomConfigurer extends PropertySourcesPlaceholderConfigurer {
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, ConfigurablePropertyResolver propertyResolver) throws BeansException {
((ConfigurableEnvironment) beanFactoryToProcess.getBean("environment"))
.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
super.processProperties(beanFactoryToProcess, propertyResolver);
}
}
use ConfigurationProperties for your properties and change it via an api like this:
#Component
#ConfigurationProperties(prefix = "user")
public class AppProperties {
private String name;
//getter and setter
}
#RestController
public class AppPropertiesController {
#Autowire
AppProperties prop;
#PostMapping("/changeProp/{name}")
public void change(#PathVariable String name){
prop.setName(name);
}
}

Spring Boot #Component doesn't create Beans

Since according to the docs #Component registers beans for the Spring container I'm trying to create a simple example of dependency injection using the following code:
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
#Autowired
private static Building building;
public static void main(String[] args) {
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.facade;
//...
#Component
public class Building {
private HeatingService service;
private Long buildingSize;
#Autowired
public Building(HeatingService service) {
this.service = service;
}
public Double monthlyHeatingCost() {
return service.getMonthlyHeatingCost(buildingSize);
}
// getters & setters...
}
package pl.playground.service;
public interface HeatingService {
Double getMonthlyHeatingCost(Long size);
}
package pl.playground.service;
//...
#Component
public class HeatingServiceImpl implements HeatingService {
private final Double CUBIC_PRICE = 2.3;
public HeatingServiceImpl() {}
#Override
public Double getMonthlyHeatingCost(Long size) {
return size * CUBIC_PRICE;
}
}
It builds and runs, but there is a NullPointerException at building.setBuildingSize(12L);. However the one below works without any issues:
//PlaygroundApplication.java
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.config;
//...
#Configuration
public class Config {
#Bean
public Building building(HeatingService service) {
return new Building(service);
}
#Bean
public HeatingServiceImpl heatingServiceImpl() {
return new HeatingServiceImpl();
}
}
The rest is the same as before.
Why is #Component not creating Beans?
It is working the way I think it should when used inside a #Controller of a web app, does that make a difference? How does exactly #Bean and #Component differ?
What am I failing to understand?
EDIT
Consider the following scenario:
package pl.playground;
//...
#SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
package pl.playground.controller;
//...
#Controller
public class Controller {
private Facade facade;
#Autowired
public Controller(Facade facade) {
this.facade = facade;
}
#GetMapping("/")
public String getIndexPage(Model model) {
return "index";
}
}
package pl.playground.facade;
//...
#Component
public class Facade {
private PostsService postService;
private UserService userService;
private TagService tagService;
#Autowired
public Facade(PostsService retrieve, UserService user, TagService tag) {
this.postService = retrieve;
this.userService = user;
this.tagService = tag;
}
//...
}
I don't need #Configuration here for it to work. That's my concern.
The problem with your code is that you are trying to #Autowire on a static field. You simply cannot do that. Look here: Can you use #Autowired with static fields?
It fails to work because the PlaygroundApplication class is not being created and managed by spring. The injection works only inside instances managed by spring. You can treat class annotated with #SpringBootApplication as configuration classes. Spring creates instances of those classes and injection works inside them but only on instance fields.
The second example shows the correct way to access spring beans from main method of the application.
Well. I used your original question and is working without any issues. #cezary-butler pointed out in the comments you can autowire into PlaygroundApplication but you can get hold of it easily in the static main method using context.getBean(Building.class)
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(PlaygroundApplication.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
Here is the sample repo https://github.com/kavi-kanap/stackoverflow-63072236
TLDR;
A Spring context needs to be created before any bean can be injected. In the first scenario, just the fact of having a #SpringBootApplication decorator does not ensure a context in the scope of the class it decorates.
SpringApplication.run(ExampleApplication.class, args); instantiates a context (and e.g. a web server among other things)
var context = new AnnotationConfigApplicationContext(Config.class); instantiates a scoped context
Thus the first example had null inside of Building as there was no context with the bean to inject.

How to pick spring option arguments at run time in java class

I want to know how to pick spring option arguments like
--server.port , --spring.config.name
in a java class.
Basically I want to know the value of this argument at run time to load some property
You can access them in your application's main() method. A great blog about this topic covers it in detail. Following is how you can do it.
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
for(String arg:args) {
System.out.println(arg);
}
SpringApplication.run(Application.class, args);
}
}
Please try using spring org.springframework.core.env.Environment,
public class MyService {
#Autowired
private Environment env;
public String getPropertyValue(String key) {
return env.getProperty(key);
}
}
OR
In application-<env>.propeties (if using spring.profiles)
else application.properties
myapp.property=007
In your class :
#Value("${myapp.property}")
private String myProperty;

Spring Boot insert sample data into database upon startup

What is the right way for creating test data upon server startup and inserting them into the database (I'm using a JPA/JDBC backed Postgres instance).
Preferably in form of creating Entities and having them persisted through a Repository interface rather than writing plain SQL code. Something like RoR's Rake db:seed helper.
If the framework exposes a hook for doing stuff when all the beans have been injected and the database is ready, that could also work.
You can catch ApplicationReadyEvent then insert demo data, for example:
#Component
public class DemoData {
#Autowired
private final EntityRepository repo;
#EventListener
public void appReady(ApplicationReadyEvent event) {
repo.save(new Entity(...));
}
}
Or you can implement CommandLineRunner or ApplicationRunner, to load demo data when an application is fully started:
#Component
public class DemoData implements CommandLineRunner {
#Autowired
private final EntityRepository repo;
#Override
public void run(String...args) throws Exception {
repo.save(new Entity(...));
}
}
#Component
public class DemoData implements ApplicationRunner {
#Autowired
private final EntityRepository repo;
#Override
public void run(ApplicationArguments args) throws Exception {
repo.save(new Entity(...));
}
}
Or even implement them like a Bean right in your Application (or other 'config') class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner demoData(EntityRepository repo) {
return args -> {
repo.save(new Entity(...));
}
}
}
From Spring documentation: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-database-initialization
Initialize a database using Hibernate
A file named import.sql in the root of the classpath will be executed on startup if Hibernate creates the schema from scratch (that is if the ddl-auto property is set to create or create-drop). This can be useful for demos and for testing if you are careful, but probably not something you want to be on the classpath in production. It is a Hibernate feature (nothing to do with Spring).
You can do like this
#SpringBootApplication
public class H2Application {
public static void main(String[] args) {
SpringApplication.run(H2Application.class, args);
}
#Bean
CommandLineRunner init (StudentRepo studentRepo){
return args -> {
List<String> names = Arrays.asList("udara", "sampath");
names.forEach(name -> studentRepo.save(new Student(name)));
};
}
}

How to use guice-servlet with Jersey 2.0?

Is there any sample code demonstrating how to use guice-servlet with Jersey 2.0?
https://github.com/Squarespace/jersey2-guice seems to be the first genuine Guice integration for Jersey 2 but it requires version 2.11+.
NOTE: I haven't tested this, but the idea is sound.
Yes, I've adapted an example and it's available here - https://github.com/piersy/jersey2-guice-example-with-test
I've updated the example code now, its got a test using jetty and another using tomcat.
There is a page at HK2 official about correct guice implementation: https://javaee.github.io/hk2/guice-bridge.html
You should create your Injector something like this:
public class GuiceConfig extends ResourceConfig {
#Inject
public GuiceConfig(ServiceLocator serviceLocator) {
this();
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(GuiceListener.createBiDirectionalGuiceBridge(serviceLocator));
}
public GuiceConfig() {
packages(Injections.packages);
addProperties(Injections.propertiesMap);
}
}
And code from the doc should be upgraded like:
#WebListener
public class GuiceListener extends GuiceServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
Locale.setDefault(Locale.ENGLISH);
super.contextInitialized(servletContextEvent);
}
public static volatile Injector injector = null;
#Override
protected Injector getInjector() {
return injector;
}
#SuppressWarnings("unchecked")
private static Module getModule() {
return binder -> {
Injections.singletonInterfaces.forEach((i, c) -> binder.bind(i).to(c).in(Scopes.SINGLETON));
Injections.singletonClasses.forEach(c -> binder.bind(c).in(Scopes.SINGLETON));
};
}
static synchronized Injector createBiDirectionalGuiceBridge(ServiceLocator serviceLocator) {
return GuiceListener.injector = createBiDirectionalGuiceBridge(serviceLocator, getModule());
}
}
Using the maven dependency at your pom.xml
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>2.3.0</version>
</dependency>
https://github.com/phxql/jersey2-guice doesn't work with jersey 2.22 and guice 4.0.
This is a minimum working PoC which wires Jersey 2 and Guice together:
https://github.com/phxql/jersey2-guice
I've already done in this sample:
https://github.com/jbescos/tododev
You have to register the class https://github.com/jbescos/tododev/blob/master/jersey2-guice/src/main/java/es/tododev/rest/ApplyGuiceContextFilter.java in your ResourceConfig, and the guice injector binded in an AbstractModule.
#Provider
#PreMatching
public class ApplyGuiceContextFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Inject
public ApplyGuiceContextFilter(ServiceLocator serviceLocator, Injector injector) {
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
}
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
}
#Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws IOException {}
}
This is the ResouceConfig:
public class RestConfig extends ResourceConfig {
#Inject
public RestConfig() {
this(Guice.createInjector(new Module(){
#Override
public void configure(Binder arg0) {
// TODO Auto-generated method stub
}
}));
}
// Test
public RestConfig(Injector injector) {
packages(ResourceSample.class.getPackage().getName());
register(ApplyGuiceContextFilter.class);
register(new LoggingFilter(Logger.getLogger(LoggingFilter.class.getName()), true));
property(ServerProperties.TRACING, "ALL");
register(new RestBinder(injector));
}
private static class RestBinder extends AbstractBinder{
private final Injector injector;
private RestBinder(Injector injector){
this.injector = injector;
}
#Override
protected void configure() {
bind(injector).to(Injector.class);
}
}
}
GWizard includes a module that gives you out-of-the-box integration between Jersey2 and Guice. Here's an example of a complete JAX-RS service:
public class Main {
#Path("/hello")
public static class HelloResource {
#GET
public String hello() {
return "hello, world";
}
}
public static class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(HelloResource.class);
}
}
public static void main(String[] args) throws Exception {
Guice.createInjector(new MyModule(), new JerseyModule()).getInstance(WebServer.class).startJoin();
}
}
Note that this is based on the Squarespace jersey2-guice adapter, which may not function properly with future point releases of Jersey. GWizard also offers a RESTEasy JAX-RS module, which is preferred.
Here is a blog entry about this that might help: http://blorn.com/post/107397841765/guice-and-jersey-2-the-easy-way
For those interested, there is a sample of guice/jersey integration available at https://github.com/mycom-int/jersey-guice-aop.
Here is an example using Embedded Jetty (it should probably work for Jetty server too)
jetty-jersey-HK2-Guice-boilerplate
If you are planning to use Guice for your application, all Guice components injected into Jersey need to be declared as a binding in the Guice config.
If you don't want to declare every binding in Guice config, there is an adapter here:
guice-bridge-jit-injector

Resources