In my Spring MVC application I have a 'DrawingController' that accepts MultipartHttpRequests from clients.
A user can upload any type of drawing( for the time being only auto cad and bim drawings) from the front end.
There is an interface called 'DrawingService' and two implementations 'BIMDrawingService' and 'CADDrawingService' as follows.
public interface DrawingService{
public String manageUpload();
}
#Component("bimService")
public class BIMDrawingService implements DrawingService{
public String manageUpload() {//}
}
#Component("cadService")
public class CADDrawingService implements DrawingService{
public String manageUpload() {//}
}
public class DrawingController {
#Autowired
#Qualifier("bimService")
private DrawingService bimService;
#Autowired
#Qualifier("cadService")
private DrawingService cadService;
public void setDrawingService(DrawingService bimService) {
this.bimService= bimService;
}
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public #ResponseBody String handleFileUpload(MultipartHttpServletRequest request){
//if request type == BIM, then ignore the logic how I differentiate
if bimrequest
bimService.manageupload()
else if cadrequest
cadService.manageupload()
}
I feel this is not the good way to do and my question is how I can inject the services dynamically at run time, even If I add new drawing services later, with the minimal changes I should be able to progress. Please suggest me some best design solution.
Related
I have 2 APIs
1. localhost:8080/myservice/foo/1/0/updatesStatus
2. localhost:8080/myservice/bar/1/0/updatesStatus
I am not allowed to have different controllers for each API. So both the APIs are pointing to the same controller method where I have if-else check, but the code looks very bad in that, is there any better way to handle this.
#PostMapping(value = UPDATE_STATUS_API_PATH)
public Response updateStatus(#PathVariable("group") String group , #RequestBody UpdateStatusRequest updateStatusRequest, HttpServletRequest request) {
try {
if(APIUrl.FOO_GROUP.equals(group)){
//code spefic to foo
}
else{
//code spefic to bar
}
//common code
}
The same conditional checks also have to be performed on the service layer as well.
Is there any way I can avoid this conditional checks without having separate controller methods.
I can think of this.
Create an interface for service.
public interface UpdateService {
void updateStatus(UpdateStatusRequest updateStatusRequest);
}
Then you create different implementations.
public class FooUpdateService implements UpdateService {
void updateStatus(UpdateStatusRequest updateStatusRequest) {
// foo specific logic
}
}
public class BarUpdateService implements UpdateService {
void updateStatus(UpdateStatusRequest updateStatusRequest) {
// Bar specific logic
}
}
Create a UpdateServiceFactory
public class UpdateServiceFactory {
#Autowired
private UpdateService fooUpdateService;
#Autowired
private UpdateService fooUpdateService;
public UpdateService getUpdateService(String group) {
// Move the if-else logic here
if(APIUrl.FOO_GROUP.equals(group)){
return fooUpdateService;
}
else{
//code spefic to bar
return barUpdateService;
}
}
}
Controller:
#PostMapping(value = UPDATE_STATUS_API_PATH)
public Response updateStatus(#PathVariable("group") String group , #RequestBody UpdateStatusRequest updateStatusRequest, HttpServletRequest request) {
updateServiceFactory.getUpdateService(group).updateStatus(updateStatusRequest);
//common code
}
I am currently working on a Spring Boot application that allows users to save categories into a database. I can get my code to "work", however, I think it limits the amount of testing I can do, hence my question.
The controller receives a list of categories. The controller iterates over these categories, validates them and depending on whether they are valid, they are saved to a database. The controller finally returns a list of messages, such that the recipient can identify which category has been accepted or rejected, etc.
I have a list of model messages (List), which upon each iteration, the controller instantiates a new model message (new ModelMessage()) and eventually adds it to the List. Is there a way to inject a new ModelMessage upon each iteration or do I need to use the new keyword? If I do use the new keyword, I feel like this is limiting my testability/tightly coupling my controller to the model message.
The controller:
#PostMapping("/category")
public String saveCategoryModelToDatabase(#RequestBody CategoryModelWrapper categoryModelWrapper){
List<CategoryModel> categoryModelList = categoryModelWrapper.getCategoryModelList();
modelMessageList.clear();
for(CategoryModel categoryModel : categoryModelList){
//Resetting model
modelMessage = new ModelMessage(); //This tightly couples my method to the ModelMessage class, which is bad for testing?
//#Autowired modelMessage; <-- something like this? Inject a new ModelMessage with each iteration.
modelMessage.setName(categoryModel.getName());
//Resetting categoryModelErrors
Errors categoryModelErrors = new BeanPropertyBindingResult(categoryModel, "categoryModel");
categoryModelValidator.validate(categoryModel, categoryModelErrors);
if(categoryModelErrors.hasErrors()){
modelMessage.setStatus(ModelMessageStatusEnum.REJECTED);
modelMessage.setReason(MODEL_MESSAGE_0004);
}
if(categoryModelService.save(categoryModel)){
modelMessage.setStatus(ModelMessageStatusEnum.ACCEPTED);
}
else{
modelMessage.setStatus(ModelMessageStatusEnum.REJECTED);
modelMessage.setReason(MODEL_MESSAGE_0005);
}
modelMessageList.add(modelMessage);
}
return gson.toJson(modelMessageList);
}
An example of the response to the recipient:
[{"name":"Arts","status":"ACCEPTED"},{"name":"Business","status":"ACCEPTED"},{"name":"Gaming","status":"ACCEPTED"},{"name":"Deals","status":"REJECTED","reason":"Category rejected because of an unexpected exception, i.e. possibly due to duplicate keys."}]
Thanks for any help :)
You could use the ApplicationContext, assuming you have access to it, as a factory for ModelMessage. But, is that really necessary?
I think you can create new ModelMessages in your Controller, it's only a data object and not a service bean.
A junit can check the result of the method.
But if you really want to use Spring, I would look at the FactoryBean...
Example:
public class ModelMessage {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Component
public class ModelMessageFactory implements FactoryBean<ModelMessage> {
#Override
public ModelMessage getObject() throws Exception {
return new ModelMessage();
}
#Override
public Class<?> getObjectType() {
return ModelMessage.class;
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
#SpringJUnitConfig
public class ModelMessageFactoryTest {
#Autowired
private ModelMessageFactory messageFactory;
#Test
public void testGetObject() throws Exception {
assertNotNull("Factory is null", messageFactory);
IModelMessage modelMessage1 = messageFactory.getObject();
IModelMessage modelMessage2 = messageFactory.getObject();
assertNotEquals("error object is equal", System.identityHashCode(modelMessage1),
System.identityHashCode(modelMessage2));
}
#Test
public void testGetObjectType() throws Exception {
assertEquals(ModelMessage.class, messageFactory.getObjectType());
}
}
I created one factory to decide what best implementation should be returned, based in some conditional check.
// Factory
#Component
public class StoreServiceFactory {
#Autowired
private List<StoreService> storeServices;
public StoreService getService(){
if(isActiveSale){
return storeServices.get("PublicStoreService")
}
return storeServices.get("PrivateStoreService")
}
}
//Service Implementations
#Service
#Qualifier("PublicStoreService")
public class PublicStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
#Service
#Qualifier("PrivateStoreService")
public class PrivateStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService();
return simulationService.simulate(sellerId, simulation);
}
Is this approach good? If yes, how can i get my service from an elegant way?
I would like to use only annotations, without configurations.
You should use a map instead of a List and pass a string parameter to the getService method.
public class StoreServiceFactory {
#Autowired
private Map<String,StoreService> storeServices = new HashMap<>();
public StoreService getService(String serviceName){
if(some condition...){
// want to return specific implementation on storeServices map, but using #Qualifier os something else
storeServices.get(serviceName)
}
}
}
You can prepopulate the map with supported implementations. You can then get an appropriate service instance as follows :
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService("private");//not sure but you could pass storeId as a parameter to getService
return simulationService.simulate(sellerId, simulation);
}
If you don't like using Strings, you can define an enum for the supported implementations and use that as the key for your map.
You don't need to create a list or map on your code. You can retrieve it directly from Spring context using GenericBeanFactoryAccessor. This has various method to retrieve a specific bean like based on name, annotation etc. You can take a look at javadoc here. This avoids unnecessary complexity.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/beans/factory/generic/GenericBeanFactoryAccessor.html
I'm looking for a way to customize the default Spring MVC parameter binding. Take this method as an example:
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#RequestParam String param) {
...
This is easy, when I have just a Stringthat I want to extract from the request. However, I want to populate a more complete object, so that my method looks like this:
#RequestMapping(value="/index.html")
public ModelAndView doIndex(Foo bar) {
...
What I'm looking for is some way to declare a binding like this;
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#FooPopulator Foo bar) {
...
And have some other kind of implementor (determined by the #FooPopulator annotation) that does this:
public void doBind(Foo target, ServletRequest originalRequest) {
target.setX(this.computeStuffBasedOn(originalRequest));
target.sety(y);
}
So far I've found out about the #InitBinderbinder annotaion but I'm unsure whether that's really the right choice for this scenarion.
What's the best way?
It is very easy. You can use Converters (that work like one way PropertyEditors but are stateless).
See chapter 5.5 Spring 3 Type Conversion in Spring reference.
If such an converter is registered once, you do not need any additional information, you can simply use
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#RequestParam Foo param) {
For example a simple converter that load an object by its id:
#Component
#CustomConverter //custom qualifyer
public class BUdToUserConverter implements Converter<String, User> {
#Resource
private UserDao userDao;
#Override
public User convert(String source) {
Integer id = Integer.parse(source);
return this.userDao.getByBusinessId(id);
}
}
A "helper" that registers all Beans with #CustomConverter anntoation
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {
#Resource
#CustomConverter
private List<Converter<?, ?>> customConverter;
#Override
protected void installFormatters(final FormatterRegistry registry) {
super.installFormatters(registry);
for (Converter<?, ?> converter : customConverter) {
registry.addConverter(converter);
}
}
}
How to use it
UserController {
...
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView show(#PathVariable("id") User user) {
return new ModelAndView("users/show", "user", user);
}
}
just a quick thank you and the info, that I've found the "correct" solution to the problem. Spring already provides the WebArgumentResolver for this scenario.
http://sergialmar.wordpress.com/2011/03/29/extending-handler-method-argument-resolution-in-spring-mvc/
http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html
Simple and short question: Is there a way to create a handler for custom #RequestParam types in Spring MVC?
I know I can register custom WebArgumentResolvers but then I cannot bind these to parameters. Let me describe my use case:
Consider I have defined a Model Class Account:
public class Account {
private int id;
private String name;
private String email;
}
My request handling method looks as follows:
#RequestMapping("/mycontroller")
public void test(Account account1, Account account2) {
//...
}
If I make a request mydomain.com/mycontroller?account1=23&account2=12 I would like to automatically load the Account objects from the database and return an error if they dont exist.
Yes, you should just register a custom property editor:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(CustomType.class,
new CustomTypePropertyEditor());
}
Update: Since you need to access the DAO, you need the property editor as a spring bean. Something like:
#Component
public class AccountPropertyEditor extends PropertyEditorSupport {
#Inject
private AccountDAO accountDao;
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(accountDao.getById(Integer.parseInt(text)));
}
#Override
public String getAsText() {
return String.valueOf(((Account) getValue()).getId());
}
}
And then, when registering the editor, get the editor via injection rather than instantiating it.