im using spring SimpleMailMessage,whenever i try to send mail function i get this exception:
java.lang.IllegalArgumentException: The 'original' message argument cannot be null
My spring.xml
<bean id="MailSender" class="com.util.MailSender">
<property name="mailSender" ref="mailSender"/>
<property name="templateMessage" ref="templateMessage"/>
</bean>
MailSenderClass:
private MailSender mailSender;
private SimpleMailMessage templateMessage;
//getter and setter
public void sendMail(String content, List<String> listEmail){
SimpleMailMessage[] mailMessageArray = new SimpleMailMessage[listEmail.size()];
Iterator<String> iterator = listEmail.iterator();
for (int index = 0; iterator.hasNext(); index ++){
SimpleMailMessage message = new SimpleMailMessage(this.templateMessage);
String toAddress = iterator.next();
message.setTo(toAddress);
message.setText(content);
mailMessageArray[index] = message;
}
this.mailSender.send(mailMessageArray);
}
Action Bean Class :
private MailSender mailSender=new MailSender();
public void ReSend(){
mailSender.sendMail(listBEeans[i].getEmailContent(),listEmail);
//able to pass the content and listEmails correctly
}
Did i miss any configurations in my action bean? Thank you
It looks like your bean is creating a local reference to MailSender instead of using the injected dependency (assuming you created one). Somewhere you should have:
AnObject
{
...
MailSender mailsender = (MailSender)context.getBean("mailSender");
...
}
And your bean should then say:
private MailSender = AnObject.getMailSender();
The problem is your template used in the constructor is null. It cannot be.
From the code of SimpleMailMessage:
public SimpleMailMessage(SimpleMailMessage original) {
Assert.notNull(original, "The 'original' message argument cannot be null");
...
Check if your bean templateMessage is really set to something or remove it from the MailSender and use the no-args constructor.
Related
I met this exception with springboot 1.5.3 + hibernate5.
And ,i used the #EnableTransactionManagement at the start application class. Have the transaction aop configuration as follow code...
#Aspect
#Configuration
public class TransactionManagerConfig {
private static final String AOP_POINTCUT_EXPRESSION="execution (* *..service..*Service*.*(..))";
#Autowired
#Qualifier("transactionManager")
private HibernateTransactionManager transactionManager;
......
......
It worked very nice until yestardy.And then,i create a service named c.a.b.service.MessageCoreService.
#Service
public class MessageCoreService {
#Autowired
private MessageSourceResolverMapper mapper;
#Autowired
private TenantIdentifierResolver tir;
public Map<String, MsgDetail> fetchCode(String code, Locale locale) {
System.err.println("Thread.currentThread().getName() at MessageCoreService:" + Thread.currentThread().getName());
System.err.println("request teanantId:" + tir.resolveCurrentTenantIdentifier());
System.err.println("locale3 at MessageCoreService:" + locale.toString());
MsgDetail enMsg = mapper.resolveCode(code, MessageSourceResolver.DEFAULT_LOCALE);
MsgDetail localeMsg = mapper.resolveCode(code, locale.toString());
Map<String, MsgDetail> ret = new HashMap<String, MsgDetail>();
ret.put("defaultLocale", enMsg);
ret.put("requestLocale", localeMsg);
return ret;
}
In the controller, do a test first.
#Autowired
private MessageCoreService msgService;
#RequestMapping("/test")
#ResponseBody
public Map<String, MsgDetail> test() {
System.err.println("111111111111111111111111111111111:");
TenantIdentifierResolver.tempOverideTenantId(ZAppUtils.getInitDatabaseId()); // change to init database
ServiceResult<Module> rslt = new ServiceResult<Module>();
Locale locale = new Locale("en", "US");
System.err.println("Thread.currentThread().getName() at ResourcesModuleController:" + Thread.currentThread().getName());
System.err.println("request teanantId:" + tir.resolveCurrentTenantIdentifier());
return msgService.fetchCode("e90001.5", locale);
Then, the hibernate exception "Could not obtain transaction-synchronized Session for current thread" occured.
I checked over and over,everything is ok.But it's aways occured.
I don't understand any of this. And then,i coped c.a.b.service.MessageCoreService. to c.a.c.service.MessageAdminService. Yes,just change a direcory,rename it.
In the controller,add a method.
#Autowired
private MessageCoreAdminService msgAdminService;
#RequestMapping("/test2")
#ResponseBody
public Map<String, MsgDetail> test2() {
System.err.println("111111111111111111111111111111111:");
TenantIdentifierResolver.tempOverideTenantId(ZAppUtils.getInitDatabaseId()); // change to init database
ServiceResult<Module> rslt = new ServiceResult<Module>();
Locale locale = new Locale("en", "US");
System.err.println("Thread.currentThread().getName() at ResourcesModuleController:" + Thread.currentThread().getName());
return msgAdminService.fetchCode("e90001.5", locale);
}
And then ,an incredible scene appeared. When i access the test2,it worked normally, but the test method ,it still show me the "transaction-synchronized Session" exception. Oh ,I feel like I'm breaking down. I can't resolve the problem.
Pls help.
I am working with Spring-websocket and I have the following problem:
I am trying to put a placeholder inside a #MessageMapping annotation in order to get the url from properties. It works with #RequestMapping but not with #MessageMapping.
If I use this placeholder, the URL is null. Any idea or suggestion?
Example:
#RequestMapping(value= "${myProperty}")
#MessageMapping("${myProperty}")
Rossen Stoyanchev added placeholder support for #MessageMapping and #SubscribeMapping methods.
See Jira issue: https://jira.spring.io/browse/SPR-13271
Spring allows you to use property placeholders in #RequestMapping, but not in #MessageMapping. This is 'cause the MessageHandler. So, we need to override the default MessageHandler to do this.
WebSocketAnnotationMethodMessageHandler does not support placeholders and you need add this support yourself.
For simplicity I just created another WebSocketAnnotationMethodMessageHandler class in my project at the same package of the original, org.springframework.web.socket.messaging, and override getMappingForMethod method from SimpAnnotationMethodMessageHandler with same content, changing only how SimpMessageMappingInfo is contructed using this with this methods (private in WebSocketAnnotationMethodMessageHandler):
private SimpMessageMappingInfo createMessageMappingCondition(final MessageMapping annotation) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
private SimpMessageMappingInfo createSubscribeCondition(final SubscribeMapping annotation) {
final SimpMessageTypeMessageCondition messageTypeMessageCondition = SimpMessageTypeMessageCondition.SUBSCRIBE;
return new SimpMessageMappingInfo(messageTypeMessageCondition, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
These methods now will resolve value considering properties (calling resolveAnnotationValues method), so we need use something like this:
private String[] resolveAnnotationValues(final String[] destinationNames) {
final int length = destinationNames.length;
final String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = this.resolveAnnotationValue(destinationNames[i]);
}
return result;
}
private String resolveAnnotationValue(final String name) {
if (!(this.getApplicationContext() instanceof ConfigurableApplicationContext)) {
return name;
}
final ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) this.getApplicationContext();
final ConfigurableBeanFactory configurableBeanFactory = applicationContext.getBeanFactory();
final String placeholdersResolved = configurableBeanFactory.resolveEmbeddedValue(name);
final BeanExpressionResolver exprResolver = configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return name;
}
final Object result = exprResolver.evaluate(placeholdersResolved, new BeanExpressionContext(configurableBeanFactory, null));
return result != null ? result.toString() : name;
}
You still need to define a PropertySourcesPlaceholderConfigurer bean in your configuration.
If you are using XML based configuration, include something like this:
<context:property-placeholder location="classpath:/META-INF/spring/url-mapping-config.properties" />
If you are using Java based configuration, you can try in this way:
#Configuration
#PropertySources(value = #PropertySource("classpath:/META-INF/spring/url-mapping-config.properties"))
public class URLMappingConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Obs.: in this case, url-mapping-config.properties file are in a gradle/maven project in src\main\resources\META-INF\spring folder and content look like this:
myPropertyWS=urlvaluews
This is my sample controller:
#Controller
public class WebSocketController {
#SendTo("/topic/test")
#MessageMapping("${myPropertyWS}")
public String test() throws Exception {
Thread.sleep(4000); // simulated delay
return "OK";
}
}
With default MessageHandler startup log will print something like this:
INFO: Mapped "{[/${myPropertyWS}],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
And with our MessageHandler now print this:
INFO: Mapped "{[/urlvaluews],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
See in this gist the full WebSocketAnnotationMethodMessageHandler implementation.
EDIT: this solution resolves the problem for versions before 4.2 GA. For more information, see this jira.
Update :
Now I understood what you mean, but I think that is not possible(yet).
Documentation does not mention anything related to Path mapping URIs.
Old answer
Use
#MessageMapping("/handler/{myProperty}")
instead of
#MessageMapping("/handler/${myProperty}")
And use it like this:
#MessageMapping("/myHandler/{username}")
public void handleTextMessage(#DestinationVariable String username,Message message) {
//do something
}
#MessageMapping("/chat/{roomId}")
public Message handleMessages(#DestinationVariable("roomId") String roomId, #Payload Message message, Traveler traveler) throws Exception {
System.out.println("Message received for room: " + roomId);
System.out.println("User: " + traveler.toString());
// store message in database
message.setAuthor(traveler);
message.setChatRoomId(Integer.parseInt(roomId));
int id = MessageRepository.getInstance().save(message);
message.setId(id);
return message;
}
It may be a duplicate question.
My question is: I want to read a property from properties file and put that in servlet context as soon as I start my application.
Could any one please help me on that?
Thanks in advance.
Implement Spring's ApplicationListener:
#Component
public class MyApplicationListener implements ApplicationListener {
/* if you want to set predefined properties you even don't have to load properties filed - you can directly inject properties values ... you can configure it in applicationContext.xml
<util:list id="locations">
<value>classpath:appconfig1.properties</value>
<value>classpath:appconfig2.properties</value>
</util:list>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:locations-ref="locations" />
*/
#Value("${myproperty1}") private String myProperty1;
#Value("${myproperty2}") private String myProperty2;
#Value("${myproperty3}") private String myProperty3;
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
applicationClosed();
return;
}
if (!(event instanceof ContextRefreshedEvent)) return;
ContextRefreshedEvent e = (ContextRefreshedEvent) event;
ApplicationContext appContext = e.getApplicationContext();
if (!(appContext instanceof WebApplicationContext)) return;
WebApplicationContext ctx = (WebApplicationContext) e.getApplicationContext();
ServletContext context = ctx.getServletContext();
context.setAttribute("myProperty1", myProperty1);
context.setAttribute("myProperty2", myProperty2);
context.setAttribute("myProperty3", myProperty3);
}
}
For now I could inject values from property-files:
#Value("${aaa.prop}")
public String someProp;
But I want something more...
For example I have some property file:
aaa.props=p1,p2,p3
aaa.props.p1=qwe
aaa.props.p2=asd
aaa.props.p3=zxc
I know for sure, that it contains property aaa.props, and know nothing about other properties. And I want to get this properties in to map using code like this:
#Value ("${aaa.props}")
public Map<String, String> someProps;
Resulting someProps: {p1=qwe,p2=asd,p3=zxc}
Well I built a generic approach for you: a factory bean that creates a map by filtering another map (properties are a kind of map, after all).
Here's the factory bean:
public class FilteredMapFactoryBean<V> extends
AbstractFactoryBean<Map<String, V>>{
private Map<String, V> input;
/**
* Set the input map.
*/
public void setInput(final Map<String, V> input){
this.input = input;
}
/**
* Set the string by which key prefixes will be filtered.
*/
public void setKeyFilterPrefix(final String keyFilterPrefix){
this.entryFilter = new EntryFilter<String, V>(){
#Override
public boolean accept(final Entry<String, V> entry){
return entry.getKey().startsWith(keyFilterPrefix);
}
};
}
public static interface EntryFilter<EK, EV> {
boolean accept(Map.Entry<EK, EV> entry);
}
/**
* If a prefix is not enough, you can supply a custom filter.
*/
public void setEntryFilter(final EntryFilter<String, V> entryFilter){
this.entryFilter = entryFilter;
}
private EntryFilter<String, V> entryFilter;
/**
* {#inheritDoc}
*/
#Override
public Class<?> getObjectType(){
return Map.class;
}
/**
* {#inheritDoc}
*/
#Override
protected Map<String, V> createInstance() throws Exception{
final Map<String, V> map = new LinkedHashMap<String, V>();
for(final Entry<String, V> entry : this.input.entrySet()){
if(this.entryFilter == null || this.entryFilter.accept(entry)){
map.put(entry.getKey(), entry.getValue());
}
}
return map;
}
}
Here is a spring bean definition file with some sample usage:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- use System.getProperties() as input -->
<bean class="spring.test.FilteredMapFactoryBean" id="javaMap">
<property name="keyFilterPrefix" value="java." />
<property name="input" value="#{T(java.lang.System).getProperties()}" />
</bean>
<!-- use custom properties as input -->
<bean class="spring.test.FilteredMapFactoryBean" id="customMap">
<property name="keyFilterPrefix" value="hello" />
<property name="input">
<props>
<prop key="hello">Is it me you're looking for?</prop>
<prop key="hello.again">Just called to say: hello.</prop>
<prop key="hello.goodby">You say goodbye and I say hello</prop>
<prop key="goodbye.blue.sky">Did-did-did-did-you hear the falling bombs?</prop>
<prop key="goodbye.ruby.tuesday">Who could hang a name on you?</prop>
</props>
</property>
</bean>
</beans>
And here is a test class:
public class Tester{
#SuppressWarnings("unchecked")
public static void main(final String[] args){
final ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:spring/test/mapFactorybean.xml");
final Map<String, String> javaMap =
(Map<String, String>) context.getBean("javaMap");
print("java.", javaMap);
final Map<String, String> customMap =
(Map<String, String>) context.getBean("customMap");
print("hello.", customMap);
}
private static void print(final String prefix, final Map<String, String> map){
System.out.println("Map of items starting with " + prefix);
for(final Entry<String, String> entry : map.entrySet()){
System.out.println("\t" + entry.getKey() + ":" + entry.getValue());
}
System.out.println("");
}
}
The output is as expected:
Map of items starting with java.
java.runtime.name:Java(TM) SE Runtime Environment
java.vm.version:14.2-b01
java.vm.vendor:Sun Microsystems Inc.
java.vendor.url:http://java.sun.com/
java.vm.name:Java HotSpot(TM) Client VM
java.vm.specification.name:Java Virtual Machine Specification
java.runtime.version:1.6.0_16-b01
java.awt.graphicsenv:sun.awt.Win32GraphicsEnvironment
[... etc]
Map of items starting with hello.
hello.goodby:You say goodbye and I say hello
hello:Is it me you're looking for?
hello.again:Just called to say: hello.
I'm afraid you can't, directly. But you can
implement ApplicationContextAware and set the ApplicationContext as a field in your bean.
in a #PostConstruct method call context.getBean("${aaa.props}")
parse the result manually and set it to the desired fields
you can use #Value.
Properties file:
aaa.props={p1:'qwe',p2:'asd',p3:'zxc'}
Java code:
#Value("#{${aaa.props}}")
private Map<String,String> someProps;
You could do something like this:
Maven dependency
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
Add the import.
import javax.annotation.Resource;
...
#Resource (name="propertiesMapName")
public Properties someProps;
In your spring xml application context:
<util:properties id="propertiesMapName" location="classpath:yourFile.properties"/>
You will need this namespace
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
The solution it's well defined on https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
#ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
The intent here is to deal with obfuscated passwords for resources.
We have an Advisor that intercepts calls to setPassword and decrypts the argument.
We've set up a template that looks somewhat like this:
<bean id="pwAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice"><bean class="our.advice.bean.class"/></property>
<property name="mappedName" value="setPassword"/>
</bean>
<bean id="passwordHandlerTemplate" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
<property name="interceptorNames"><list><value>pwAdvisor</value></list></property>
</bean>
I'm unclear on the exact syntax to use it. The most obvious way is:
<bean id="myPasswordProtectedThing" parent="passwordHandlerTemplate">
<property name="target">
<bean class="the.target.class.name">
<property name="password" value="encrypted garbage"/>
</bean>
</property>
</bean>
But that doesn't work right, since the password property is applied to the inner bean, which means that the advisor won't wind up doing its work.
Well, what about this:
<bean id="myPasswordProtectedThing" parent="passwordHandlerTemplate">
<property name="target"><bean class="the.target.class.name"/></property>
<property name="password" value="encrypted garbage"/>
</bean>
Nope. Spring complains that the ProxyFactoryBean doesn't have a password property. And, of course, it doesn't. The thing that has the password property is the thing the factory bean creates.
Bueller?
My first effort was poor, but I was in hurry. I apologize. Now I think I know how it should work, because I believe I've implemented what you want myself.
I started with a Credential class (note: no interface):
package aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Credential
{
private static final String DEFAULT_USERNAME = "username";
private static final String DEFAULT_PASSWORD = "password";
private String username;
private String password;
public static void main(String[] args)
{
Credential cred1 = new Credential("foo", "bar");
System.out.println("created using new: " + cred1);
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:aop-context.xml");
Credential cred2 = (Credential) context.getBean("credential");
System.out.println("created using app context: " + cred2);
String password = ((args.length > 0) ? args[0] : "baz");
cred2.setPassword(password);
System.out.println("initialized using setter: " + cred2);
}
public Credential()
{
this(DEFAULT_USERNAME, DEFAULT_PASSWORD);
}
public Credential(String username, String password)
{
this.setUsername(username);
this.setPassword(password);
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String toString()
{
return new StringBuilder().append("Credential{").append("username='").append(username).append('\'').append(", password='").append(password).append('\'').append('}').toString();
}
}
I created a Decryptor interface:
package aop;
public interface Decryptor
{
String decrypt(String encrypted);
}
And a DecryptorImpl:
package aop;
public class DecryptorImpl implements Decryptor
{
public static final String DEFAULT_DECRYPTED_VALUE = " - not secret anymore";
public String decrypt(String encrypted)
{
// Any transform will do; this suffices to demonstrate
return encrypted + DEFAULT_DECRYPTED_VALUE;
}
}
I needed DecryptorAdvice to implement Spring's MethodBeforeAdvice:
package aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class DecryptionAdvice implements MethodBeforeAdvice
{
private Decryptor decryptor;
public DecryptionAdvice(Decryptor decryptor)
{
this.decryptor = decryptor;
}
public void before(Method method, Object[] args, Object target) throws Throwable
{
String encryptedPassword = (String) args[0];
args[0] = this.decryptor.decrypt(encryptedPassword);
}
}
And I wired it together in an aop-context.xml. (If you tell me how to get XML to display, I'll post it.) Note the passwordDecryptionAdvisor: it only matches the setPassword method.
The interesting part happens when I run it. Here's what I see in the console:
created using new: Credential{username='foo', password='bar'}
created using app context: Credential{username='stackoverflow', password='encrypted-password'}
initialized using setter: Credential{username='stackoverflow', password='baz - not secret anymore'}
What this tells me is:
If I create an object with new it's
not under Spring's control, advice
isn't applied.
If I call setPassword in the ctor
before the app context is
initialized, advice isn't applied.
If I call setPassword in my code
after the app context is
initialized, advice is applied.
I hope this can help you.
I thought you wanted the beforeMethod advice to use the encrypted password String that's passed into the setPassword method. You want to decrypt that and have the advised class get an decrypted version.
I also don't see a proxy interface set in your proxy factory. "Spring In Action" says "...Creating a proxy with interfaces is favored over proxying classes..." Proxying classes should be the exception, not the rule.
Post your advice class.