I've a nicely working Spring argument mapping for my arguments, unfortunately I've one component which isn't passed through the DispatcherServlet and I struggle to find a way to get the mapping to run somehow manually.
An example would look like this:
public class Bar {
public Bar(#RequestParam("map") String map) {
System.out.println("Found: " + map); // Expected result "Found: value
}
}
public class Foo {
public static void main(String[] args) {
context = new ClassPathXmlApplicationContext("META-INF/context.xml");
String pseudoUrl = "/?map=value"
Bar bar = context.getBeans('bar', pseudoUrl);
}
}
It that something which could work somehow? As you can imaging the passed around data has a more complex structure and therefore I'd like to avoid that I've to write an parser on my own.
Cheers.
You could basically follow the same algorithm that DispatcherServlet.doDispatch() does:
for each HandlerMapping in the context, (line 1058 of DispatcherServlet in spring-webmvc.jar 3.1.2-RELEASE), test if HandlerMapping.getHandler(request) returns non-null
for each HandlerAdapter in the context, (line 1091) test if HandlerAdapter.supports(handlerMapping) returns true
execute HandlerAdapter.handle(request, ...) and handle the output (line 923)
This would require you to bundle up your pseudoUrl into a pseudo-HttpServletRequest, etc.
On the other hand, if you know that you always want to get a specific bean from the context in your main() method, I would just ask Spring for that bean and invoke the method explicitly, rather than dealing with mapping beans to a URL (which I assume does not change).
Related
I have an API - http://localhost:8080/api/version/<versionA> where versionA is a Path Parameter.
HttpServletRequest getRequestURI() returns -> /api/version/<versionA>
How do we ensure that only /api/version is returned back? Is there any way to generalize this for all endpoints and return only the first part of the URI excluding path params?
For example -
/api/version/<param1> Should Return /api/version
/api/<param1> Should return /api
/api/version/<param1>/name/<param2> Should return /api/version
Path Parameter is not a Query Parameter; it is rather a part/segment of the URL Path. So, there is no (and there should not be) built-in Servlet API method, that will return your, "custom-sub-stringed" path.
Q: Why?
A: Because you may have more than one path parameters /a/b/<param1>/<param2>/c/<param3>
Is there any way to generalize this for all endpoints and return only the first part of the URI excluding path params?
In your case, one option would be to implement some utility method, which will find the the lastIndexOf "/" and then will return the substring to that last "/".
It depends a bit on how you've constructed things but let's take an example.
http://localhost:8080/sample/rest/v1/classLevel/methodLevel/param1/param2/something/param3
and a service defined as:
#Path("/v1/classLevel")
public class NewService {
#Path("/methodLevel/{param1}/{param2}/something/{param3}")
public Response getSomeStuff(#PathParam("param1") String param1,
#PathParam("param2") String param2,
#PathParam("param3") String param3,
#Context HttpServletRequest request) {
return Response.ok().build();
}
}
This webapp is deployed under the context root "sample". That means that if I were to go to http://localhost:8080/sample/ I'd get the root element (perhaps index.html) for the webapp. In my Application I have:
#ApplicationPath("/rest")
public class RestApplicationConfig extends Application {
// intentionally empty
}
So the parameters in the URL are:
request.getServletPath() returns "/rest"
request.getContextPath() returns "/sample"
request.getPathInfo() returns "/v1/classLevel/methodLevel/param1/param2/something/param3"
So I believe that what you want is request.getContextPath() + request.getServletPath(). But it's a bit unclear which part you really need.
EDIT
To find out what path is at the class level a little reflection is needed. Within a class that is being called (i.e. non-static methods of the NewService class above) you would be able to get that with:
public String getClassLevelPath() {
if( this.getClass().isAnnotationPresent(Path.class)) {
Path annotation = this.getClass().getAnnotation(Path.class);
return annotation.value();
}
return null;
}
As my class is defined, this returns "/v1/classLevel". I would personally cache this in something like a static variable as it's not going to change during runtime unless you're doing something else to change it.
I am having a bit of trouble getting a return value from a bean in camel and using it in my route.
I have a route that looks like this:
from(file:test/?delete=true)
.unmarshal(jaxb)
.bean(testBean, "testMethod")
.to(direct:nextRoute);
The bean looks something like this:
public void testBean (PojoName pojoInstance){
//do stuff
int i= 75; //a number I generate within the bean after I've started this method
}
I want to use the number I generate inside of my bean and in my route. Something like this:
from(file:test/?delete=true)
.unmarshal(jaxb)
.bean(testBean, "testMethod")
.log(integer generated from testBean)
.to(direct:nextRoute);
What I tried:
So, instead of returning void in my bean, I changed the return type to an int and returned the integer. Then, I was hoping to do something like this in my route:
.log("${body.intFromBean}")
My thinking is that once I return the value from a bean, it should store that value in the exchange body (at least that's what I'm getting from the Camel documentation). Then, I could access it in my route.
The Problem:
However, when I change the testBean return type to an int, I get the following errors:
org.apache.camel.CamelExecutionException: Execution occurred during execution on the exchange
Caused by: org.apache.camel.InvalidPayloadException: No body available of type: PojoName but has value: numberIGenerated of type java.lang.Integer
(Sorry I don't have the full stack trace. I'm using the s.o mobile app)
My question:
From reading some other s.o. submissions, I think I understand the problem. The message body is of one type and the return type is another. However, even when I tried using .
.convertTo(Integer.class)
before calling the bean, but that didn't work either. (Conceptually, that wouldn't work either because if I converted it to an int right after I unmarshalled it, I wouldn't be able to use the unmarshalled data. But I thought I'd try it anyway).
Can someone help me understand how I can properly return the integer and use it in my route?
I've read the documentation on bean binding and the exchange, and I thought I understood it well enough to do this. But I must be missing something.
I think a simpler solution would be:
public class TestBean {
public int testMethod() {
return 75;
}
}
Whether you want the return result to be stored in a header or in the body should be up to the route definition.
As you read in the Camel documentation, the default behavior is to set the return value in the body:
TestBean testBean = new TestBean();
from("file:test/?delete=true")
.unmarshal(jaxb)
.bean(testBean, "testMethod")
.log("${body}")
.to("direct:nextRoute");
And if you want it in a header:
TestBean testBean = new TestBean();
from("file:test/?delete=true")
.unmarshal(jaxb)
.setHeader("MyMagicNumber", method(testBean, "testMethod"))
.log("${header.MyMagicNumber}")
.to("direct:nextRoute");
Be careful that if you use a Camel version older than 2.10, you will need to use the (now deprecated) "bean" method instead of the "method" method :-)
Depending on what you need to use it for you can either add it to the header or you can make it the body.
To add it to the header(key/value) do the following:
public class TestBean
{
#Handler
public void testMethod
(
#Body Message inMessage,
#Headers Map hdr,
Exchange exch
) throws Exception
{
int i= 75;
hdr.put("MyMagicNumber", i);
}
}
Your "return" result is now stored in the header and you can read it from there in the steps that follow.
For the body do the following:
public class TestBean
{
#Handler
public void testMethod
(
#Body Message inMessage,
#Headers Map hdr,
Exchange exch
) throws Exception
{
int i= 75;
inMessage.setBody(i);
}
}
The body of the message will now contain i.
I am validating the parameters passed to a series of commands in a file using the following code:
for (Parameter p : s.getCommand(idx).getParameters()) {
for (ValidationFactory.TYPES validationType : ValidationFactory.TYPES.values()) {
validator = ValidationFactory.getValidator(validationType, errors);
try {
validator.validate(p);
} catch (ValidationException e) {
Report.logErrorMessage("Failed to validate: " + validationType);
continue;
}
}
}
Then in the ValidationFactory I have:
public final class ValidationFactory {
public enum TYPES {
PROPERTIES,
PORTS
};
private ValidationFactory() {
}
public static AbstractValidator getValidator(TYPES validationType,
ValidationErrors errors) {
switch (validationType) {
case PROPERTIES:
return new PropertiesValidator(errors);
case PORTS:
return new PortRangeValidator(errors);
default:
return null;
}
}}
This code works really nicely and allows for new validators to be added at a later date. There is one relatively minor problem though...
The outer for loop iterates over a list of parameters that will be passed to the command, while the inner for loop iterates over a list of validators which can do the validation. Depending on the parameter however, it may not be necessary to continue the validation with the second validator, the first one may have already done the work... So, PropertiesValidator might have done the work needed, and now there is no need to call the second validator, but it is going to call it anyway. I guess I could use a variable to maintain validation state, and then it could skip if already done.. both validators extend an AbstractValidator class which would be the best place for this.
I would like to do the validation in one pass while keeping the structure of the Factory pattern. I was thinking of putting in some sort of delegator class.. I am using java 1.6 so I can't switch on string arguments which would be nice.
Define a Generic Validator, which is going to be common to all the validator, and define specific validation in properties and port validation. So now there is no duplication of validation by moving common logic into generic validator and specific validation in others.
Maybe title "can annotation get context object?" is not correct, but I don't know how to give it a right and clear one.
I use Spring AOP + Java Annotation to save log, here is my code:
CategoryAction.java :
#ServiceTracker(methodDesp="save category, category name:"+this.category.getName())
public String save() throws Exception
{
this.categoryService.save(this.category);
this.setJsonDataSimply(null);
return "save";
}
TrackAdvice.java :
public Object trackAround(ProceedingJoinPoint point) throws Throwable
{
String log = "success";
ServiceTracker tracker = null;
Method method = null;
AbstractAction action = null;
try
{
Object result = point.proceed();
action = (AbstractAction) point.getTarget();
MethodSignature signature = (MethodSignature) point.getSignature();
method = signature.getMethod();
tracker = method.getAnnotation(ServiceTracker.class);
return result;
}
catch (Exception e)
{
log = e.getMessage();
throw e;
}
finally
{
if (tracker != null)
{
String userId = (String) ActionContext.getContext().getSession().get(Constant.USERID);
if (userId == null)
{
userId = "unknown";
}
TrackLog t = new TrackLog();
t.setWhen(new Date());
t.setUserId(userId);
t.setResult(log);
t.setMethodName(action.getClass().getCanonicalName() + "." + method.getName());
t.setMethodDesp(tracker.methodDesp());
this.trackService.save(t);
}
}
}
ServiceTracker is my own annotation, in my TrackAdvice class, I get the current executing method, if the method has a ServiceTracker annotation, then save the methodDesp in annotation to database.
Now the question is the methodDesp in annotation is dynamic, I want to get this object and retrieve its category property.
It seems that Java Annotation doesn't support this, maybe it supports but I don't know how.
What you can do is use some sort of expression language in the annotation value and then run some interpreter in your advice code. One example using SPEL could look like this:
#ServiceTracker(methodDesp="save category, category name: #{category.name}")
And in your advice code, you can then extract the expression token, make use of a SpelExpression and pass it the target reference as root object (you may want to check what's available out of the box in the SPEL API for supporting your use-case(s)).
It seems that Java Annotation doesn't support this
You are correct - there is no way to do this in pure java.
The reason is that because annotations are static metadata that is wired into classes and defined at compile-time (this start to exist only at run-time, not compile-time).
In other words there is no straightforward way to make methodDesp of some annotated method of some class dynamic, since it's value has to be resolved statically, at compile-time.
However, technically there is a way to do something like you want. What I talk about is using javassist to either manipulate or create your classes (and annotations applied to them) at runtime. But be warned that this is rather hacky way and I generally would not recommend to go there.
I write a little web API which should it make easy to create URIs. Each resource class should contain a method createURI which takes the needed parameters. This method should use a helper method, populateUriTemplate, in the background to create an URI string. populateUriTemplate needs key value pairs to populate an URI template. In another language like Scala or Python I would use named parameters, but Java doesn't support them. So the question is: How to simulate named parameters in Java?
The straight forward solution would be to create a map:
public String createUri(int id, String name){
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", id);
params.put("name", name);
return populateUriTemplate(params);
}
But I don't like to create a map first and put each parameter to it.
Another idea is to use a static method, param, to create key value pairs:
public String createUri(int id, String name){
return populateUriTemplate(param("id", id), param("name", name));
}
Looks much better to me!
It could be refined a bit to make it more self-explanatory, even if a few more characters are needed:
public String createUri(int id, String name){
return populateUriTemplate(key("id").value(id), key("name").value(name));
}
I've also thought of the builder pattern, but this would force the user of my API to create an explicit builder for each resource class, what would be tedious without a benefit. The type of the parameter is not important, as long as a proper implemented toString method exists.
My favourite is one of the both approaches with the static methods above (param(key, value) or key(k).value(v)). Do you know a better way to simulate named parameters in this case?
For some ideas on the builder pattern, you could see this blog post by Stephan Schmidt.
You also just gave me the idea to do the following, with fluent interfaces, a Callable, and a static method:
createUri().id(5).name("dennetik").call();
Which would require createing a Callable class (CreateUri) with the static method:
public static final CreateUriFluentInterface createUri() {
return FluentInterface.of(new CreateUri(), CreateUriFluentInterface.class);
}
And a fluent interface, like this:
public interface CreateUriFluentInterface {
public CreateUriFluentInterface id(Integer id);
public CreateUriFluentInterface name(String name);
}
Which isn't that much boilerplate code, is it?
(Well, if you tone down that horribly named CreateUriFluentInterface a bit, it isn't.)
(You would probably have CreateUriFluentInterface extend Callable<String>, to be able to reroute the call to Callable#call())
populateUriTemplate("id",id, "name",name);
void populateUriTemplate(Object... nvs){
for(int i=0; i<nvs.length/2; i++)
....
}
Maybe you like this approach:
class Params {
private HashMap<String, Object> allParams = new HashMap<String,Object>();
public Params(ParamEntry...params) {
for( ParamEntry p : params ) {
allParams.put(p.name, p.value);
}
}
public getParam(String name) {
return allParams.get(name);
}
class ParamEntry {
public String name;
public Object value;
}
}
public String createUri(Params.ParamsEntry ... params){
return populateUriTemplate(new Params(params));
}
To call it use
createUri(new Param.ParamEntry("name", valueObject) );
Inside the populateUriTemplate...
just use params.get("name");
Spring MVC does exactly this. As well as being able to bind requests to specific methods in controller classes, you can bind request parameters to method parameters. You can have a look to see how it works, but basically it picks a strategy to map the right request parameter to the right method parameter.
You basically get something like:
public String createUri(#RequestParam int id, #RequestParam String name){
return populateUriTemplate(id, name);
}
This is almost silly and slightly off topic, but using Lombok's #Builder annotation takes this closer to the desired result.
Furthermore if the builder, builder method and build method names are changed to _ they almost disappear:
import static foo.Template._;
class Resource {
String createURI(String id, String name) {
return populateURITemplate(_.id(id).name(name)._());
}
String populateURITemplate(Template t ){
return t.id+"="+t.name;
}
}
#Builder(builderClassName = "_", builderMethodName = "_", buildMethodName = "_" )
class Template {
static _ _ = _();
String id;
String name;
}
Named parameters are not the way:
Named parameters do not make your code any cleaner in this case. I would argue that they make things more complex and error prone in Java because you lose type safety and you lose compiler warnings about identifiers that do not exist.
TypeSafe Immutable Fluent Builders:
I wrote an article on a UrlBuilder implementation earlier this year, it shows a type safe fluent interface that enforces order of construction for mandatory input and allows for optional parts with sane defaults as well.
Now I will be the first to admit that the approach I use is fairly verbose, but it is extremely productive once that initial price is paid. It works with dependency injection and is easily unit testable and most importantly is composable for specialization.
final URL url1 = new UrlBuilder().scheme("http").host("www.google.com").build();
System.out.println("url1 = " + url1);
final URL url2 = new UrlBuilder().scheme("https").userInfo("xkcd", "correcthorsebatterystaple").host("admin.xkcd.com").build();
System.out.println("url2 = " + url2);
Produces:
url1 = http://www.google.com
url2 = https://xkcd:correcthorsebatterystaple#admin.xkcd.com
I am addressing the verbosity of the anonymous inner class implementations of the interfaces with another approach I am experimenting with; type safe implementations of value objects from interfaces using dynamic proxies.
This will do away with the boilerplate value objects and replace them with Map<String,?> but put a dynamically generated type safe immutable Interface wrapper around them.
I encourage you to read about both of these and see how combining them gives you a better solution than named properties ever would.
When I get time to refactor my UrlBuilder with the dynamic proxies I will post another blog post about it as well.
Named Parameters via Guice
If you are dead set on named parameters then I would recommend looking at Guice #Named bindings. You still lose the compile type checks and safety but at least you get some validations from Guice.
public class RealBillingService implements BillingService {
#Inject
public RealBillingService(#Named("Checkout") CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}