Ok so I have build a converter and added it to the dispatcher xml. But it won't work. I don't understand how the controller should know when it should use the converter. In my jsp page I check multiply checkboxes. Each checkbox holds an id of a developer. Spring should make a set of developers from these id's. I have the feeling I'm missing something in the controller. I used to do it with editors and then you would override the initbinder method. I don't know how to do it with converters.
Thank you in advance,
David
so first I made a class implementing the interface:
public class DeveloperConverter implements Converter<String, Developer> {
private GameOrganizer gameOrganizer;
public void setGameOrganizer(GameOrganizer gameOrganizer) {
this.gameOrganizer = gameOrganizer;
}
public Developer convert(String s) {
long id2 = Long.parseLong(s);
Developer type = gameOrganizer.getDeveloper(id2);
return type;
}
}
then I added the bean to the dispatcher xml:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="converters.GameConverter" />
<bean class="converters.DeveloperConverter" />
</list>
</property>
</bean>
And the controller:
#Controller
#RequestMapping("/AddGame")
public class GameFormController {
#Autowired
private GameOrganizer gameOrganizer;
private DeveloperConverter developerEditor;
private GameValidator gameValidator;
private ConversionService service;
public GameFormController() {
setGameValidator(new GameValidator());
}
public void setGameOrganizer(GameOrganizer gameOrganizer) {
this.gameOrganizer = gameOrganizer;
}
public void setDeveloperEditor(DeveloperConverter developerEditor) {
this.developerEditor = developerEditor;
developerEditor.setGameOrganizer(gameOrganizer);
}
public void setGameValidator(GameValidator gameValidator) {
this.gameValidator = gameValidator;
}
#RequestMapping(method = RequestMethod.GET)
private String showForm(ModelMap model) {
return "AddGame";
}
#ModelAttribute("editGame")
private Game GameformBackingObject(HttpServletRequest request) throws Exception {
Game game = null;
long id = ServletRequestUtils.getLongParameter(request, "id");
if (id <= 0) {
game = new Game();
} else {
game = new Game();
game.setId(gameOrganizer.getGame(id).getId());
game.setDevelopers(gameOrganizer.getGame(id).getDevelopers());
game.setGameNaam(gameOrganizer.getGame(id).getGameNaam());
game.setImages(gameOrganizer.getGame(id).getImages());
game.setPrijs(gameOrganizer.getGame(id).getPrijs());
}
return game;
}
#RequestMapping(method = RequestMethod.POST)
protected String doSubmitAction(#ModelAttribute("editGame") Game game, BindingResult result) throws Exception {
gameValidator.validate(game, result);
if (result.hasErrors()) {
return "AddGame";
} else {
if (game.getId() <= 0) {
gameOrganizer.addGame(game);
} else {
gameOrganizer.update(game);
}
return "forward:/Gamedatabase.htm";
}
}
#ModelAttribute("allDevelopers")
private Set<Developer> getDevelopers() throws Exception {
Set<Developer> developers = gameOrganizer.getAllDevelopers();
return developers;
}
#ModelAttribute("currentId")
private long getCurrentId(HttpServletRequest request) throws ServletRequestBindingException {
long id = ServletRequestUtils.getLongParameter(request, "id");
return id;
}
}
I suppose you have not configured the conversion service for Spring MVC in your XML configuration:
<mvc:annotation-driven conversion-service="conversionService" />
Related
I'm trying to make artificial CONSTRAINT violation by Spring instead of throwing exception from DB (an expert sad DB-produced errors have high performance cost):
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
#Component
public class AccountValidator implements org.springframework.validation.Validator {
#Autowired
private Validator validator;
private final AccountService accountService;
public AccountValidator(#Qualifier("accountServiceAlias")AccountService accountService) {
this.accountService = accountService;
}
#Override
public boolean supports(Class<?> clazz) {
return AccountRequestDTO.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Set<ConstraintViolation<Object>> validates = validator.validate(target);
for (ConstraintViolation<Object> constraintViolation : validates) {
String propertyPath = constraintViolation.getPropertyPath().toString();
String message = constraintViolation.getMessage();
errors.rejectValue(propertyPath, "", message);
}
AccountRequestDTO account = (AccountRequestDTO) target;
if(accountService.getPhone(account.getPhone()) != null){
errors.rejectValue("phone", "", "Validator in action! This number is already in use.");
}
}
}
However, second part of validate() method never works for reasons I cant understand and always pass a call from controller to be handled in try-catch block throwing exception from DB:
public void saveAccount(AccountRequestDTO accountRequestDTO) throws Exception {
LocalDate birthday = LocalDate.parse(accountRequestDTO.getBirthday());
if (LocalDate.from(birthday).until(LocalDate.now(), ChronoUnit.YEARS) < 18) {
throw new RegistrationException("You must be 18+ to register");
}
Account account = new Account(accountRequestDTO.getName(), accountRequestDTO.getSurname(),
accountRequestDTO.getPhone(), birthday, BCrypt.hashpw
(accountRequestDTO.getPassword(), BCrypt.gensalt(4)));
account.addRole(Role.CLIENT);
try {
accountRepository.save(account);
}
catch (RuntimeException exc) {
throw new PersistenceException("Database exception: this number is already in use.");
}
}
Here's a controller method:
#PostMapping("/confirm")
public String signIn(#ModelAttribute("account") #Valid AccountRequestDTO accountRequestDTO,
BindingResult result, Model model) {
accountValidator.validate(accountRequestDTO, result);
if(result.hasErrors()) {
return "/auth/register";
}
try {
accountService.saveAccount(accountRequestDTO);
}
catch (Exception exc) {
model.addAttribute("message", exc.getMessage());
return "/auth/register";
}
return "/auth/login";
}
At service:
#Transactional(readOnly = true)
public String getPhone(String phone){
return accountRepository.getPhone(phone);
}
JpaRepository query:
#Query("SELECT phone FROM Account accounts WHERE phone=:check")
String getPhone(String check);
Tests are green:
#BeforeAll
static void prepare() {
search = new String("0000000000");
}
#BeforeEach
void set_up() {
account = new Account
("Admin", "Adminov", "0000000000", LocalDate.of(2001, 01, 01), "superadmin");
accountRepository.save(account);
}
#Test
void check_if_phone_presents() {
assertThat(accountRepository.getPhone(search).equals(account.getPhone())).isTrue();
}
#Test
void check_if_phone_not_presents() {
String newPhone = "9999999999";
assertThat(accountRepository.getPhone(newPhone)).isNull();
}
#AfterEach
void tear_down() {
accountRepository.deleteAll();
account = null;
}
#AfterAll
static void clear() {
search = null;
}
You need to register your validator.
After we've defined the validator, we need to map it to a specific
event which is generated after the request is accepted.
This can be done in three ways:
Add Component annotation with name “beforeCreateAccountValidator“.
Spring Boot will recognize prefix beforeCreate which determines the
event we want to catch, and it will also recognize WebsiteUser class
from Component name.
#Component("beforeCreateAccountValidator")
public class AccountValidator implements Validator {
...
}
Create Bean in Application Context with #Bean annotation:
#Bean
public AccountValidator beforeCreateAccountValidator () {
return new AccountValidator ();
}
Manual registration:
#SpringBootApplication
public class SpringDataRestApplication implements RepositoryRestConfigurer {
public static void main(String[] args) {
SpringApplication.run(SpringDataRestApplication.class, args);
}
#Override
public void configureValidatingRepositoryEventListener(
ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", new AccountValidator ());
}
}
Consider the following code:
public class UserJMXBeanExpose{
private UserJMXBeanImpl userJMXBean;
public UserJMXBeanExpose(UserJMXBeanImpl userJMXBean) {
this.userJMXBean = userJMXBean;
}
public void init() throws MBeanRegistrationException, InstanceAlreadyExistsException, NotCompliantMBeanException, MalformedObjectNameException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("MineStar:type=UserJMXBeanExpose,name=Statistics");
MineStarMBean impl = new MineStarMBean();
impl.addProperty(new PropertyAdapter("User", "User List", "java.lang.String") {
public Object getValue() {
if (userJMXBean == null) return 0;
return userJMXBean.getUserAttributes();
}
});
server.registerMBean(impl, objectName);
}
}
And this code:
public class UserJMXBeanImpl implements UserJMXBean {
private List<User> userList = new ArrayList<>();
public void addUser(User user){
userList.add(user);
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public String[] getUserAttributes(){
for(User user : this.getUserList()){
return new String[]{user.getUserId(),user.getRoles()};
}
return null;
}
}
When a user logs in the the class userjmxbeanimpl stores the data. But I need to activate the userjmxbeanexpose class too as soon as a new user is added so that it can expose the data grabbed from impl class. How can I do this?
The spring configuration goes like this:
<bean id="userJMXBeans" class="minestar.platform.domain.user.UserJMXBeanExpose" init-method="init" scope="singleton"
lazy-init="false">
<constructor-arg ref="userJMXBean"/>
</bean>
I'm not sure what logic it would represent, but judging by the code you provided... The only thing you need to do to make it work is to do:
public void addUser(User user){
userList.add(user);
new UserJMXBeanExpose(this).init();
}
After consideration Observer pattern doesn't seem to work here, it would be a lot of boilerplate code that would essentially do the same and bring no benefits. Unless I misunderstood what it means to activate UserJMXBeanExpose.
I am not being able to make messageSource work in the Pojo classes,its throwing a nullpointerexception. However in all the other classes namely controller,service messageSource is working alright. Could someone please suggest what needs to be done ?
#Autowired
private MessageSource messageSource;
I have autowired the MessageSource using the above code snippet.
public class ProposalWiseSelectionForm implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Autowired
private MessageSource messageSource;
private String txtPageHierarchy="";
private String txtLineOfBusiness;
private String txtProduct;
private String btn;
private String clickedGo="N";
private List arrLineOfBusiness=new ArrayList();
private List arrProduct=new ArrayList();
#Valid
private ArrayList documentList=initiateDocumentList();
private String txtPageMode="I";
private String enableDiscardBtn="N";
private String enableInsertBtn="N";
private String isDivVisible="N";
private int numApplicationType=1;
public ProposalWiseSelectionForm() {
}
public String getTxtPageHierarchy() {
return txtPageHierarchy;
}
public void setTxtPageHierarchy(String txtPageHierarchy) {
this.txtPageHierarchy = txtPageHierarchy;
}
public String getTxtLineOfBusiness() {
return txtLineOfBusiness;
}
public void setTxtLineOfBusiness(String txtLineOfBusiness) {
this.txtLineOfBusiness = txtLineOfBusiness;
}
public String getTxtProduct() {
return txtProduct;
}
public void setTxtProduct(String txtProduct) {
this.txtProduct = txtProduct;
}
public String getBtn() {
return btn;
}
public void setBtn(String btn) {
this.btn = btn;
}
public String getClickedGo() {
return clickedGo;
}
public void setClickedGo(String clickedGo) {
this.clickedGo = clickedGo;
}
public List getArrLineOfBusiness() {
return arrLineOfBusiness;
}
public void setArrLineOfBusiness(List arrLineOfBusiness) {
this.arrLineOfBusiness = arrLineOfBusiness;
}
public List getArrProduct() {
return arrProduct;
}
public void setArrProduct(List arrProduct) {
this.arrProduct = arrProduct;
}
public void setArrProduct(ArrayList arrProduct) {
this.arrProduct = arrProduct;
}
public ArrayList getDocumentList() {
return documentList;
}
public void setDocumentList(ArrayList documentList) {
this.documentList = documentList;
}
public String getTxtPageMode() {
return txtPageMode;
}
public void setTxtPageMode(String txtPageMode) {
this.txtPageMode = txtPageMode;
}
public String getEnableDiscardBtn() {
return enableDiscardBtn;
}
public void setEnableDiscardBtn(String enableDiscardBtn) {
this.enableDiscardBtn = enableDiscardBtn;
}
public String getEnableInsertBtn() {
return enableInsertBtn;
}
public void setEnableInsertBtn(String enableInsertBtn) {
this.enableInsertBtn = enableInsertBtn;
}
public String getIsDivVisible() {
return isDivVisible;
}
public void setIsDivVisible(String isDivVisible) {
this.isDivVisible = isDivVisible;
}
public int getNumApplicationType() {
return numApplicationType;
}
public void setNumApplicationType(int numApplicationType) {
this.numApplicationType = numApplicationType;
}
}
In order to be able to use #Autowired in a class, that class has to be managed by Spring.
of
Your ProposalWiseSelectionForm class is obviously not managed by Spring and therefor messageSource is always null.
Using #Autowired MessageSource messageSource in your other classes works, because as you mention those classes are managed by Spring (as you have mentioned they are either controllers, services etc).
I am guessing that ProposalWiseSelectionForm is a DTO used to capture values from a form. The sort of class will not be a Spring bean and therefor you can't autowire stuff into it.
I suggest you either move the logic you need out of the DTO and into the controller (or some Spring managed utility) or in the extreme case that you absolutely need #Autowired in the DTO, take a look at #Configurable here and here
Try using #Component,you might be getting this issue because of the fact the Pojo class is not being recognized.
You have to make your class a Spring bean
Add #Component annotation to your class and add these 2 lines to your appContext.xml:
<context:component-scan base-package="com.<your-company-name>" />
<context:annotation-config />
Or just add the service in your beans section in the appContext.xml if you wish not to work with Spring component-scan feature.
I am just dabbling with Spring framework. Here I was trying out the "parent" attribute in the bean declaration,
This is my code below for CommonCar.java:
package com.justPractise.ex01;
public class CommonCar {
private String modelName;
private String engine;
public CommonCar(String modelName){
this.modelName = modelName;
System.out.println(" PARAMETERISED "+this.getClass().getName()+" INITIALISED..... ");
}
public CommonCar(){
System.out.println(this.getClass().getName()+" INITIALISED..... ");
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
#Override
public String toString(){
StringBuffer strb = new StringBuffer();
strb.append("\nDEFAULT CAR ");
strb.append(this.modelName);
strb.append("\nENGINE NAME ");
strb.append(this.engine);
return strb.toString();
}
}
This the code below for CustomCar.java:
package com.justPractise.ex01;
public class CustomCar {
private String modelName;
private String engine;
public CustomCar(){
System.out.println(this.getClass().getName()+" INITIALISED..... ");
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
#Override
public String toString(){
StringBuffer strb = new StringBuffer();
strb.append("\nDEFAULT CAR ");
strb.append(this.modelName);
strb.append("\nENGINE NAME ");
strb.append(this.engine);
return strb.toString();
}
}
This is the bean-jojo.xml file:
<?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" default-lazy-init="true">
<bean class="com.justPractise.ex01.CommonCar" id="commonCAR">
<constructor-arg value="TATA-SAFARI V30" />
<property name="engine" value="2340 CC FOUR CYLINDER 1700 BHP ENGINE" />
</bean>
<bean class="com.justPractise.ex01.CustomCar" id="customCAR" parent="commonCAR">
<property name="modelName" value="TOYOTA-INNOVA" />
</bean>
</beans>
This is the class with the main method, which I run from commandline:
package com.justPractise.ex01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainPractise01 {
/**
* #param args
*/
public static void main(String[] args) {
ApplicationContext ctx = null;
CustomCar obj = null;
try{
ctx = new ClassPathXmlApplicationContext("bean-jojo.xml");
obj = (CustomCar) ctx.getBean("customCAR");
System.out.println(obj);
}catch(Exception e){
e.printStackTrace();
}
}
}
Now if I run the above programme, I get this error in the command prompt:
[java] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customCAR' defined in class path resource
[bean-jojo.xml]: 1 constructor arguments specified but no matching constructor found in bean 'customCAR' (hint: specify index/type/name
arguments for simple parameters to avoid type ambiguities)
But if I make the following changes to the bean-jojo.xml, my programme runs fine:
<bean class="com.justPractise.ex01.CommonCar" id="commonCAR">
<property name="modelName" value="TATA-SAFARI V30" />
<property name="engine" value="2340 CC FOUR CYLINDER 1700 BHP ENGINE" />
</bean>
This is the expected output I get by making the above change in xml:
[java] com.justPractise.ex01.CustomCar INITIALISED.....
[java]
[java] DEFAULT CAR TOYOTA-INNOVA
[java] ENGINE NAME 2340 CC FOUR CYLINDER 1700 BHP ENGINE
[echo] Java running completed
So, can you tell me why constructor args in the CommonCar declaration in bean-jojo.xml was not working?
Waiting for the comments
The exception could not be more readable. Create a constructor in your customBean car that accepts a String (Spring will pass it TATA-SAFARI V30)
Your second example works because you no longer reference the commonClass superclass, so it is not defined a constructor with a parameter
I've been trying to figure out why my simple aspect is not getting executed. I looked at the answers of similar problems but i still can't get it to work.
My intention is to wrap the execution of a method annotated with a custom annotation with an AOP advice that will track how long the method takes to run. When i run my test, i see the output of the method but the advice is not being run (i'm expecting it to log some output).
Here's the Aspect class:
#Aspect
class LatencyProfiler {
private LatencyTrackerFactory factory = LatencyTrackerFactory.NOOP;
#Pointcut(value="execution(#ProfileLatency * *(..)) && #annotation(annotation)", argNames="annotation")
public void profiled(ProfileLatency annotation) {}
#Around(value="profiled(annotation)", argNames="pjp,annotation")
public Object profile(ProceedingJoinPoint pjp, ProfileLatency annotation) throws Throwable {
ILatencyTracker tracker;
try {
tracker = factory.create(annotation.trackerName(), annotation.trackerNameSuffix());
} catch (ConfigException e) {
throw new RuntimeException(e);
}
tracker.begin();
Object ret = pjp.proceed();
tracker.end(null);
return ret;
}
#Optional
public void setFactory(LatencyTrackerFactory factory) {
this.factory = factory;
}
}
Followed by the Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface ProfileLatency {
String trackerName();
String trackerNameSuffix() default"[unassigned]";
}
Followed by a test class:
public class Test {
private static final Log LOG = LogFactory.getLog(Test.class);
#PostConstruct
public void init() {
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
#Override
public void run() {
for(int i = 0; i < 60; i++) {
foo();
LOG.info("HERE");
}
}
}, 2000, TimeUnit.MILLISECONDS);
}
#ProfileLatency(trackerName = "latency", trackerNameSuffix = "s")
public void foo() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
}
Spring configuration:
<context:annotation-config/>
<aop:aspectj-autoproxy>
<aop:include name="latencyProfileAspect"/>
</aop:aspectj-autoproxy>
<bean
id = "latencyLogger"
class = "util.logging.LatencyLogger"
/>
<bean
id = "trackerFactory"
class = "util.latency.LatencyTrackerFactoryImpl">
<constructor-arg value = "config/latency-config.xml"/>
<constructor-arg ref = "latencyLogger"/>
</bean>
<bean
id = "latencyProfileAspect"
class = "util.latency.aop.LatencyProfiler"
p:factory-ref = "trackerFactory"
/>
<bean id = "test" class="util.Test"/>
and finally the test's output:
21:20:37,930 INFO main/SpringMain - Ready.
21:20:40,928 INFO pool-4-thread-1/Test - HERE
21:20:41,927 INFO pool-4-thread-1/Test - HERE
21:20:42,926 INFO pool-4-thread-1/Test - HERE
21:20:43,925 INFO pool-4-thread-1/Test - HERE
21:20:44,924 INFO pool-4-thread-1/Test - HERE
...
Any advice is greatly appreciated.
So i fiddled with this around a bit and got it to work. I modified the aspect as follows:
#Aspect
public class LatencyProfiler {
private static final Log LOG = LogFactory.getLog(LatencyProfiler.class);
#Around("#annotation(annotation)")
public Object profile(ProceedingJoinPoint pjp, ProfileLatency annotation) throws Throwable {
ILatencyTracker tracker = ILatencyTracker.NOOP;
try {
tracker = StaticLatencyTrackerFactory.getTracker(annotation.trackerName(), annotation.trackerNameSuffix());
} catch (Exception e) {
LOG.error(e);
}
LatencyContext ctx = tracker.beginContext();
Object ret = pjp.proceed();
ctx.end();
return ret;
}
/*
* special purpose factory method
*/
public static LatencyProfiler aspectOf() {
return MyAspectHolder.instance;
}
/**
* private class holding the singleton
*/
private static class MyAspectHolder {
static final LatencyProfiler instance = new LatencyProfiler();
}
}
i also changed the spring configuration to be:
<context:annotation-config/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean
id = "latencyProfileAspect"
class = "util.latency.aop.LatencyProfiler"
factory-method = "aspectOf"
/>
<bean id = "test" class="util.Test"/>