I am trying to return a Single.just(..) from my endpoint.
I have created it using jersey and rx-jersey.
I keep getting this message on my browser:
No serializer found for class io.reactivex.internal.operators.single.SingleJust and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
Here is my code:-
JerseyCOnfig:
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(RxJerseyServerFeature.class);
register(RxJerseyClientFeature.class);
register(new JacksonJsonProvider(new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)));
register(UserService.class);
}
}
My End point
#Path("/users")
public class UserService {
#GET
#Path("/setup/rx")
#Produces(MediaType.APPLICATION_JSON)
public Single<User> setupRx() {
return Single.just(new User(29));
}
}
User:-
public class User {
private Integer age;
//getters and settters
It doesn't make sense for the jersey service to return any kind of observable. You would use chains of observables to produce concrete files or pages, but not observables.
In your case, the result of the GET method would be User, not instructions on how to get User.
Related
I'm trying to receive message through Grpc service, send it to Kafka Emitter, and return some value back.
#Singleton
#GrpcService
public class MessageService implements protobuf.MessageService{
#Inject
#Channel("hello-out")
Emitter<Record<String, GeneratedMessageV3>> emitter;
#Override
public Uni<EnvelopeReply> processMessage(Envelope request) {
return Uni.createFrom().completionStage(
emitter.send(Record.of(request.getKey(), request))
).replaceWith(EnvelopeReply.newBuilder().build());
}
}
During build, I'm getting next error:
Error injecting org.eclipse.microprofile.reactive.messaging.Emitter<io.smallrye.reactive.messaging.kafka.Record<java.lang.String, com.google.protobuf.GeneratedMessageV3>> com.test.MessageService.emitter
...
Caused by: javax.enterprise.inject.spi.DefinitionException: SRMSG00019: Unable to connect an emitter with the channel `hello-out`
It works properly with Rest resource.
Without going deeply into the topic, here's my solution:
You can't inject Kafka Emmiter directly to grpc service, it'll throw an exception.
GrpcService <- Emitter<Record...>
Possible reason(I'm sure Quarkus team will reply lower with correct solution :)) is that all GrpcServices are of #Singleton type, and they can't have lazy-initialised properties, they need to have something directly injected. Emitter is generated at a later stage.
By adding a wrapper class you're solving all the headaches, so:
GrpcService <- KafkaService <- Emitter<Record...>
#ApplicationScoped
public class KafkaService {
#Inject
#Channel("hello-out")
Emitter<Record<String, GeneratedMessageV3>> emitter;
// Implement this part properly, added just for example
public Emitter<Record<String, GeneratedMessageV3>> getEmitter() {
return emitter;
}
}
...
#Singleton
#GrpcService
public class MessageService implements protobuf.MessageService {
#Inject
KafkaService kafkaService;
#Override
public Uni<EnvelopeReply> processMessage(Envelope request) {
// use metadata if needed
Map<String, String> metadataMap = request.getMetadataMap();
return Uni.createFrom().completionStage(
kafkaService.getEmitter().send(Record.of(request.getKey(), request))
).replaceWith(EnvelopeReply.newBuilder().build());
}
}
I have a custom validator class that implements Validator, like this:
public class MyCustomValidator implements Validator
I want to be able to call its validate() method from a Service.
This is how this method looks:
#Override
public void validate(Object target, Errors errors) {
// validation goes here
MyClass request = (MyClass) target;
if (request.getId() == null) {
errors.reject("content.id", "Id is missing";
}
}
I don't want to have this validator in my endpoint, because I need to fetch the object to be validated from the database and then call the validation on it, so I need to do it from my service.
Can you please guide me on how to achieve this?
Use validation annotations in class but don't use #Valid on request body, then spring won't validate your class.
public class MyClass{
#NotNull
private Integer id;
#NotBlank
private String data;
}
Autowired Validator first
#Autowired
private final Validator validator;
Then for class validate using the validator conditionally when needed.
if(isValidate) {
Set<ConstraintViolation<MyClass>> violations = validator.validate(myClassObj);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations));
}
}
The Validator interface is, as far as i understand it, called as soon as a matching object (determined by the public boolean Validator.supports(Class clazz) method).
However, your goal seems to be to validate an object of MyClass only at a specific time, coming from your persistence layer to your service layer.
There are multiple ways to achieve this.
The first and most obvious one is to not extend any classes, but to use a custom component with some notion of a validation function:
#Component
public class CustomValidator{
public void validate(MyClass target) throws ValidationException {
// validation goes here
if (target.getId() == null) {
throw new ValidationException("Id is missing");
}
}
}
And inject/autowire it into your service object:
#Component
public class MyClassService{
// will be injected in first instance of this component
#Autowired
private CustomValidator validator
public MyClass get(MyClass target) {
try {
validator.validate(target);
return dao.retrieve(target);
} catch (ValidationException) {
// handle validation error
} catch (DataAccessException) {
// handle dao exception
}
}
}
This has the benefit that you yourself can control the validation, and error handling.
The negative side is the relatively high boilerplate.
However, if you want different Validators for different CRUD-Operations (or Service Methods), you may be interested in the Spring Validation Groups Feature.
First, you create a simple marker interface for each Operation you want to differ:
interface OnCreate {};
interface OnUpdate {};
Then, all you need to do is use the marker interfaces in the fields of your entity class,
using the Bean Validation Annotations:
public class MyClass{
#Null(groups = OnCreate.class)
#NotNull(groups = OnUpdate.class)
String id;
}
In order to use those groups in your Service Class, you will have to use the #Validated annotation.
#Validated
#Service
public class MyService {
#Validated(OnCreate.class)
void validateForCreate(#Valid InputWithGroups input){
// do something
}
#Validated(OnUpdate.class)
void validateForUpdate(#Valid InputWithGroups input){
// do something
}
}
Note that #Validated is applied to the service class as well as the methods. You can also set the group for the whole service, if you plan on using multiple services.
I for once mostly use the built-in Jakarta Bean Validation annotations in combination with marker interfaces, because of their ease of use and almost no boilerplate, while staying somewhat flexible and adjustable.
You could inject Validator and call validate
#Autowired
Validator validator;
And then call validate:
Set<ConstraintViolation<Driver>> violations = validator.validate(yourObjectToValidate);
I've developed a rest api using Spring Boot. In one of my service methods, I throw a ServletException, in case a specific user is not found. I am wondering if that is the best way to do that, I mean, is that the right layer to thrown the exception?
Creating a custom exception type is a better idea than using ServletException.
In order to handle an exception you can use #ControllerAdvice.
First create custom exception type:
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Assuming that your controller and service look more or less like this:
#RestController
#RequestMapping("users")
class UserController {
private final UserService userService;
UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
List<String> users() {
return userService.getUsers();
}
}
#Service
class UserService {
List<String> getUsers() {
// ...
throw new UserNotFoundException("User not found");
}
}
You can handle you UserNotFoundException using #ControllerAdvice
#ControllerAdvice
class CustomExceptionHandler {
#ExceptionHandler({UserNotFoundException.class})
public ResponseEntity<Object> handleUserNotFoundException(UserNotFoundException exception) {
return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND);
}
}
One of best way or what I do is,
Check data / parameters for valid data( e.g Null check sometime manually using if statement).
Checking data / parameters for size (like file size)
checking data or parameters for valid range also data type, do typecasting if not in valid type (like String, Long, Integer etc).
Raise a message and return to that API, if the data / parameters are not valid before system raise an exception
I am assuming you are looking to catch all exceptions occured inside your application. Spring-Boot provides a Global Exception Handler to catch all the exceptions gracefully and return response according to the the specific Exception.
It gives you flexibility to change the status codes, response data, headers accordingly. Few useful links to implement this feature is -
1.) Dzone
2.) Spring Boot Tutorial
Throwing exception in your #Service is okay. ServletException is not super meaningful. What I would suggest is to create your own Exception class extending RuntimeException and throw it.
So you would end up with something like that:
A Controller that only calls a service method (better not to have any
logic here)
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/{id}")
public User getUserById(#PathVariable("id") Long id) {
return userService.getById(id);
}
}
A Service class that calls DAO class (extending JPARepository)
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public User getById(Long id) {
return userDAO.findById(id).orElseThrow(() -> new UserNotFoundException("No user with id = " + id + " found."));
}
}
DAO:
#Repository
public interface UserDAO extends JpaRepository<User, Long> {
}
note: it returns Optional<Object> which is very convinient.
And finally your own Exception class.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Note: #ResponseStatus - it is going to return HTTP Status code 404 on throwing this exception.
This is imho a very clean and good way to develop your rest api.
Also take a look here: How to get spesific error instead of Internal Service Error . I answered a question providing information you might find useful
[EDIT] The problem is with the
register(new ServiceBinder<>(MyService.class));
Jersey generates a warning and ignores the registration for all but the first one (Existing previous registration found for the type); it only considers the type-erased ServiceBinder class to decide there is a conflict.
It looks like I need to use a more sophisticated version of register to get past that issue.
[/EDIT]
In Jersey 1 I was able to use custom injectable providers to inject my objects into both class fields and method parameters, by extending
LazySingletonInjectableProvider
I can't figure out how to port that pattern to Jersey 2 (with hk2 on Tomcat 7). I have read everything I could find on the topic, including Jersey custom method parameter injection with inbuild injection - but I don't want to use a custom annotation, and I am not trying to inject a request parameter.
[EDIT] I made the wrong assumption regarding what works and what doesn't:
Injection into a class field in a ContainerRequestFilter works fine
Injection into a resource, either as class field or method parameter does not work
[EDIT 2]: The InjectionResolver as described below actually doesn't work at all, I have removed it. Jersey already has a ContextInjectionResolver which presumably should take care of the #Context annotation.
I have created and registered an AbstractBinder, and with that class field injection works fine; however method parameter injection doesn't (the binder never gets invoked and the parameter remains null).
I have tried to bind an InjectionResolver but that didn't help either.
Any suggestion on how to make this work would be greatly appreciated... here is the current code:
The HK2 binder:
public class ServiceBinder<T> extends AbstractBinder
{
private final Factory<T> _factory;
private final Class<? extends T> _clazz;
public OsgiServiceBinder(Class<T> clazz)
{
_factory = new ServiceFactory<>(clazz);
_clazz = clazz;
}
protected void configure()
{
bindFactory(_factory).to(_clazz); //.in(RequestScoped.class);
bind(ServiceInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<Context>>() { })
.in(PerLookup.class);
}
}
The injection resolver:
public class ServiceInjectionResolver<T> implements InjectionResolver<Context>
{
private Class<T> _clazz;
public OsgiServiceInjectionResolver(Class<T> clazz)
{
_clazz = clazz;
}
public Object resolve(Injectee injectee, ServiceHandle<?> root)
{
if (_clazz.getCanonicalName().equals(injectee.getRequiredType().getTypeName())) {
return Framework.getService(_clazz);
}
return null;
}
public boolean isConstructorParameterIndicator()
{
return false;
}
public boolean isMethodParameterIndicator()
{
return true;
}
}
The JAX-RS registration:
public class MyApplication extends Application
{
public MyApplication()
{
registerClasses(<resource classes>);
register(new ServiceBinder<>(MyService.class));
}
}
The resource class:
#Path("/schedules")
public class SchedulesResource
{
#Context UriInfo _uriInfo;
// This injection works fine, _service1 is properly initialized
#Context MyService _service1;
#PUT
#Consumes({MediaType.APPLICATION_JSON})
#Path("{jobGroup}/{jobName}")
public Response putSchedule(#Context MyService service2,
...)
{
// The injection of service2 doesn't work...
}
}
The Factory class:
public class ServiceFactory<T> implements Factory<T>
{
private Class<T> _clazz;
protected ServiceFactory(Class<T> clazz)
{
_clazz = clazz;
}
public T provide()
{
return Framework.getService(_clazz);
}
}
public void dispose(T t)
{
}
}
pok
The problem was actually with Jersey component registrations.
Even though I was registering binder instances, Jersey was checking the class (ServiceBinder) and discarding all but the first registration (WARN: existing registration found for the type).
This seems a bit bogus given I am registering instances, and I wish Jersey would fail with an error rather than log a warning when failing to register a component, but the solution is to simply change the registration pattern slightly:
// Doesn't work
register(new ServiceBinder<>(MyService1.class));
register(new ServiceBinder<>(MyService2.class));
// Works like a charm
register(new ServiceBinder(MyService1.class, MyService2.class));
where obviously the ServiceBinder is adjusted to call bindFactory for each supplied service.
EDIT: I just realized, is it even possible to perform a custom action with a custom attribute in Java? Or is it just informational?
I want to include an authentication token in my Jax-RS service header, but I don't want to add a parameter to every request to get the header and check it like so:
public Response getUser(#Context HttpHeaders headers) {
if(authorize(headers.getRequestHeader("token").get(0)) {
// Do something
}
}
I would much rather add an attribute to each request (or even the class if that is possible:
#Authorize
public Response getUser() {
// Do something
}
This way I can also add the attribute to only the requests I want to.
And if the request isn't authorized, I can override it and return a 401.
A custom attribute is easy to write, but how can I get the header information in the attribute without passing it in every time?
NOTE: I would rather not use a web.xml. I don't have one right now and I don't like using them. I want to keep my code clean without xml and I think if I used a filter/web.xml it would apply to all calls. If that is the only way, I will, but I much prefer the approach with custom attributes.
"I think if I used a filter/web.xml it would apply to all calls"
Actually there are #NameBinding annotations we can use. For example
#NameBinding
#Rentention(RetentionPoilicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Authorize {}
Then just annotate the filter and the methods/classes you want filtered.
#Authorize
public Response getUser() {
// Do something
}
#Provider
#Authorize
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
MultivauledMap<String, String> headers - requestContext.getHeaders();
...
if (!authorized) {
throw new NotAuthorizedException();
}
}
}
Notice the use of #Priority. This is important. Say you want the authenticate also, so you create a filter for authentication. If you don't set the priority, either filter may occur first. It's unpredictable. If we provide the authentication filter with #Priority(Priorities.AUTHENTICATION), then that filter will always occur before the #Priority(Priorities.AUTHORIZATION) filter.
You will also need to register this filter with the Application subclass (See some other Deployment Options (Jersey, but the Application subclass is portable with implementations))
#ApplicationPath("/api")
public class YourApplication extends Application {
private Set<Class<?>> classes = new HashSet<>();
private Set<Object> singletons = new HashSet<>();
public YourApplication() {
classes.add(AuthorizationRequestFilter.class);
}
#Override
public Set<Class<?>> getClasses() {
return classes;
}
#Override
public Set<Object> singletons() {
return singletons;
}
}
See more on Filters and Interceptors
See the WebAppplicationException Hierarchy for more exceptions like NotAuthorizedException
See the Priorities class and Priories guide
The best way to solve your use case would be to use name binding and filters. In this way, you can use a filter to do your authorization logic as well as return a 401 in the case of unauthorized requests.
You can find more information here.
Name binding via annotations is only supported as part of the Server API. In name binding, a name-binding annotation is first defined using the #NameBinding meta-annotation:
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(value = RetentionPolicy.RUNTIME)
#NameBinding
public #interface Logged { }
The defined name-binding annotation is then used to decorate a filter or interceptor class (more than one filter or interceptor may be decorated with the same name-binding annotation):
#Logged
public class LoggingFilter
implements ContainerRequestFilter, ContainerResponseFilter {
...
}
At last, the name-binding annotation is applied to the resource method(s) to which the name-bound JAX-RS provider(s) should be bound to:
#Path("/")
public class MyResourceClass {
#GET
#Produces("text/plain")
#Path("{name}")
#Logged
public String hello(#PathParam("name") String name) {
return "Hello " + name;
}
}
A name-binding annotation may also be attached to a custom JAX-RS Application subclass. In such case a name-bound JAX-RS provider bound by the annotation will be applied to all resource and sub-resource methods in the JAX-RS application:
#Logged
#ApplicationPath("myApp")
public class MyApplication extends javax.ws.rs.core.Application {
...
}
Based on peeskillet's answer, which the concept is right but the code is slightly wrong, this is the final code for the answer.
Using #NameBinding, this works:
Authorize.java
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Authorize {
}
AuthorizeFilter.java
note: still needs to do the actual token authorization. This is just checking if the token exists right now.
#Provider
#Authorize
#Priority(Priorities.AUTHORIZATION)
public class AuthorizeFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
MultivaluedMap<String, String> headers = requestContext.getHeaders();
String token = headers.getFirst("token");
if (token == null || token.isEmpty()) {
Response.ResponseBuilder responseBuilder = Response
.status(Response.Status.UNAUTHORIZED)
.type(MediaType.APPLICATION_JSON)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600");
requestContext.abortWith(responseBuilder.build());
}
}
}
ApplicationConfig.java
note: add the filter here so it doesn't have to be included in the web.xml
#ApplicationScoped
#ApplicationPath("/api")
public class ApplicationConfig extends Application
{
#Override
public Set<Class<?>> getClasses()
{
return getRestResourceClasses();
}
private Set<Class<?>> getRestResourceClasses()
{
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
resources.add(com.example.AuthorizeFilter.class);
resources.add(com.example.UserService.class);
return resources;
}
}