java: cannot find symbol symbol: variable POST - java

I have below logic in my Java code
switch (method) {
case POST:
// Logic
break;
case PUT:
// Logic
break;
}
This logic is working when I'm using spring-web version as 5.3.22.
I'm getting below issue when I'm using spring-web version as 6.0.2.
Please suggest

The problem is that up to Spring version 5.3.x, HttpMethod used to be an enum (https://github.com/spring-projects/spring-framework/blob/5.3.x/spring-web/src/main/java/org/springframework/http/HttpMethod.java#L33):
public enum HttpMethod {
But they changed in in Spring Version 6.x to a "normal" class (https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/http/HttpMethod.java#L37):
public final class HttpMethod implements Comparable<HttpMethod>, Serializable {
The reason for this change is given as
Refactor HTTP Method from Enum to Class
This commit refactors HttpMethod from a Java enum into a class. The
underlying reason being that HTTP methods are not enumerable, but
instead an open range and not limited to the predefined values in the
specifications.
While you can use enum constants in a switch statement, you cannot use instances of normal classes.
Therefore if you want to use Spring version 6.x you must rewrite yourswitch(method) to an if () - else if () chain or use specific request mappings for the different methods.

Related

How to access request headers on JAXRS classes generated by Swagger Codegen

I have a project with an Swagger API and its server code was generated by swagger-codegen-2.4.24 for language jaxrs.
The code generated has an abstract class suffixed "*ApiService" that defines a series of methods, each corresponding to each operation defined on the Swagger specification of the API.
Each method has a javax.ws.rs.core.SecurityContext interface local variable.
Now, on my custom class which extends "*ApiService", that obviously has javax.ws.rs.core.SecurityContext class local variable, I need to fetch the value of request header "X-Forwarded-For".
If I debug my custom class I see that SecurityContext interface is an instance of org.glassfish.jersey.server.internal.process.SecurityContextInjectee, which has the header I need.
How do I get that information, since I'm not able to work with SecurityContextInjectee since it's private?
I realize that if classes generated by swagger-codegen added javax.servlet.http.HttpServletRequest class, besides SecurityContext, it would be possible to have access to the request parameters, but I didn't see any jaxrs parameter that allows that.
Looking forward for your comments.
In every specification version you can define a header like one of the possible parameter locations.
So, one possible solution, will be to define the header in the methods you required in the request parameters sections:
parameters:
-
name: X-Forwarded-For
description: X-Formarwed-For header.
schema:
type: string
in: header
Or, in JSON notation:
"parameters": [
{
"name": "X-Forwarded-For",
"description": "X-Formarwed-For header.",
"schema": {
"type": "string"
},
"in": "header"
}
]
I am aware that perhaps it is a less maintainable solution because you will need to include the header in every request, but maybe you could mitigate that fact with inheritance in your services implementation.
There is an open Github issue asking for the behavior you described, handling the header processing in a general way.
One suitable option, suggested as well in this related SO answer, could be modifying the Mustache templates used in the API code generation and include within them the required headers processing. Please, be aware that this will do your code less maintainable and you will have the risk of perform some change that breaks the compatibility with the official Swagger Codegen repository. I am not sure in Swagger Codegen, but in the OpenAPI generator there is an option to override the used templates without modifying the actual provided in the official distribution. Please, see this related SO question.
Although it seems that is no longer the case, at least in older versions of Jersey in which the class was public, you could try accessing the requestContext internal variable in org.glassfish.jersey.server.internal.process.SecurityContextInjectee by reflection as well, although I think that workaround makes your application very implementation dependent. In any case, perhaps you could define an utility method like this that you could reuse in your services implementation:
public static String getXForwardedForHeaderValue(final SecurityContext securityContext) {
SecurityContextInjectee securityContextImpl = (SecurityContextInjectee) securityContext;
Field requestContextField = SecurityContextInjectee.class.getDeclaredField("requestContext");
requestContextField.setAccessible(true);
ContainerRequestContext requestContext = requestContextField.get(securityContextImpl);
String xForwardedForHeaderValue = requestContext.getHeaderString("X-Forwarded-For");
return xForwardedForHeaderValue;
}
Finally, another possibility could be using a filter that process your header. If required you could pass the header value using for instance a thread local variable to the underlying services. The idea would be something like the following.
First, define a convenient object that wraps your ThreadLocal value:
public class XForwardedForHeaderHolder{
private static final ThreadLocal<String> value = new ThreadLocal<String>();
public static void setXForwardedForHeader(String xForwardedFor) {
value.set(xForwardedFor);
}
public static String getXForwardedForHeader() {
return value.get();
}
public static void clean() {
value.remove();
}
}
Next, create a ContainerRequestFilter. This filter will read header from the information received in the HTTP request being processed:
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Provider;
#Provider
public class XForwardedForHeaderRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
String xForwardedForHeaderValue = requestContext.getHeaderString("X-Forwarded-For");
XForwardedForHeaderHolder.setXForwardedForHeader(
xForwardedForHeaderValue
);
}
}
Finally, consume the value in your services implementation:
String xForwardedForHeaderValue = XForwardedForHeaderHolder.getXForwardedForHeader();
// Clean up
XForwardedForHeaderHolder.clean();
A word of caution: on one hand, the filter registration should work properly but it could depend on the JAXRS version you are using and Swagger itself; on the other, the solution assume that the filter will provide, in the thread local variable, the right header for every request to the underlying services, in other words, that there are not any threading related issue. I think it should be the case, but it is something that need to be tested.

How to dynamic generate Enum type from mysql in Java spring boot application?

In my project,we want manage all REST APIs errorcodes in a Enum type,like the following code:
package com.example.util
public enum ErrorType{
SUCCESS("0000", "success")
PARAMS_EMPTY("3001", "params cannot be empty")
}
The problem we encounter is if we put the class into util package,everytime we add a new error type in business spring boot app,we'll need to modify,publish and recompile the app and util project.That would be hard to maintance the util package.Basically,we prefer to maintance a relatively stable utility package.
So we are considering if there is a way that we can generate Enum type dynamiclly,we can comfigure the error information in mysql in advance,then we can load them into enum type in application boot procedure.
I'm not sure is this a good idea to dynamic generate enum type in Java,or if there is a better solution for this problem.
You can't add or remove values from an enum. Enums are complete static enumerations.
If you need to handle variable values you need to work with a standard class.
For example you can have something like the following:
public Error {
public static Error getByName(String name) {
....
}
public static Error getByCode(int code) {
....
}
}
and use it as follow:
Error myError404 = Error.getByCode(404);
Obviously this code gives you a lot of flexibility, but you cannot know in advance if a particular error code exists or not. And you can't use ide facilities related to enums.
Generating an enum would not be so useful I think, since part of the power of enums is that you can use them statically in your code - so then you have to update your code anyway.
How about adding a an exception handler to your util library that can be populated with error codes / description mappings from the database that can then translate errors/exceptions to response codes / error messages for your API? (just guessing you have an api here :-) )
Thanks for your reply,we finally decide to give up this idea.Dynamic generate an enum would not help,indeed it will add more complexity to our project,it's not a common use of enum.
Instead of this,we predefine our main error type likes user_error,system_error and db_error,etc.The specific error information will be processed in the business service.

versioning rest service by creating abstract class?

I have a REST service with version v1 running fine in production. Now I need to make version v2 url as the response format is changed so we don't want to affect our current customers who are using v1 url. We will be returning some other object back with version v2 url instead of using ClientResponse object.
Below is my current design in which version is provided in #Path annotation. This was done by somebody else who left our team.
#Component
#Scope("request")
#Path("/abc/hello/v1")
public class ClientService {
// ... some variables
#GET
#Path("/json/line")
#Produces(MediaType.APPLICATION_JSON)
public ClientResponse getLineData(#Context UriInfo uriInfo) {
}
}
What is the best way to design version v2 url here? Should I just make a new class and have #Path as #Path("/abc/hello/v2") like this and copy paste everything in it? Or should I create some abstract class and have ClientServiceV1 extend that abstract class and then have ClientServiceV2 extend that abstract class as well? How should I proceed?
My strategy for versioning REST API is to not let JAX-RS runtime automatically determine what REST resources to load and instead explicitly state them in the java.ws.rs.Application implementation.
My java.ws.rs.Application implementation is where I do the versioning and I state it there the base API URI
#javax.ws.rs.ApplicationPath("v1")
public class MyAppV1 extends java.ws.rs.Application {
Set<Class<?>> getClasses() {
return new java.util.HashSet<>(java.util.Arrays.asList(
ClientService.class,
OtherService.class));
}
}
And then create another one for "v2" where I start adding my components there.
The intent of it is I can have multiple versions present and I can deprecate the old ones and eventually remove them as needed. It also allows me to reuse the existing services.
However, if your existing services are suffixed with "v1" then you may want to either duplicate the code or make it point to the new version depending on your needs.

Best practice for grouping Java classes for instantiation clarity

I am building a piece of software that sends and receives messages in particular binary definitions and with a particular version. As such, I have classes that look like this, which vary mostly only in the package name (the version, in this case):
For version 1.5:
com.mydomain.clothesmessage.v0105.fielddefinitions.Field100
com.mydomain.clothesmessage.v0105.fielddefinitions.Field200
com.mydomain.clothesmessage.v0105.messagedefinitions.Pants
com.mydomain.clothesmessage.v0105.messagedefinitions.Socks
and for version 2.7:
com.mydomain.clothesmessage.v0207.fielddefinitions.Field100
com.mydomain.clothesmessage.v0207.fielddefinitions.Field200
com.mydomain.clothesmessage.v0207.messagedefinitions.Pants
com.mydomain.clothesmessage.v0207.messagedefinitions.Socks
The class that manages the transmission and reception of these messages uses all versions, depending on where the message comes from, etc.
My problem is that defining an instance of the class requires I use the entire package path, because otherwise it's ambiguous. Even if there exists a situation where I use only one version in a given file, a casual reader of the code won't be able to see what version is being used. Pants pants = new Pants() is ambiguous until you look at the imported package.
My ideal usage of this would be something like this:
V0207.Pants pantsMessage = new V0702.Pants();
That makes it very clear what version is being used. I could make this happen by creating the Pants message classes as inner classes of the V0207 class, but then the V0207 class becomes gigantic (there could be a hundred messages, each with 100 fields, for every given version). Is there possibly a way to #include an inner class, so they can be stored in separate files? This would be ideal.
I suppose I can emulate this with a wrapper class, that does something (silly?) like this, where there exists an instance of the Pants class in the V0207 object:
Object pantsMessage = V0207.pants.getClass().newInstance();
((com.mydomain.clothesmessage.v0207.messagedefinitions.Pants)pantsMessage).getZipperType();
But I dislike that. It looks contrived and requires try/catch and casting when in use. Terrible.
I could also use a factory. That would be a bit nicer, but requires a parent class (or interface) and would require casting when used, since each message has unique methods.
Message pantsMessage = V0207Factory.newMessage(V0207.PantsMessage);
((com.mydomain.clothesmessage.v0207.messagedefinitions.Pants)pantsMessage).getZipperType();
or
Message sockMessage = V0207Factory.newSock();
((com.mydomain.clothesmessage.v0207.messagedefinitions.Socks)sockMessage).getSmellLevel();
What are your thoughts? I'm using JDK 1.7, but 1.8 might be usable.
Consider using the factory design pattern with interfaces. The version of Java that you use does not make a difference (though support for Java 7 goes away in the spring, April if I remember correctly).
Define an interface for each class containing the method signatures that will be implemented by all the versions of the class.
Update your class definitions to include the appropriate interface definition.
Create a class factory for each needed class, passing it the information needed to create the appropriate version of the class. This class factory should return the interface type for the created class.
Here is an example:
TestPants
public class TestPants {
IPants pants = PantsFactory.PantsFactory(207);
Message zipperType = pants.getZipperType();
Message color = pants.getColor();
)
}
IPants
public interface IPants {
Message getZipperType();
Message getColor();
}
Pants
public class Pants implements IPants {
// Class fields and Object fields
#Override
public Message getColor () {
return null;
}
#Override
public Message getZipperType () {
return null;
}
// implement any common methods among all versions
}
PantsV0105
public class PantsV0105 extends Pants {
// add changes for this version
}
PantsV0207
public class PantsV0207 extends Pants {
// add changes for this version
}
PantsFactory
public class PantsFactory {
public static IPants PantsFactory(int version) {
switch (version) {
case 105: return new PantsV0105(); break;
case 207: return new PantsV0207(); break;
default: return null;
}
}
I initially solved this by using inner static classes in one gigantic "version" class. Thus, the use looked like this:
V0207.Pants pantsMessage = new V0702.Pants();
But the version class ('V0207') grew too quickly, especially as other developers on the team demanded a more "Java" way of setting the fields (which required a lot of getters and setters).
Thus, the final solution is to put the messages inside their own v0207.messages package name, and prepend each message with the version:
V0207_Pants pantsMessage = new V0702_Pants();
It's not as nice as using a C++ namespace, but it works. The version is clear to the reader, and the object can contain a lot of code without any files becoming too large.

How to get a reference of cucumber RuntimeOptions when using cucumber-java and junit?

I am using cucumber-java in groovy code. I prefer cucumber-java to cucumber-groovy because I can run the tests like plain old good JUnit tests. However, the step definition templates spitted out by cucumber are in java style. Instead, I would like to have a groovy style. For example, in java style, you will get something like
#When("^an HTTP GET request is sent to obtain config.xml of \"([^\"]*)\"$")
public void an_HTTP_GET_request_is_sent_to_obtain_config_xml_of(String arg1) throws Throwable {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
Since I am using groovy, I would like to get something like
#When(/^an HTTP GET request is sent to obtain config.xml of "([^"]*)"$/)
void 'an HTTP GET request is sent to obtain config.xml of'(String arg1) {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
I am thinking to implement such a feature. Basically, my idea is to add a new field, maybe called templateLanguage, in cucumber.api.CucumberOptions. When this new field is equal to groovy, then the groovy-style templates will be spitted. This will probably involve an if statement in cucumber.runtime.java.JavaSnippet.template(), such as
if( runtimeOptions.getTemplateLanguage().toLowerCase().equals('groovy') ) {...}
However, my question is: how can I get a reference of the runtimeOptions that is passed in like
#CucumberOptions(
format = ["pretty", "html:build/cucumber"],
features="src/test/resources/cucumber_features/api/job_view.feature",
glue=['com.yahoo.adcd.jenkins.tests.smoke.api.cucumber.job.view'],
strict = true
)
Thank you very much!
In a case like this, you would need to write your own boot class since there is no dependency injection for RuntimeOptions. A good starting location is to look at cucumber.api.cli.Main. You would need to create your own class that extends RuntimeOptions, then add in your logic there.
This solution, however, will not allow you run the app using the CucumberOptions annotation anymore. If you do prefer using the annotation though, you would need to also implement your own custom annotation and override the RuntimeOptionsFactory to use your annotation, and then use that factory in your new main class to create the runtime dynamically.

Categories