Spring 3 MVC Web services: many parameters with same name - web-services

I am running into the next problem. I have declared a method in the controller like the next one, to be used as a web service:
#RequestMapping(value = "/" + "prueba" , method = RequestMethod.GET)
public void prueba(ExampleBean pExample1, ExamlpleBean pExample2) {
// Wonderful code here
}
And the class ExampleBean is just, well, a Bean:
public class ExampleBean implements Serializable {
private String id;
private String whatever;
// getters, setters, and more.
}
If the interface were something like that:
#RequestMapping(value = "/" + "prueba" , method = RequestMethod.GET)
public void prueba(ExampleBean pExample1) {
// Wonderful code here
}
Each time I would like to call that web service, I would call the URL in the next way:
http://myWebProject/prueba?id=1&whatever=hola
But... How can I do when I have to give values to both params from the same class? I mean, I can not repeat parameters, so I dont know how to differ between the id from pExample1, and the id from pExample2 when writing the URL.
I mean, also with two parameters from different classes, but with an attribute with the same name. For example, if the second parameter is from the class DifferentExampleBean, which has also an "id" parameter.
Thanks a lot!
PS: I am using StringHttpMessageConverter.

What you would do is to create a parent class which would hold particular field you're interested in then both ExampleBean and ExampleBean1 would extend this parent class and you'd have only one type to be sent in prueba(ParentClass instance1, ParentClass instance2).
Where instance1 would be instance of ExampleBean and instance2 would be instance of ExampleBean2

Related

Extending SimpleNeo4jRepository in SDN 6

In SDN+OGM I used the following method to extend the base repository with additional functionality, specifically I want a way to find or create entities of different types (labels):
#NoRepositoryBean
public class MyBaseRepository<T> extends SimpleNeo4jRepository<T, String> {
private final Class<T> domainClass;
private final Session session;
public SpacBaseRepository(Class<T> domainClass, Session session) {
super(domainClass, session);
this.domainClass = domainClass;
this.session = session;
}
#Transactional
public T findOrCreateByName(String name) {
HashMap<String, String> params = new HashMap<>();
params.put("name", name);
params.put("uuid", UUID.randomUUID().toString());
// we do not use queryForObject in case of broken data with non-unique names
return this.session.query(
domainClass,
String.format("MERGE (x:%s {name:$name}) " +
"ON CREATE SET x.creationDate = timestamp(), x.uuid = $uuid " +
"RETURN x", domainClass.getSimpleName()),
params
).iterator().next();
}
}
This makes it so that I can simply add findOrCreateByName to any of my repository interfaces without the need to duplicate a query annotation.
I know that SDN 6 supports the automatic creation of a UUID very nicely through #GeneratedValue(UUIDStringGenerator.class) but I also want to add the creation date in a generic way. The method above allows to do that in OGM but in SDN the API changed and I am a bit lost.
Well, sometimes it helps to write down things. I figured out that the API did not change that much. Basically the Session is replaced with Neo4jOperations and the Class is replaced with Neo4jEntityInformation.
But even more important is that SDN 6 has #CreatedDate which makes my entire custom code redundant.

Mapping #Json property with JDBI

According to JDBI document https://jdbi.org/#_jackson_2, it seems that it's quite straight forward to have a json property of your object model, however I've tried the following and it ran into many issues.
DB: Postgres with a column type of Jsonb
class Event {
private String name;
#Json
private EventProperty jsonProperty;
...
}
Datasource has been configured with
#Bean
public Jdbi jdbi(TransactionAwareDataSourceProxy eventStoreTxAwareDataSourceProxy) {
Jdbi jdbi = Jdbi.create(eventStoreTxAwareDataSourceProxy);
jdbi.installPlugin(new PostgresPlugin());
jdbi.installPlugin(new Jackson2Plugin());
}
SQL for binding list of insertion
INSERT INTO event (name, json_property)
VALUES (
:name,
:jsonProperty)
When running the code to insert, the following error occurred:
org.jdbi.v3.core.statement.UnableToCreateStatementException: no argument factory for type com.EventProperty [statement:"INSERT INTO event (...]
If I created EventPropertyArgumentFactory and using Jackson ObjectMapper and writeValueAsString then I can save it to DB. However, when retrieving it back from DB by
try (Handle handle = jdbi.open()) {
EventDao dao = handle.attach(EventDao.class);
return dao.findByName(name);
}
throws the following errors
java.lang.ClassCastException: Cannot cast org.postgresql.util.PGobject to com.EventProperty
I thought all I needed to do is declare the field annotated with #Json, the DB column has to be json/jsonb type and install the plugins, but seems like this is not the case?
Anyone has tried this successfully, without having to define custom row mapper and argument factory implementation?
Thanks
The documentation says:
// use #Json qualifier:
...
// also works on bean or property-mapped objects:
class MyBean {
private final MyJson property;
#Json
public MyJson getProperty() { return ...; }
}
I've checked and it's unfortunate but #Json only works when placed on a property( i.e. getter or setter) and not on a field.
You can make your work easier if you use Lombok library.
Modify lombok.config file by adding this line:
lombok.copyableannotations += org.jdbi.v3.json.Json
Now in bean declaration you can do this:
#Data // will generate setters and getters among other things
class Event {
private String name;
#Json // will be copied onto getter and setter due to config change we made
private EventProperty jsonProperty;
...
}
Have fun!
Not sure if you've figured this out by now but I just ran into this same issue and finally figured it out.
Basically, you just have to add the annotation on the getter or setter of the class, not the top-level field.
class Event {
private String name;
private EventProperty jsonProperty;
...
#Json
public getEventProperty() {
return jsonProperty;
}
}

spring boot - why is #valid not working on controller for type?

I have Spring Boot application with a rest endpoint in a #RestController annotated class that is something like this:
#Postmapping(path = "<url>")
private #ResponseBody ResponseEntity<?> methodName(
otherParameters otherParameters,
#Valid #RequestBody Entity entity,
Errors errors) {
if(errors.hasErrors()) {
// log something
// do something
}
// rest of the controller
}
The Entity class is something like this:
public class Entity {
#Pattern(regexp = "[^<>&]+")
private String someString;
// getters and setters
}
But when I try to reach the controller with someString as some&tring, the #Valid annotation does not seem to work.
This is accessible only from a rest call. There are no forms that use this action.
There is no spring-security implementation in this application at the moment.
What am I missing here?
Be sure to add #Valid on the member fields of your pojos if those fields represent pojo's themselves, otherwise the validation does not propagate.
I think you are missing the object add to the page.
check the following code.
check 1 :
// on controller side
#GetMapping("/registration")
public String registration(Model model) {
model.addAttribute("entity", new Entity());
return "registration";
}
#PostMapping("/registration")
public String registration(#ModelAttribute("entity") Entity entity, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "registration"; // stay on that page here
}
return "login"; // after no error go here
}
use this entity in form on the page where you want to access the validation
<form action="#" th:action="#{/registration}" th:object="${entity}" method="post" class="m-t">
check 2 :
both the get and post method must be the same name for the object created like **entity**

RestEasy : GET request with multiple dynamic arguments

The required URL should be something like this :
http://<host>:<port>/path/item?<arguments>
The arguments key and value supposed to be multiple and dynamic, so I cannot use #BeanParam or #QueryParam. Also I can only call this interface without implementation.
My current code is something like this :
public interface RestService {
#GET
#Path("/path/item")
#Produces(MediaType.APPLICATION_JSON)
public JsonNode method(#QueryParam("params") String params);
}
Example of arguments that I want to pass : brand=myBrand&price=myPrice
Is there any way to do something like this ?
My References :
REST Web Service - Dynamic Query Parameters
Passing indefinite Query Parameters with RESTful URL and reading them in RESTEasy
Use UriInfo.getQueryParameters(), as following:
#GET
#Path("/path/item")
#Produces(MediaType.APPLICATION_JSON)
public JsonNode method(#Context UriInfo uriInfo) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
...
}
It returns a MultivaluedMap. Then just iterate over it.

using a Singleton to pass credentials in a multi-tenant application a code smell?

I'm currently working on a multi-tenant application that employs Shared DB/Shared Schema approach. IOW, we enforce tenant data segregation by defining a TenantID column on all tables. By convention, all SQL reads/writes must include a Where TenantID = '?' clause. Not an ideal solution, but hindsight is 20/20.
Anyway, since virtually every page/workflow in our app must display tenant specific data, I made the (poor) decision at the project's outset to employ a Singleton to encapsulate the current user credentials (i.e. TenantID and UserID). My thinking at the time was that I didn't want to add a TenantID parameter to each and every method signature in my Data layer.
Here's what the basic pseudo-code looks like:
public class UserIdentity
{
public UserIdentity(int tenantID, int userID)
{
TenantID = tenantID;
UserID = userID;
}
public int TenantID { get; private set; }
public int UserID { get; private set; }
}
public class AuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
new EventHandler(context_AuthenticateRequest);
}
private void context_AuthenticateRequest(object sender, EventArgs e)
{
var userIdentity = _authenticationService.AuthenticateUser(sender);
if (userIdentity == null)
{
//authentication failed, so redirect to login page, etc
}
else
{
//put the userIdentity into the HttpContext object so that
//its only valid for the lifetime of a single request
HttpContext.Current.Items["UserIdentity"] = userIdentity;
}
}
}
public static class CurrentUser
{
public static UserIdentity Instance
{
get { return HttpContext.Current.Items["UserIdentity"]; }
}
}
public class WidgetRepository: IWidgetRepository{
public IEnumerable<Widget> ListWidgets(){
var tenantId = CurrentUser.Instance.TenantID;
//call sproc with tenantId parameter
}
}
As you can see, there are several code smells here. This is a singleton, so it's already not unit test friendly. On top of that you have a very tight-coupling between CurrentUser and the HttpContext object. By extension, this also means that I have a reference to System.Web in my Data layer (shudder).
I want to pay down some technical debt this sprint by getting rid of this singleton for the reasons mentioned above. I have a few thoughts on what a better implementation might be, but if anyone has any guidance or lessons learned they could share, I would be much obliged.
CurrentUser isn't quite a singleton. I'm not exactly sure what you'd call it. (A singleton by definition can only exist one at a time, and any number of UserIdentity instances can be created at will by outside code and coexist without any issues.)
Personally, i'd take CurrentUser.Instance and either move it to UserIdentity.CurrentUser, or put it together with whatever similar "get the global instance" methods and properties you have. Gets rid of the CurrentUser class, at least. While you're at it, make the property settable at the same place -- it's already settable, just in an way that (1) would look like magic if the two classes weren't shown right next to each other, and (2) makes changing how the current user identity is set later harder.
Doesn't get rid of the global, but you're not really gonna get around that without passing the UserIdentity to every function that needs it.