I have a web application developped in Java 1.5 with Spring framework. Application contains "dashboards" which are simple pages where a bunch of information are regrouped and where user can modify some status. Managers want me to add a logging system in database for three of theses dashboards. Each dashboard has different information but the log should be traced by date and user's login.
What I'd like to do is to implement the Strategy pattern kind of like this :
interface DashboardLog {
void createLog(String login, Date now);
}
// Implementation for one dashboard
class PrintDashboardLog implements DashboardLog {
Integer docId;
String status;
void createLog(String login, Date now){
// Some code
}
}
class DashboardsManager {
DashboardLog logger;
String login;
Date now;
void createLog(){
logger.log(login,now);
}
}
class UpdateDocAction{
DashboardsManager dbManager;
void updateSomeField(){
// Some action
// Now it's time to log
dbManagers.setLogger = new PrintDashboardLog(docId, status);
dbManagers.createLog();
}
}
Appcontext.xml :
<bean id="dashboardManagers" class="...DashboardManagers" />
In this solution I'm therefore not using dependency injection. Is it "correct" (good practice, performance, ...) to do it this way ? Is there a better way where I could use DI ?
Note :I did not write basic stuff like constructors and getter/setter.
Your solution will create a new instance of PrintDashboardLog for each call to updateSomeField(). This might take up unnecessary time/memory/GC-effort. Also, from a design perspective it makes sense if there is one DashboardLog for each Dashboard, not a new one for each call.
I think it may be a good idea to use aspects for which Logging is one of the exemplary usecases. Something like:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="loggingAspect" class="com.yourcompany.yourapplication.aspects.DashboardLogAspect" />
<aop:aspectj-autoproxy>
<aop:include name="loggingAspect" />
</aop:aspectj-autoproxy>
</beans>
package com.yourcompany.yourapplication.aspects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class DashboardLogAspect {
#Around("execution(* com.yourcompany.yourapplication..*Action+.*(..)) && target(target)")
public Object logActionCall(ProceedingJoinPoint pjp, Object target) throws Throwable {
long before = System.nanoTime();
Object returnValue = pjp.proceed();
long after = System.nanoTime();
long durationNs = after - before;
String logMsg = target.getClass() + "." + pjp.getSignature().toShortString() + " (" + durationNs + " ns)";
// TODO: store the log message in your database
System.out.println(logMsg);
return returnValue;
}
}
This logs all calls to application classes with a name ending in 'Action'. It also adds the time each call took to complete. You might want to tweak the Around advice for a specific method name pattern as well. See the AspectJ programming guide
While it is perfectly "correct" to employ the strategy pattern as you have, but considering the fact that you're using Spring - it would be better to employ the Dependency Injection mechanism provided by the Spring framework - might as well put to use what your framework has to offer as one of its core strengths.
If each "dashboard" is has a controller, why not call the logging from the controller.
public interface DashboardLog
{
void createLog(...);
}
public class DashboardUno
implements DashboardLog
{
...
public void createLog(...)
{ ... }
}
#Controller
#RequestMapping("/blah/schmarr")
public class BlahController
{
...
#RequestMapping(value = "/xxx")
public String someMeaningfulName(...)
{
DashboardUno elEsUno;
... get the dashboard object ...
elEsUno.createLog(...);
...
}
}
Related
Is it bad practice to use dependency injection in factory classes? Should I let the users of my framework take care of dependency injection? Should I use approach A or approach B?
SomeUserClass
package com.impl;
#Service
public class SomeUserClass {
#Autowired
private SMSActionFactoryService actionFactoryService:
#Autowired
private PropertyManager properties;
public void doStuff(){
// approach A
SMSAction action = actionFactoryService.createAction("hello");
// approach B
action = SMSActionFactory.createAction(properties, "hello");
// the user should never call Action::doAction.
// It gets called by the framework on a condition.
scheduler.addAction(State.ERROR, action)
}
}
SMSAction
package com.framework;
public class SMSAction extends Action {
public SMSAction(PropertyManager properties, String message){
}
public void doAction(){
}
}
SMSActionFactoryService
package com.framework;
#Service
public class SMSActionFactoryService {
#Autowired
private PropertyManager properties;
public SMSActionFactory createAction(String message) {
return new SMSActionFactoryService(properties, message);
}
}
SMSActionFactory
package com.framework;
public class SMSActionFactory {
public static SMSActionFactory createAction(PropertyManager properties, String message) {
return new SMSActionFactory(properties, message);
}
}
I think you have a context problem, so the answer depends on the context. But I'll give some of my experience, and not a formal (and irrefutable) answer. Based on the title of the answer (practices) I'll give you what I call good practices tips that helped me a lot when I started Spring development.
First of all, let's think about the Dependency Injection you have. You're wiring a field, and we know that the Spring team used to suggest us to use constructor based injection (and assertions for all mandatory dependency) as you can see here. Well, I know it was a problem with the tests framework that couldn't wire the dependencies in an easy way, but now they can. But there's another advantage using this pattern, you can make your bean field final. Another advantage is that you prevent circular dependencies, like X depends on Y and Y depends on X and so on. So, as the first tip, I would suggest you to use something like:
private final SMSActionFactoryService actionFactoryService:
private final PropertyManager properties;
#Autowired
public SomeUserClass(SMSActionFactoryService actionFactoryService,
PropertyManager properties) {
Assert.notNull(actionFactoryService, "The actionFactoryService bean is null, you should provide the bean to run this application");
Assert.notNull(properties, "The properties bean is null, you should provide the bean to run this application");
this.actionFactoryService = actionFactoryService;
this.properties = properties;
}
This way you prevent any other code part to change the field value. As you can see in Spring autowiring setter/constructor PROs and CONs this is a preference subject.
Now, for the second tip, I wouldn't use #Service for a factory, not even #Component because factories needs to be open for extension and close for modification. You're going to understand better if take a look here.
That said friend, I suggest you to embrace approach B.
I have the next simple application(class Message has only one method which prints incoming message and has no interest for the question):
package messager.spring;
public class User {
private Messenger misiger;
private String name;
public User(String name) {
this.name = name;
}
public void setMessenger(Messenger messinger) {
this.misiger = messinger;
}
public void send(String mess) {
String message = name + " sent message " + "'" + mess + "'";
misiger.send(message);
}
// public String getname() {
// return name;
// }
}
Main class:
package messager.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
user.send("testing3...");
}
}
Spring configuration 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-2.5.xsd">
<bean id="user" class="messager.spring.User" autowire="byName">
<constructor-arg type="java.lang.String" value="Vova2"/>
</bean>
<bean id="messenger" class="messager.spring.MobileMessenger"/>
</beans>
I used autowiring for class User autowiring Messanger class. According to documentation :
When ByName is used, it then tries to match and wire its properties with the beans defined by the same names in the configuration file. If matches are found, it will inject those beans otherwise, it will throw exceptions.
This configuration works but I dont understand why((( I dont have property with name messenger inside User class((( I changed it on purpose to misiger. And it still works. It seems that bean id directly depends not on property name, but on setter name!!! Is it so?
YES, you are right. As described here :
Spring will lowercase the first letter after “set” in the method name and use the rest of the method name as-is for deducing the property name.
So not the member variable but the setter defines the property name.
That's how JavaBeans work, by looking at naming conventions.
The underlying reference isn't necessarily relevant.
The name of your property is messenger because there's a getter called that.
What would be a somewhat equivalent class for TransactionScope(.Net) in Spring.
I know absolutely nothing about .Net, so I'm not certain if this is what you're looking for. You can use SimpleTransactionScope in order to maintain objects across the lifecycle of a transaction. It is not registered by default, so you will have to register it with spring core like any custom scope, and give it a stringy name. Then if you want you can also create an annotation specifically to register one.
It was my issue that suggested this a few years ago, after this question was created for certain. We requested it specifically for timestamps across multiple methods for injection in a service. You can do something like this.
public class SimpleTransactionFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerScope("transaction", new SimpleTransactionScope());
}
}
#Configuration
class MyConfiguration {
#Scope(scopeName = "transaction")
#Bean
Instant nowInstant() {
return Instant.now();
}
}
#Service
class MyService {
private final ObjectFactory<Instant> nowFactory;
MyService( #Qualifier("nowInstant") ObjectFactory<Instant> nowFactory ) {
this.nowFactory = nowfactory
}
#Transactional
public boolean nowisEqualAlways() {
var now = nowFactory.getObject();
var sameNow = nowFactory.getObject();
return Objects.equals( now, sameNow );
}
}
If you don't do this, your now could actually change during your transaction by a small amount of time. You can test that simply by spamming now calls in a test.
It may not be required for your needs, so it's hard for me to tell (obviously your needs are probably long past, hopeful.y this helps someone in the future though)
The #Transactional annotation looks equivalent.
This can be placed on classes and methods and can be defined with propagation, isolation, rollback etc.
Consider the following controller method:
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(#RequestParam(value = "fq", required = false) String[] filterQuery) {
logger.debug(fq = " + StringUtils.join(filterQuery, "|"));
}
Here is the output for different fq combinations:
/test?fq=foo results in fq = foo
/test?fq=foo&fq=bar results in fq = foo|bar
/test?fq=foo,bar results in fq = foo|bar
/test?fq=foo,bar&fq=bash results in fq = foo,bar|bash
/test?fq=foo,bar&fq= results in fq = foo,bar|
Example 3 is the problem. I expect (want/need) it to output fq = foo,bar.
I've tried escaping the comma with \ and using %3C but niether work.
If I look at the HttpServletRequest object's version:
String[] fqs = request.getParameterValues("fq");
logger.debug(fqs = " + StringUtils.join(fqs, "|"));
It prints the expected output: fqs = foo,bar. So the "problem" is with the Spring data binding.
I could by-pass Spring's binding and use HttpServletRequest but I really don't want to as I'm using a backing bean in my real code (same thing is happening) and don't wish to re-implement the binding functionality. I'm hoping someone can provide a simple way of preventing this behavior via escaping or some other mechanism.
TIA
UPDATE: I posted this Q on Twitter and got a reply saying the expected output appears with Spring 3.0.4.RELEASE. I've now confirmed this is the case and thus is a temporary fix. I'll go ahead and log this as a bug on the Spring JIRA system. If anyone can provide a work around or fix with 3.0.5, I'll accept their answer.
I've tested your code: it's unbelievable, but I can't reproduce your issue. I've downloaded the latest version of spring (3.0.5), this is my controller:
package test;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/test/**")
public class MyController {
private static final Logger logger = Logger.getLogger(MyController.class);
#RequestMapping(value = "/test/params", method = RequestMethod.GET)
public void test(SearchRequestParams requestParams, BindingResult result) {
logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));
}
}
this is my SearchRequestParams class:
package test;
public class SearchRequestParams {
private String[] fq;
public String[] getFq() {
return fq;
}
public void setFq(String[] fq) {
this.fq = fq;
}
}
and this is my simple spring configuration:
<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="test.MyController" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
I've tested my code within tomcat 7.0.8; when I type http://localhost:8080/testweb/test/params.htm?fq=foo,bar I'm able to read in my log file this line: DEBUG fq = foo,bar.
What are the the differences from my code to yours? Am I doing something wrong?
I'd like to help you, so if you have any doubts or if I can do some other tests for you, it will be a pleasure.
UPDATE / SOLUTION
With your code I've reproduced the issue; you have the tag <mvc:annotation-driven /> in your dispatcher servlet configuration, so you silently use a default conversion service, instance of FormattingConversionService, which contains a default converter from String to String[] that uses comma as separator.
You have to use a different conversion service bean containing your own converter from String to String[]. You should use a different separator, I've choosed to use ";" because it's the separator commonly used into query string ("?first=1;second=2;third=3"):
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
public class CustomStringToArrayConverter implements Converter<String, String[]>{
#Override
public String[] convert(String source) {
return StringUtils.delimitedListToStringArray(source, ";");
}
}
Then you have to specify this conversion service bean in your configuration:
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="au.org.ala.testspringbinding.CustomStringToArrayConverter" />
</list>
</property>
</bean>
The issue has fixed, now you should check for any side effects. I hope you don't need in your application the original conversion from String to String[] (with comma as separator). ;-)
I have found the most elegant and the shortest way for me - add #InitBinder to a #Controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor(null));
}
It will convert String to String[] without using separator (null param), with Spring class org.springframework.beans.propertyeditors.StringArrayPropertyEditor.
If someone in same project will use new default conversion way, it will be ok.
As suggested by Philip Potter, I'm posting the "update" to my question as an answer, as it might've been easy to miss...
Down-grading from Spring 3.0.5.RELEASE to 3.0.4.RELEASE fixed the issue, when using the #RequestParam annotation, suggesting it is a bug with 3.0.5.
However, it does NOT fix the related issue, when binding to a form-backing bean - which is what I have in my webapp. I've tested all version back to 3.0.0.RELEASE and get the same result (/test?fq=foo,bar produces fq = foo|bar).
E.g.
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(SearchRequestParams requestParams, BindingResult result) {
logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));
}
where SearchRequestParams contains a field String[] fq.
If anyone has a fix for this, I'll gladly accept their answer.
rougou's comment was the only solution that worked for me because I was using List<String> instead of String[].
Add this to your controller to eliminate parsing comma-deliminated values:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(List.class, new CustomCollectionEditor(List.class));
}
javanna already pointed out the correct root cause. I just wanted to further point out that you can also remove the StringToArrayConverter altogether as shown here and here.
The following is my journey of bypassing comma delimiting in a MVC request
This is my understanding of Springs evolution of these type of solutions:
The documentation is very vague about what is the latest solution.
First was WebMvcConfigAdapter implementing the interface WebMvcConfig. This was eventually deprecated
That was replaced by WebMvcConfigSupport. This was eventually deprecated but it was better than the first solution.
The main issued was that it turned off the MVC auto-configuration and had side issues like swagger.html not working and
actuator info being missing pretty format and the date became a large decimal
The latest is a revised interface WebMvcConfig that implements default methods using Java 8 features.
Create a class in my case, WebConfigUUID could be AnyClass, that implements the later version of WebMvcConfig
This allows you to change what you need, in our case a custom converter,
without impacting anything else or having to override another to get swagger to work,
or deal with actuator info output
The following are the two classes that implemented my change to bypass comma delimiting of strings to a list
in processing the MVC request:
It still produces a list of strings but with only one value.
import java.util.Collection;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebConfigUUID implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.removeConvertible(String.class,Collection.class);
registry.addConverter(String.class,Collection.class,BypassCommaDelimiterConfiguration.commaDelimiterBypassedParsingConverter());
}
}
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
public class BypassCommaDelimiterConfiguration {
public static Converter<String, List<String>> commaDelimiterBypassedParsingConverter() {
return new Converter<String, List<String>>() {
#Override
public List<String> convert(final String source) {
final List<String> classes = new ArrayList<String>();
classes.add(source);
return classes;
}
};
}
}
Its a hack but, have you considered passing your params delimited with '-'
/test?fq=foo-bar results in fq = foo-bar
/test?fq=foo-bar&fq=bash results in fq = foo-bar|bash
Or any other delimiter maybe ~, or !,or ^, or ???
I have a wicket contact form, and i receive the form object. Now i need to pass this object to Spring Service.
package com.mysticcoders.mysticpaste.web.pages;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import com.mysticcoders.mysticpaste.model.Contact;
import org.apache.wicket.model.CompoundPropertyModel;
import com.mysticcoders.mysticpaste.services.IContact;
public class FormPage extends WebPage
{
private Contact contact;
private IContact icontact;
public FormPage()
{
// Add a FeedbackPanel for displaying our messages
FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
add(feedbackPanel);
Form<Object> form = new Form<Object>("contactForm",
new CompoundPropertyModel<Object>(contact))
{
private static final long serialVersionUID = 1L;
protected void onSubmit(Contact contact)
{
icontact.saveContact(contact);
}
};
form.add(new TextField<Object>("name"));
form.add(new TextField<Object>("email"));
form.add(new TextField<Object>("country"));
form.add(new TextField<Object>("age"));
add(form);
// add a simple text field that uses Input's 'text' property. Nothing
// can go wrong here
}
}
I am pretty much sure that we need to do something with application-context xml where i may need to wire out.
My Application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="WicketApplication" class="com.mysticcoders.mysticpaste.web.pages.WicketApplication" />
</beans>
My Question is simple.
What should i do which can make my
onSubmit method call the Spring
Service?
Could someone let me know what needs
to modified in my
Application-context.xml so that once
the form gets submitted, it contacts
the Spring Service class.
Wicket-Spring integration shows various ways on how to inject Spring Beans (e.g. your IContactService bean) into Wicket pages.
Basically, after configuration of the component injector, you end up with the following code:
public class FormPage extends WebPage
{
#SpringBean
private IContact icontact;
...
Form<Object> form = new Form<Object>("contactForm",
new CompoundPropertyModel<Object>(contact))
{
private static final long serialVersionUID = 1L;
protected void onSubmit(Contact contact)
{
icontact.saveContact(contact);
}
};
The #SpringBean answer by mhaller is of course valid and considered a best practice by many. But I prefer a more standard Spring approach, where your Wicket Application has the services you need.
public class YourWicketApp extends WebApplication{
public static YourWicketApp get(){
return (YourWicketApp) Application.get();
}
private ServiceA serviceA;
// getter and setter for serviceA here
}
Now in your component, call
YourWicketApp.get().getServiceA();
There are of course some drawbacks, the biggest one being that you can't easily test such a component without the application.