suppose I have the following simple rest defined:
#RequestMapping("/user/data")
#ResponseBody
public String getUserDetails(#RequestParam int id) {
...
}
Is there a way to read the path string problematically by another part of the code (i.e. from a different method altogether) ?
Something like:
String restPath = SomeReflection.getPath("getuserdetails"); // received value: "/user/data"
WDYT?
thanks!
Solved!
here's the implementation I needed:
public String getUrlFromRestMethod(Class controllerClass, String methodName) {
try {
Method method = controllerClass.getMethod(methodName);
if (method != null && method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMappingAnnotation = method.getAnnotation(RequestMapping.class);
return requestMappingAnnotation.toString();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();//TODO
}
return null;
}
If you mean that you wanna access that value programmatically even from another class, then maybe you can start working out your solution from this example:
//get all methods defined in ClassA
Method[] methods = ClassA.class.getMethods();
for (Method m : methods) {
//pick only the ones annotated with "#RequestMapping"
if (m.isAnnotationPresent(RequestMapping.class)) {
RequestMapping ta = m.getAnnotation(RequestMapping.class);
System.out.println(ta.value()[0].toString());
}
}
I would suggest you add a HttpServletRequest request in your method, and then from there go request.getServletPath()
ei
public String getUserDetails(HttpServletRequest request, #RequestParam int id) {
Or if this is done in Spring http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-requestmapping
#RequestMapping(path = "/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(#PathVariable #DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
#RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(#PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
maybe u can call its value.
Related
Is it possible to cast List Objects to List of Objects from Factory pattern?
I have a Jersey REST endpoint and I migrate data from one environment to another. I wish to post some list of Object and cast them to right object taken from factory pattern
#Path("/migrateTableAtOnce")
#Consumes(MediaType.APPLICATION_JSON)
public <T> Response saveObjectIntoDb(List<T> listOfObj) {
// if listOfObj.getTableName() == "MW_ID_GENERATOR" tableOject gets new MwIdGenerator()
myEntity tableObject = myEntityFactory.getTable(listOfObj.getTableName());
return Response.status(201).entity("ok").build();
}
Is it possible that I have only one post method which that generic List. I have 20 objects which I need to transfer and I dont want to write 20 post methods :( I dont know how to do it exactly.
One of my method looks like that:
#POST
#Path("/migrateTableAtOnceMwIdGenerator")
#Consumes(MediaType.APPLICATION_JSON)
public Response saveObjectIntoDb(List<MwIdGenerator> listOfObj) {
Boolean result = false;
String dbResponse ="";
try {
dbResponse = obtainFacade().saveToDb(listOfObj);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return Response.status(201).entity(result+" "+dbResponse).build();
}
Is there no better solution to solve this problem?
I post the body as a custom object, and have a list within that object like;
#POST
#Path("/migrateTableAtOnceMwIdGenerator")
#Consumes(MediaType.APPLICATION_JSON)
public Response saveObjectIntoDb(CustomObject object) {
List <Stuff>list = object.getList();
and the object
public class CustomObject extends Serializable {
public List <Stuff>sList = null;
public List <OtherStuff>osList = null;
public List <TheBestStuff>tbsList = null; //etcetc
public List getList ()
{
return list;
}
public void setList(List <Stuff>list)
{
this.list = list;
}
public List getOsList ()
{
return osList ;
}
public void setList(List <OtherStuff>osList)
{
this.osList = osList;
}
public List getTbsList ()
{
return tbsList;
}
public void setTbsList(List <TheBestStuff>tbsList)
{
this.tbsList = tbsList;
}
and Jersey can parse the json object into your custom object, provided that you can pass the fields of that class as json. Seeing as you're passing the data with json, you're limited as to your implimentation as json only covers String, int, boolean and simple date, but you can pass most values as String and then parse.
I have a user table in my database with 2 fields id and name. I want to create an API to delete a user on the bases of id or name. Like this:
#DeleteMapping(path = "/{name}")
public ResponseEntity<Object> clearUser(#PathVariable("name") String name){
myService.deleteUser(name);
return new ResponseEntity<>(HttpStatus.OK);
}
#DeleteMapping(path = "/{id}")
public ResponseEntity<Object> clearUser(#PathVariable("id") int id){
myService.deleteUser(id);
return new ResponseEntity<>(HttpStatus.OK);
}
But I want to do it under one #DeleteMapping and I have to do it using path param only not query param. A person can enter id or name to delete that user.
You might use your two path variables as optional.
#DeleteMapping(path = {"/{name}", "/{id}")
public ResponseEntity<Object> clearUser(#PathVariable("name") Optional<String> name, #PathVariable("id") Optional<Integer> id){
myService.deleteByIdOrName(id, name);
return new ResponseEntity<>(HttpStatus.OK);
}
MyRepo ...
void deleteByIdOrName(Optional<Integer> id, Optional<String> name);
MyService
void deleteByIdOrName(Optional<Integer> id, Optional<String> name) {
repo.deleteByIdOrName(id, name);
}
Just check the path parameter type whether it is numeric or a String. Based on that, you can change the implementation under one method. just a hint:
#DeleteMapping(path = "/{user}")
public ResponseEntity clearUser(#PathVariable("user") String user){
myService.deleteUser(user);
return new ResponseEntity<>(HttpStatus.OK);
}
and inside the implementation, do the following:
use Java's built-in java.text.NumberFormat object to see if, after
parsing the string the parser position is at the end of the string. If
it is, we can assume the entire string is numeric:
public static boolean isNumeric(String str) {
NumberFormat formatter =
NumberFormat.getInstance();
ParsePosition pos = new
ParsePosition(0);
formatter.parse(str, pos);
return str.length() == pos.getIndex();
}
....
if(isNumeric(user)){
// implement delete by ID
}else{
//implement delete by Name
}
....
Hope this will give you an idea..
Hi I have Rest API and used swagger for the test this API.
below is one of my API.
#RequestMapping(value = "/api/test", method = RequestMethod.POST)
public void test(String string){
// body
}
The Possible value for the arguments are "Database" or "Cache".
So i want drop down in swagger view.
I have gone through the google search , i can not found how to implement with java.
You have to use Enum as your method argument instead of String. See below reference:
#RequestMapping(value = "/api/test", method = RequestMethod.POST)
public void test(TestEnum enum) {
// body
}
And below is your TestEnum:
public enum TestEnum {
Dropdown1("DropDown1"),
DropDown2("DropDown2");
private String str;
TestEnum(String str){
this.str = str;
}
public String getStr() {
return str;
}
}
You can annotate your parameter with the possible values
#RequestMapping(value = "/api/test", method = RequestMethod.POST)
public void test(#ApiParam(allowableValues = "one, two, three") String string) {
// body
}
I'm a new member in Restful API, I'm writing a GET method:
#RequestMapping(method = RequestMethod.GET, value = "/resourcerecords", produces={"application/json", "application/xml"})
public #ResponseBody Object getRRs(#RequestBody RRRequest requestBody){
// do something
}
The RRRequest class:
public class RRRequest{
private RRREC reqObject;
// getter and setter
}
The RRREC class:
public class RRREC{
protected String infraAddr;
protected RRINFRATYPE infraType;
// getter and setter
}
And the RRINFRATYPE class:
public enum RRINFRATYPE {
V_6_ADDRESS("V6ADDRESS"),
OBJECT("OBJECT"),
ZONE("ZONE"),
V_4_REVERSEZONE("V4REVERSEZONE"),
V_6_REVERSEZONE("V6REVERSEZONE"),
NODE("NODE"),
ALL("ALL");
private final String value;
RRINFRATYPE(String v) {
value = v;
}
public String value() {
return value;
}
public static RRINFRATYPE fromValue(String v) {
for (RRINFRATYPE c: RRINFRATYPE.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
Then, I sent a request GET with RequestBody ( I use Fiddler Web Debugger)
"reqObject" : {
"infraAddr" : "192.168.88.4",
"infraType": {
"value": "OBJECT"
}
}
I get 400 Bad Request. If I change to
"reqObject" : {
"infraAddr" : "192.168.88.4",
"InfraType": {
"value": "OBJECT"
}
}
I can debug.
However, The reqObject only receive infraAddr with "192.168.88.4", the InfraType is null.
Who can explain to me, why I must be use "InfraType" instead of "infraType" and how to send value for InfraType.
The first one is when your api in GET method you still cant send body of request to server, try to change it to POST.
Because you use ENUM in your Object so you should define a converter like Converting JSON to Enum type with #RequestBody
But in this case, I think the fastest way is change infraType to String and use switch case with String on server side.
public class RRREC{
protected String infraAddr;
protected String infraType;
// getter and setter
}
Your JSON will be:
{
"reqObject" : {
"infraAddr" : "192.168.88.4",
"infraType": "OBJECT"
}
}
Is it possible to dynamically call a method on a class from java?
E.g, lets say I have the reference to a class, e.g either the string: 'com.foo.Bar', or com.foo.Bar.class, or anything else which is needed..). And I have an array / list of strings, e.g [First, Last, Email].
I want to simply loop through this array, and call the method 'validate' + element on the class that I have a reference to. E.g:
MyInterface item = //instantiate the com.foo.Bar class here somehow, I'm not sure how.
item.validateFirst();
item.validateLast();
item.validateEmail();
I want the above lines of code to happen dynamically, so I can change the reference to a different class, and the names in my string list can change, but it will still call the validate + name method on whichever class it has the reference to.
Is that possible?
The simplest approach would be to use reflection
Given...
package com.foo;
public class Bar {
public void validateFirst() {
System.out.println("validateFirst");
}
public void validateLast() {
System.out.println("validateLast");
}
public void validateEmail() {
System.out.println("validateEmail");
}
}
You could use something like...
String methodNames[] = new String[]{"First", "Last", "Email"};
String className = "com.foo.Bar";
try {
Class classRef = Class.forName(className);
Object instance = classRef.newInstance();
for (String methodName : methodNames) {
try {
Method method = classRef.getDeclaredMethod("validate" + methodName);
method.invoke(instance);
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
ex.printStackTrace();
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
ex.printStackTrace();
}
To look up the methods and execute them.
You will need to decide the best way to handle errors and what they mean to you, but it wouldn't be a difficult them to expand the idea to a reusable method...
Updated with idea of concept discussed in comments
Given....
public interface Validator {
public boolean isValid(Properties formProperties);
}
We can create one or more...
public class UserRegistrationValidator implements Validator {
public boolean isValid(Properties formProperties) {
boolean isValid = false;
// Required fields...
if (formProperties.containsKey("firstName") && formProperties.containsKey("lastName") && formProperties.containsKey("email")) {
// Further processing, valid each required field...
}
if (isValid) {
// Process optional parameters
}
return isValid;
}
}
Then from our input controller, we can look and valid the required forms
public class FormController ... {
private Map<String, Validator> validators;
public void validForm(String formName, Properties formProperties) {
boolean isValid = false;
Validator validator = validators.get(formName);
if (validator != null) {
isValid = validate.isValid(formProperties);
}
return isValid;
}
}
Of course you need to provide some way to register the Validators and there may be differences based on the backbone framework you are using and the parameters you can use (you don't have to use Properties, but it is basically just a Map<String, String>...)
You can write something like this... it takes name of a class as string as an argument, the method name and its arguments
private static String invoke(String aClass, String aMethod, Class<?>[] params,
Object[] args) throws Exception {
String resp = "";
Class<?> c = Class.forName(aClass);
Method m = c.getDeclaredMethod(aMethod, params);
Object i = c.newInstance();
resp = m.invoke(i, args).toString();
return resp;
}
You can also refer to the oracle tutorial on reflection ... which demonstrates how to call methods
http://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html
It's possible using reflection.
First, you create a new class from the FQN (fully qualified name, which is the class name including the package).
Then you iterate through your elements and invoke the "validate" methods on your item.
Class<?> clazz = Class.forName("com.foo.Bar");
Object item = clazz.newInstance();
for (String element : elements) {
Method method = clazz.getDeclaredMethod("validate" + element);
method.invoke(item);
}
You can use reflection, but my favorite method is to use beanutils, eg:
Bar b1 = //...
BeanUtils.getProperty(b1, "first");
BeanUtils.getProperty(b1, "last");
Note that your class has to conform to javabean convention. You can read more about beanutils on this blog post (disclaimer I'm the blog author)
If you know the name of the class beforehand, use Class.forName(yourClassname)
That way, you can invoke the class, and then, you can invoke its methods.
Yes, using reflection.
Using Class.getDeclaredMethod on your object
Object validator = <your object instance>;
final String[] values = {
"Item1","Item2","Item3"
}
for(final String s : values) {
Method m = validator.getDeclaredMethod("validate" + s,String.class);
try {
Object result = m.invoke(validator, s);
}
catch(ex) {}
}