I've built an application composed of angularJs for the client side and spring MVC for the backend side. My purpose is to expose my language properties to the client side, so that I can use them in my JS files. To do that, I've used the library angular-translate.min.js and followed thoses instructions:
(https://gist.github.com/rvillars/6422287).
Basically the idea is to create a custom class which extends from ReloadableResourceBundleMessageSource, and override getAllProperties(Locale locale) method. Then, injecting that custom class in a #controller, which will return the list of properties in a Json format to the client.
So far my issue is that I haven't been able to autowired my custom class extending ReloadableResourceBundleMessageSource, in my #controller. My app always crashing during the deployment phase with "No qualifying bean of type CustomResourceBundleMessageSource".
My environment:
Spring MVC 3.2.3
Angular JS
Please find the code:
Controller class:
#Controller
#RequestMapping("/messageBundle")
public class SerializableMessageBundleController {
#Autowired
private CustomResourceBundleMessageSource messageSource;
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public Properties list(#RequestParam String lang) {
return messageSource.getAllProperties(new Locale(lang));
}
Custom message source:
public class CustomResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
public Properties getAllProperties(Locale locale) {
clearCacheIncludingAncestors();
PropertiesHolder propertiesHolder = getMergedProperties(locale);
Properties properties = propertiesHolder.getProperties();
return properties;
}
}
}
ApplicationContext.xml:
<bean id="messageSource" class="com.contgo.CustomResourceBundleMessageSource">
<qualifier value="messageSource"/>
<property name="basename" value="LanguageResources"/>
</bean>
I've tried to add #qualifier("messageSource") as well but doesn't work.
Also try #Resource and #Inject, doesn't work either.
Has anyone ever managed to do that?
I had the same problem, and finally got it working as follows.
The class extending ReloadableResourceBundleMessageSource is correct.
All you need to do is configure it properly in your applicationContext file.
You leave the original ReloadableResourceBundleMessageSource configuration alone:
<bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="messageSource">
<property name="basename" value="LanguageResources"/>
</bean>
Then you add the following below it, using the 'parent' attribute to refer to the original config:
<bean class="com.contgo.CustomResourceBundleMessageSource" id="myCustomMessageSource" parent="messageSource"/>
Then finally, in your controller you can autowire it like this:
#Autowired
CustomResourceBundleMessageSource myCustomMessageSource;
Related
For example I have an entity of Users consisting of username,phonenumber and address.
I want to read all these fields from a csv file and store it in the respective table in the database?
Can any one Help me by describing how to do that? Or is there any documentation on how to do that?
I assume that you want the user to upload the file from some UI. Depending on the exact way in which you build UI, you might:
Send a multipart HTTP POST request (mime type = multipart/form-data; see What should a Multipart HTTP request with multiple files look like?)
Send a simple POST request with the body directly containing the file contents.
Either of the two can be fairly easily solved using Spring.
Assuming that we have the following entity:
#Data
#Entity
public class User {
#Id
private String username;
private String phoneNumber;
private String address;
}
And we define a Spring Data repository for accessing the database:
public interface UserRepository extends JpaRepository<User, String> {
}
For the CSV deserialization, I would propose using Jackson. Spring Boot already comes with Jackson, but we need to add a data format extension for CSV in your pom:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
This way, we can create a simple utility method that knows to read a CSV for a given POJO class:
public class CsvUtils {
private static final CsvMapper mapper = new CsvMapper();
public static <T> List<T> read(Class<T> clazz, InputStream stream) throws IOException {
CsvSchema schema = mapper.schemaFor(clazz).withHeader().withColumnReordering(true);
ObjectReader reader = mapper.readerFor(clazz).with(schema);
return reader.<T>readValues(stream).readAll();
}
}
And then we create a simple Rest Controller for handling the upload(s):
#RestController
#RequiredArgsConstructor
public class UserController {
private final UserRepository repository;
#PostMapping(value = "/upload", consumes = "text/csv")
public void uploadSimple(#RequestBody InputStream body) {
repository.saveAll(CsvUtils.read(User.class, body));
}
#PostMapping(value = "/upload", consumes = "multipart/form-data")
public void uploadMultipart(#RequestParam("file") MultipartFile file) {
repository.saveAll(CsvUtils.read(User.class, file.getInputStream()));
}
}
In case you also need some HTML for doing the upload, the following snippet is a minimal working example:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
<input type="submit" name="submit" value="Submit" />
</form>
Later edit: If you want to also validate the incoming data, first annotate your entity class attribute with javax.validation constraints. For example:
#Data
#Entity
class User {
#Id
#Email
#NotEmpty
private String username;
#Pattern(regexp = "[0-9 ()-]{4,12}")
private String phoneNumber;
private String address;
}
Then you can chose where do perform the actual validation call:
Service level. This is what I personally recommend in this case, as it is fairly easy to setup and would perform the validations early enough. For this you introduce a simple #Service class between the controller and the repository.
#Service
#Validated
#RequiredArgsConstructor
class UserService {
private final UserRepository repository;
public void saveAll(#Valid List<User> users) {
repository.saveAll(users);
}
}
You would then use this service class instead of the repository inside the controller class.
Repository level: here you don't actually need to do anything. If you annotate your entity classes with validation constraints, Hibernate would automatically call the validation in a pre-insert listener (BeanValidationEventListener).
Controller level. This is trickier to setup. Move the CSV deserialization in a custom HttpMessageConverter. You should also add this converter to the FormHttpMessageConverter (such that it can use it to deserialize a part of the multi-part request). You could then theoretically just declare the #Valid List<User> as inputs for your controller methods and Spring would automatically call the message converter based on the mime type and then call the validator. See Add JSON message converter for multipart/form-data for an example.
Lastly you can always manually call the validation whenever you want: Manually call Spring Annotation Validation.
You can achieve that easily with openCSV.
For a known POJO User, you just map the CSV columns(headers in your case) to corresponding fields in the POJO.
Just add the following to you dependency, check for the latest version for your application though.
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>4.1</version>
</dependency>
this link guides you through https://www.geeksforgeeks.org/mapping-csv-to-javabeans-using-opencsv/
I am using camel-cdi and it is injecting the CamelContext, detecting all the routes in project.
But I want a CamelContext with a registry because I have some components that I use in camel routes like shown below.
SimpleRegistry registry = new SimpleRegistry();
registry.put("actionProcessor", actionProcessor);
registry.put("jpa", jpaComponent);
registry.put("jtaTransactionManager", platformTransactionManager);
CamelContext camelContext = new DefaultCamelContext(registry);
When I inject CamelContext the components like actionProcess, jpa are not recognized. when in my Route I have
.to("bean:actionProcessor?method=myMethod(${body})")
but my bean does not get executed.
I documentation I read use # before components name which are in registry but still it is not working.
Please suggest how can I achieve this using camel-cdi.
Did you already try with creating a CdiCamelContext (a subclass of DefaultCamelContext) ?
Otherwise, a more elegant would be to annotate your various classes, eg:
#Named("actionProcessor")
public class MyActionProcessor{
...
}
We have been using this for years without any problem
public class ContextFactory {
#Produces
#ApplicationScoped
#ContextName("Demo")
static final CamelContext createContext() {
CdiCamelContext context = new CdiCamelContext();
context.setStreamCaching(true);
context.disableJMX();
return context;
}
}
#ContextName("Demo")
public class MyRouteBuilder extends RouteBuilder {
from("...")
.to("bean:actionProcessor?method=myMethod")
}
#Named("actionProcessor")
public class MyActionProcessor{
public void myMethod(#Body String body) {}
}
Of course, in order to work, you need to activate the JEE bean discovery (=add a "beans.xml" file in META-INF or WEB-INF) !
Normally we are deploying our camel/blueprint based services once. Each service has its own property placeholder and the camel-context is bound to it:
<cm:property-placeholder id="service-name.placeholder" persistent-id="service-name.blueprint">
<cm:default-properties>
...
</cm:default-properties>
</cm:property-placeholder>
...
<camelContext id="service-name-service-camel" xmlns="http://camel.apache.org/schema/blueprint"
useMDCLogging="true">
<propertyPlaceholder id="properties" location="blueprint:service-name.placeholder" />
<routeBuilder ref="mainRoute"/>
</camelContext>
Now we created a service that we want deploy multiple times. Each instance should use its own set of property-values.
The only way i see is to set the property placeholder name on compile time (maven filter) but this will result in different artifacts - bad.
Is there a way to set the property placeholder to be used on runtime or start time?
You can do it with ManagedServiceFactory and a few lines of code.
Define a bean for the Factory, and inject the BundleContext. Chose a Pid to later identify and configure this Factory:
<bean id="myServiceFactory" class="org.my.MyServiceFactory" init-method="init" destroy-method="destroy">
<property name="bundleContext" ref="blueprintBundleContext"/>
<property name="configurationPid" value="org.my.pid"/>
</bean>
Implement the service Factory (not working code, just to give you an idea):
public class MyServiceFactory implements ManagedServiceFactory {
private BundleContext bundleContext;
private String configurationPid;
public void setConfigurationPid(String configurationPid) {
this.configurationPid = configurationPid;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public void init() {
// your setup goes here
}
public void destroy() {
// your shutdown logic goes here
}
#Override
public String getName() {
return configurationPid;
}
#Override
public void updated(String pid, Dictionary dict) throws ConfigurationException {
// Instantiate each service with its own properties
MyServiceImpl service = new MyServiceImpl(dict);
Dictionary servProps = new Properties();
servProps.put("custom.service.property", "an id or someting")
bundleContext.registerService(MyServiceImpl.class.getName(), service, servProps);
// save your servicereferences to unregister, eg in a map
// you can customize your service by giving some property to later retrieve it
}
#Override
public void deleted(String pid) {
// get the ServiceReference from some map
servicereference.unregister();
}
}
The ManagedServiceFactory has one method init() to setup all required resources, a destroy() method to clean up (for example by unregistering all the services).
A new service instance is created for each config file in etc/org.my.pid-*.cfg, for example to create 3 instances of a service:
etc/org.my.pid-serviceinstance1.cfg
etc/org.my.pid-serviceinstance2.cfg
etc/org.my.pid-whatever.cfg
To get a particular instance of the service, register them with some custom property (like custom.service.property in my example). Then in a consumer bundle ask for an instance of MyService with custom.service.property = serviceinstance2 and you're done.
You can even create new CamelContexts this way. There is a complete tutorial on PacktPub website.
edit:
When you write a new file in etc/org.my.pid-* the updated() method is called and a new service deployed. When a file is removed, the deleted() method is called and you must destroy and unregister the service. Of course, you can add/delete/modify files with JBoss/Karaf running :-) without stopping the main bundle.
I extended WebMvcConfigurationSupport to implement an api versioning scheme - i.e.
#Configuration
public class ApiVersionConfiguration extends WebMvcConfigurationSupport {
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping(readDateToVersionMap());
}}
This uses a custom handler mapping to version the api and works quite nicely.
However it also seems to disable the #EnableAutoConfiguration bean so that now static resources aren't served (as mentioned in this question Is it possible to extend WebMvcConfigurationSupport and use WebMvcAutoConfiguration?).
Ok, I thought, let's just add a resource handler to the class above - i.e.
#Configuration
public class ApiVersionConfiguration extends WebMvcConfigurationSupport {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("classpath:/public/").addResourceLocations("/");
}
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping(readDateToVersionMap());
}}
However.. this isn't working..? I get this error when I browse to /index.html:
No mapping found for HTTP request with URI [/index.html] in DispatcherServlet with name 'dispatcherServlet'
..If I disable this class then these resources are served just fine by #EnableAutoConfiguration magic.
I've been playing with various options to serve static content having extended the WebMvcConfigurationSupport and thus far no success.
Any ideas?
I was facing the same problem and came up with a solution that just works for me. If you just want to get the resources working without worrying of repetition you can do:
#Configuration
public class StaticResourcesConfig extends WebMvcAutoConfigurationAdapter {
}
and then
#Configuration
#EnableWebMvc
#Import(StaticResourcesConfig.class)
public class WebConfig extends WebMvcConfigurationSupport {
...
}
This successfully uses the Spring Boot defaults for serving static resources, as long as you don't map /** in your controllers.
I have a Spring controller defined like this with 2 request mappings, one using localDAO and the other using dependencyDAO. LocalDAO classes exist in my project and DependencyDAO classes are imported via maven dependency:
#RestController
#PreAuthorize("hasRole('USER')")
public class MyController
#Autowired
private localDAO LocalDAO; // dao classes exist in my project
#Autowired
private DependencyDAO dependencyDAO; // dao classes imported via maven dependency
...
#RequestMapping("/getUsingLocalDAO")
private String getUsingLocalDAO(
#JsonProperty("param") String param) {
localDAO.get(param) ... // <== this never null
}
#RequestMapping("/getUsingDependencyDAO")
private String getUsingDependencyDAO(
#JsonProperty("param") String param) {
dependencyDAO.get(param) ... // <== this always null
}
...
My dao beans are defined in another class:
#Configuration
public class DaoBeans {
#Bean
public LocalDAO localDAO() throws Exception {
return new LocalDAOImpl();
}
#Bean
public DependencyDAO dependencyDAO () throws Exception {
return new DependencyDAOImpl();
}
...
I am doing an $http.post from Angular like this:
$http.post('getUsingLocalDAO', $.param($scope.parameters), {
headers : {
"content-type" : "application/x-www-form-urlencoded"
}
}).success(function(data) {
...
}).error(function(data) {
...
$http.post('getUsingDependencyDAO', $.param($scope.parameters), {
headers : {
"content-type" : "application/x-www-form-urlencoded"
}
}).success(function(data) {
...
}).error(function(data) {
...
Both posts are identical except for the method they execute.
When stepping through the debugger I can see all the dao beans being created.
When I call getUsingLocalDAO everything works as expected.
But, when I call getUsingDependencyDAO every #Autowired object is null.
I believe I am getting different instances of MyController. One managed by Spring and one not; or at least not instantiated properly.
I make these calls in succession. It doesn't matter what order they are in.
I tried injecting the servlet context via #Autowired to get the bean manually but it is always null in getUsingDependencyDAO as well.
I tried using application context aware and although I see the context setter being set in the debugger the context is always null in getUsingDependencyDAO.
If I wrap the two calls in a third request mapping like so everything works well (no null objects).
#RequestMapping("/getUsingBothDAO")
private String getUsingBothDAO(
#JsonProperty("param") String param) {
getLocalDAO(param);
getDependencyDAO(param);
...
}
I am using Spring-Boot 4.1.5. My project is 100% annotation driven and has no .xml configurations. The only difference between the two request mappings is that one uses a bean from a dependency and one does not.
I have been searching for an answer to this problem for 3 days and have not found anything close to what I am experiencing.
Can anyone shed some light as to what I am doing wrong? Any help would be greatly appreciated.
Thanks.
Ok, I solved the problem. My example code above is not entirely accurate. The request method that was giving me nulls was defined as a private method while the one that worked was defined as public as its supposed to be. Originally the private method was not a request method and that modifier remained after the change. I changed it to public and everything is working.
It was just coincidence that the private method was from an imported project. It's curious that Spring did not throw an error that the request mapping didn't exist on the private method or something to that effect.
Thanks to anyone who looked at this and was trying to figure it out.