I made a project like this sample. So the controllers are like this
package mypackagename.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
#RequestMapping("/")
public class StoresRestController {
#RequestMapping(method = RequestMethod.GET)
public String stores() {
return ...
}
}
I like to handle all throwables and make my customized unified response. The problem is I cannot find a guide or a sample to do this correctly.
First of all, I tried ExceptionHandler, with Throwable, but it didn't work, so I decided to move on. Then, the most close approach that I found is this, so I tried jersey, by adding something like this. But it's not working for all throwables. Also, it's ignoring my controllers, by complaining
o.g.jersey.internal.inject.Providers : A provider mypackagename.controller.StoresRestController registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider mypackagename.controller.StoresRestController will be ignored.
I searched for this error and found this, which I'm not using ContainerResponseFilter in my project as I provided the sample above. So I'm clueless. The main problem is how to handle all throwables, but if you can give me some suggestions about how to solve Providers problem, I'll be so appreciated.
In my project I use #ControllerAdvice to handle my exceptions. Here's an example. Hope this helps. Just make sure this class is on your component scan so it gets picked up.
#RestController
#ControllerAdvice
public class StoresExceptionHandler {
#ExceptionHandler(Throwable.class)
public ResponseEntity<Object> handleThrowable(final Throwable ex) {
return new ResponseEntity<Object>("Unable to process request.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Finally this post helped me to handle all Throwables, except authentication exceptions. The important part was to use #EnableWebMvc and ResponseEntityExceptionHandler. To handle authentication exceptions I used this answer. Hope it's helping someone.
as #carlos-cook said, you could use a #ControllerAdvice and, ProblemDetail defined in RFC 7807 which could look like:
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ProblemDetail handleUnexpectedException(RuntimeException rte, WebRequest wr) {
ProblemDetail pd = this.createProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, rte);
pd.setType(URI.create("http://your-site.com/internal-server-error"));
pd.setTitle("Internal server error");
return pd;
}
#ExceptionHandler(YourCustomeException.class)
public ProblemDetail handleUnexpectedException(YourCustomException rte, WebRequest wr) {
ProblemDetail pd = this.createProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, rte);
pd.setType(URI.create("http://your-site.com/custom-error-page"));
pd.setTitle("Internal server error");
return pd;
}
}
Then in your controller you could simply throw YourCustomException
This controller advice will handle every exception and YourCustomException separately.
Related
I'm using get_it for IoC. However, my app throws an exception when I try to register a bean using an async call.
The function that throws the exception:
import 'package:elpee/service/localstorage_service.dart';
import 'package:get_it/get_it.dart';
GetIt locator = GetIt();
Future setupLocator() async {
LocalStorageService.getInstance().then((storageService) {
locator.registerSingleton(storageService);
});
}
The error:
Exception: Object of type LocalStorageService is not registered inside GetIt
If anyone could help me out, I'd greatly appreciate it :-)
You are trying to use get_it wrong way. Check to documentation.
First of all - get_it is Singleton, therefore don't use constructor, but
GetIt locator = GetIt.instance;
Second - implement LocalStorageService as a plain class and let the get_it to provide it as a Singleton:
void main()
...
void setupLocator() {
locator.registerLazySingleton(LocalStorageService());
}
}
To use full power of IoC define interface / abstract class definition for your storageservice and provide implementation of the interface.
void setupLocator() {
locator.registerLazySingleton<IStorageService>(LocalStorageService());
}
Originally I just had MyRestController
#CrossOrigin(origins = "*")
#RestController
public class MyRestController {
#RequestMapping(value = "/v1/endpoint", method = {RequestMethod.GET})
public ResponseEntity<Object> endpoint(HttpServletRequest request,
HttpServletResponse response) {
// etc - duplicate code across controllers with the one
// difference of a single function call and its corresponding params
}
}
Then I realized that a lot of the functionality was reused across 6 other controllers so I consolidated them using an abstract BaseController
abstract class BaseController {
public ResponseEntity<Object> run(String path, String[] params) {
Object result = null;
switch (path.toLowerCase()) {
// case for each path
case MY_PATH:
result = someService.myPath(param[0]);
break;
case MY_OTHER_PATH:
result = someService.myOtherPath(param[0], param[1]);
break;
default:
System.out.println("No");
throw new Exception();
}
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
and then I changed the class header for MyRestController to
public class MyRestController extends BaseController {
and this worked!
My questions are:
How come I couldn't move the CrossOrigin from MyRestController to BaseController ?
I was told to use an abstract class. Does this help at all in this use case?
I replaced the duplicate try / catch with a single function using the path in a switch statement to use the correct method with the correct params. This seems hackish... Is there a better way to do this?
Thanks
Looking at the documentation for the annotation CrossOrigin https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html
This is why it didn't work is because your BaseController doesn't have any methods to add to the RequestMappingHandlerMapping so that is why it didn't work.
As far as the BaseController being abstract it is not necessary unless you have a method that you want your extending controllers to overwrite.
Update about Switch
That is really up to you depending on how many controller methods are going to fall into each case. If multiple methods fall into the MY_PATH case then I believe you are ok, but I can see where the cases could turn into a very lengthy switch statement. It's really up to you as the maintainer. In my opinion, I would break the switch statement cases into different methods and let the controller that extend call that method. But that is personal preference.
In our project I have modules scout.client, scout.server, scout.shared and backend.
Backend has no dependencies to scout.server and scout.shared, but scout.server has dependencies to backend.
Inside backend project I have all business logic and calling all outside services.
My problem is when I try to test scout services that use some service from backend.
Because scout provide some great tool for mocking beans, we defined our service inside backend as beans as :
BEANS.getBeanManager().registerClass(CarService.class);
BEANS.getBeanManager().registerClass(PartnerService.class);
Both, CarService.class and PartnerService.class are in backend.
When I try to write some tests and I add #BeanMock to service in test
#BeanMock
private IPartnerService partnerService;
I get mock, but then every return every function is null, even if I write
doReturn(PartnerBuilder.standardPartnerListWithOneElement()).when(this.partnerService)
.getPartners(any(Set.class));
If I debug in my test, before this test is called with debugger I can get :
partnerService.getPartners(...) -> return a list of person
what is right, but when class that is tested calles this service it return null.
I understand that this could be due to missing annotation on interface #ApplicationScoped. Without this there is no guarantee that only one bean is created, and when statement react on another copy of that bean...?
I could not add annotation on interface because backend has no dependencies to scout modules.
How could I handle this kind of cases?
Tested class is :
public class UtilityPartner {
/**
* Method return service bean for getting partners by ids.
*
* #return
*/
private static IPartnerService getPartnerService() {
return BEANS.get(IPartnerService.class);
}
public static String getPartnerName(final Long partnerId) {
if (partnerId == null) {
return "";
}
final List<Partner> partners =
(List<Partner>) getPartnerService().getPartners(Sets.newHashSet(partnerId));
if (partners == null || partners.isEmpty()) {
return "";
}
final Partner partner = partners.get(0);
return LookupUtil.createLookupDescription(partner.getId(), partner.getName());
}
}
test class is :
#RunWith(ServerTestRunner.class)
#RunWithSubject("anonymous")
#RunWithServerSession(ServerSession.class)
public class TestUtilityPartner {
#BeanMock
private IPartnerService partnerService;
#Before
public void init() {
doReturn(PartnerBuilder.standardPartnerListWithOneElement()).when(this.partnerService).getPartners(any(Set.class));
}
#Test
public void getPartnerName() {
final String name = UtilityPartner.getPartnerName(10L);
Assert.assertEquals("My name", name); // NAME IS ""
}
}
Using #BeanMock does not help here, because you are not using an application scoped service:
In the init method you are changing the local field partnerService. However, in your test you call UtilityPartner.getPartnerService, which is creating a new instance (with BEANS.get(IPartnerService.class)).
#BeanMock is more useful for convenience for mocking application scoped beans.
You can always register your beans manually as shown by Jmini. Please do not forget to unregister the bean again after the test!
We recommend using org.eclipse.scout.rt.testing.shared.TestingUtility.registerBean(BeanMetaData), which is automatically adding a testing order and removing #TunnelToServer annotations.
I think that you should register your mock instance in the Bean manager (See bean registration in the Scout Architecture Document). You should use a small order (-10 000 is recommended for tests), in order for your mock to win over the productive registration. The best approach is to use the TestingUtility class to register/unregister your mock. Do not forget to call the unregisterBean() method (in the method annotated with #After):
import java.util.Collections;
import org.eclipse.scout.rt.platform.BeanMetaData;
import org.eclipse.scout.rt.platform.IBean;
import org.eclipse.scout.rt.testing.shared.TestingUtility;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class TestUtilityPartner {
private IBean<?> beanRegistration;
#Before
public void init() {
partnerService = Mockito.mock(IPartnerService.class);
// Register the mock using the Bean meta information:
BeanMetaData beanData = new BeanMetaData(IPartnerService.class)
.withInitialInstance(partnerService)
.withApplicationScoped(true);
this.beanRegistration = TestingUtility.registerBean(beanData);
// Mockito behavior:
Mockito.doReturn(Collections.singletonList(new Partner(34L, "John Smith")))
.when(partnerService).getPartners(Mockito.any(Set.class));
}
#After
public void after() {
// Unregister the mocked services:
TestingUtility.unregisterBean(this.beanRegistration);
}
#Test
public void getPartnerName() {
String name = UtilityPartner.getPartnerName(10L);
Assert.assertEquals("10 - John Smith", name);
}
}
I am not sure what #BeanMock (org.eclipse.scout.rt.testing.platform.mock.BeanMock) is doing, but according to Judith Gull's answer it will not work:
Using #BeanMock does not help here, because you are not using an application scoped service:
In the init method you are changing the local field partnerService. However, in your test you call UtilityPartner.getPartnerService, which is creating a new instance (with BEANS.get(IPartnerService.class)).
#BeanMock is more useful for convenience for mocking application scoped beans.
I'm making a mod, and I am getting an error(no duh) and I have tried searching it up but I want an answer specific to my problem because I am not very good at this. I am getting this error in my block class.
Implicit super constructor Block() is undefined for default constructor. Must define an explicit constructor
and I don't know how to fix it. Please Help its for a project.
block class:
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
public class BlockCbBlock extends Block {
protected BlockCbBlock(Material material) {
super(material);
}
}
mod class:
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
#Mod(modid = "fc", name = "Fandomcraft", version = "1.0")
public class fandomcraft {
public static Item itemCbIngot;
public static Block blockCbBlock;
#EventHandler
public void preInit(FMLPreInitializationEvent event){
//Item/Block initialization and registering
//Config Handling
itemCbIngot = new ItemCbIngot().setUnlocalizedName("ItemCbIngot").setTextureName("fc:itemCbIngot"); //item.itemCbIngot.name
blockCbBlock = new BlockCbBlock(Material.iron);
GameRegistry.registerItem(itemCbIngot, itemCbIngot.getUnlocalizedName().substring(5));
}
#EventHandler
public void init(FMLInitializationEvent event){
//Proxy, TileEntity, entity, GUI and Packet Registering
}
#EventHandler
public void postInit(FMLPostInitializationEvent event) {
}
}
This error pertains to all of java, not just minecraft forge. Check this for some more reference. There are a couple possible reasons for this error. It is most likely 1, but 2 and 3 can be a contributing factor to the error.
Your BlockCbBlock Class declares a constructor that is not the default, no-argument constructor that the compiler would otherwise provide (that is, if the Block class doesn't have a constructor) and, if in fact the Block class is using the default constructor, then you can't call super() on the arguements because the Block class uses a constructor with no arguments. Because of this, if you wanted to modify the Block constructor, it would be safier and easier to create a custom construcotr inside of the BlockCbBlock class itself.
You are trying to inherit the constructor of Block, but you have declared it as protected, when the constructor in your class should be public to match the inherited .
If you're using Eclipse, it can give this error when you have your project setup incorrectly (system configuration mismatch)
Probably not directly realted to this specific error, but a possible cause of other errors in the near future; you are using the annotation #EventHandler, but you have not actually declared the forge event handler.
You don't actually register the block for some reason. Even if you're using the block as a recipe item, you still need to register it
To fix potential problems 1, 2, and 4, try this (obtained from here):
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
private final String name = "BlockCbBlock";
public class BlockCbBlock extends Block {
public BlockCbBlock() {
super(Material.iron);
GameRegistry.registerBlock(this, name);
setUnlocalizedName(Reference.MODID + "_" + name);
setCreativeTab(CreativeTabs.tabBlock);
}
public String getName() {
return name;
}
}
This way, you'll declare its UnlocalizedName, Material, and CreativeTab ahead of time. This method might be unnecessary, but its a good precaution to help prevent the error. Now, all you have to do is declare it like this:
//You need to make your own EventHandler class. Search online for that.
FCEventHandler handler = new FCEventHandler();
#EventHandler
public void preInit(FMLPreInitializationEvent event){
//Config Handling
//event handler registry
FMLCommonHandler.instance().bus().register(handler);
MinecraftForge.EVENT_BUS.register(handler);
//the same thing can be similarly done with this if you wish
itemCbIngot = new ItemCbIngot().setUnlocalizedName("ItemCbIngot").setTextureName("fc:itemCbIngot");
blockCbBlock = new BlockCbBlock();
GameRegistry.registerItem(itemCbIngot, itemCbIngot.getUnlocalizedName().substring(5));
}
We're a Spring Boot shop and rely heavily on Spring MVC for our REST endpoints. We use Boot and embedded Tomcat to create a self-hosting JAR. Is it possible to replace Tomcat with Ratback while still keeping all my Spring MVC code in place? I am afraid that Spring MVC is tied into the servlet specification somehow and will not run without a servlet container. I am aware of dsyer/spring-boot-ratpack work but after skimming the code couldn't decide if Spring MVC would play well using the bridge. Is anyone aware of any work that will allow us to retain our investment in Spring MVC and have Spring Boot use Ratpack to manage HTTP traffic?
I suspect the crux of your question can be distilled to: "can we put our Spring controllers on top of Ratpack's non-blocking HTTP layer?" and the simplest answer to that question is no, for reason that the MVC programming model doesn't fit well into the reactive/NIO model very well.
However, if your application has followed some common model-view-controller-(and service) patterns, then your controllers should really just be performing data binding and parsing and delegating out to a service layer. If that's the case, then likely the code in your controller is already non-blocking, and you could easily translate it to Ratpack code.
As an example, consider the following #RestController in a Spring Boot app:
#RestController
#RequestMapping("/user")
class UserController {
#Autowired
UserService userService
#RequestMapping(method = RequestMethod.POST)
Long create(#RequestBody #Valid User user) {
User savedUser = userService.save(user)
return savedUser.id
}
}
Spring's data binding aspect is a computation process (ie isn't I/O bound), so we can easily translate this into a Ratpack handler:
import app.SpringConfig
import app.User
import app.UserService
import org.springframework.boot.SpringApplication
import org.springframework.context.ApplicationContext
import ratpack.jackson.JacksonModule
import static ratpack.groovy.Groovy.ratpack
import static ratpack.jackson.Jackson.fromJson
import static ratpack.jackson.Jackson.json
import static ratpack.spring.Spring.spring
ratpack {
bindings {
add(new JacksonModule())
bindInstance(ApplicationContext, SpringApplication.run(SpringConfig))
}
handlers { ApplicationContext ctx ->
register(spring(ctx))
prefix("user") {
handler { UserService userService ->
byMethod {
post {
def user = parse(fromJson(User))
blocking {
userService.save(user)
} then { User savedUser ->
render(json(savedUser))
}
}
}
}
}
}
}
Where SpringConfig looks like this:
package app
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class SpringConfig {
#Bean
UserService userService() {
new UserService()
}
}
And here's a functional test to prove it:
package app
import com.fasterxml.jackson.databind.ObjectMapper
import ratpack.groovy.test.GroovyRatpackMainApplicationUnderTest
import ratpack.test.ApplicationUnderTest
import ratpack.test.http.TestHttpClient
import spock.lang.Shared
import spock.lang.Specification
import static groovy.json.JsonOutput.toJson
class FuncSpec extends Specification {
#Shared ApplicationUnderTest aut = new GroovyRatpackMainApplicationUnderTest()
#Shared ObjectMapper mapper = new ObjectMapper()
#Delegate TestHttpClient client = aut.httpClient
def "should parse and save user"() {
given:
def user = new User(username: "dan", email: "danielpwoods#gmail.com")
when:
requestSpec { spec ->
spec.body { b ->
b.type("application/json")
b.text(toJson(user))
}
}
post('user')
then:
def savedUser = mapper.readValue(response.body.text, User)
and:
savedUser.id
}
}
Hope this helps!
The Spring MVC programming model is not very heavily dependent on Servlet APIs, but it's not supported in any other containers (i.e. not in Ratpack). There is some async stuff there now and Servlet 3.1 enhances it some more, so if that's the part of Ratpack that attracts you, maybe just using that would be a better approach. You won't get all the way to reactive and non-blocking IO that way though.