So I have an API client type class right now, which I am trying to connect to my repository so that I can store data in the MySQL database.
The problem I'm having is that the API client class instantiates a new object of itself, so the Autowiring doesn't work correctly. I've looked around for a workaround for this problem, and I've seen a couple options, but I'm confused on how to apply them to my problem.
For reference, here are parts of some of the relevant files:
GeniusApiClient.java:
#Component
public final class GeniusApiClient {
private final OkHttpClient client = new OkHttpClient();
#Autowired
private ArtistDao artistDao;
public static void main(String[] args) throws Exception {
GeniusApiClient geniusApiClient = new GeniusApiClient();
String artistId = (geniusApiClient.getArtistId("Ugly Duckling"));
ArrayList<String> artistSongIds = geniusApiClient.getArtistSongIds(artistId);
System.out.println(geniusApiClient.getAllSongAnnotations(artistSongIds, artistId));
}
public String getAllSongAnnotations(ArrayList<String> songIds, String artistId) {
Artist artist = new Artist("test name for now", "string123", "223");
artistDao.save(artist);
return "finished";
}
}
ArtistDao.java:
#Transactional
public interface ArtistDao extends CrudRepository<Artist, Long> {
public Artist findByGeniusId(String geniusId);
}
ArtistController.java:
#Controller
public class ArtistController {
#Autowired
private ArtistDao artistDao;
/**
* GET /create --> Create a new artist and save it in the database.
*/
#RequestMapping("/create")
#ResponseBody
public String create(String name, String annotations, String genius_id) {
String userId = "";
try {
genius_id = genius_id.replaceAll("/$", "");
Artist artist = new Artist(name, annotations, genius_id);
artistDao.save(artist);
userId = String.valueOf(artist.getId());
}
catch (Exception ex) {
return "Error creating the artist: " + ex.toString();
}
return "User succesfully created with id = " + userId;
}
/**
* GET /get-by-email --> Return the id for the user having the passed
* email.
*/
#RequestMapping("/get")
#ResponseBody
public String getByEmail(String genius_id) {
String artistId = "";
try {
Artist artist = artistDao.findByGeniusId(genius_id);
artistId = String.valueOf(artist.getId());
}
catch (Exception ex) {
return "User not found";
}
return "The user id is: " + artistId;
}
}
The problem is that in GeniusApiClient.java in the getAllSongAnnotations method, I have a null pointer exception when I try and access the artistDao. I understand that my instantiation of this class is what is messing up the Autowiring, but I'm curious on what the best way to go about fixing this might be.
I considered making all of my methods in the class static so that I wouldn't have to instantiate a new method, but I don't think this would work very well. Any suggestions?
Thanks
EDIT:
Removed some irrelevant code for clarity.
EDIT2:
Added ArtistController.java
To be able to autowire/inject an object, that object must be a Spring bean.
Here you can't autowire ArtistDao because it's not a bean. There are several annotation options to make it bean but the one suits in this case is #Repository annotation. It's just a specialized version of #Component which you used in GeniusApiClient class.
So,
#Repository
#Transactional
public interface ArtistDao extends CrudRepository<Artist, Long> {
public Artist findByGeniusId(String geniusId);
}
should work.
I'd suggest you to read: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html
If reading reference documentation sounds scary to you, you can also take a look at Core Spring part of Spring in Action.
Don't make GeniusApiClient.class final. Spring will use CGLIB to dynamically extend your class in order to make a proxy. And the requirement for CGLIB to work is to have your classes non-final.
More on this here: Make Spring Service Classes Final?
What you are trying to do in your catch block is not clear to me,you have to correct that and replace it with desired action to be taken on any exception occurrence.
Related
Given the following classes :
class Contact{
#Getter
#Setter
#Email
private String mail;
}
#RestController
class ContactController{
#PostMapping(path = "/contacts")
public ResponseEntity<Contact> addContact(
#Valid #RequestBody Contact contact) {
....
}
}
How can I test in a gherkin scenario ?
I've tried the following :
Scenario Outline: validation failed use-case
Given I am authenticated as "<userFirstName>"
When I register a new contact "<contactJsonLocation>"
Then it should throw an ConstraintViolationException of contact
Examples:
| userFirstName | contactJsonLocation |
| Simon | contactWithMailNotWellFormatted.json |
Steps code :
public CreateContactSteps(ContactController contactController) {
When("^I register a new contact \"([^\"]*)\"$",
(String contactJsonLocation) -> {
try {
Contact contact;
String jsonContact = Files.readString(Paths.get(new ClassPathResource("files/" + contactJsonLocation).getURI()));
contact = objectMapper.readValue(jsonContact, Contact.class);
String id = UUID.randomUUID().toString();
contact.setId(id);
createContactAttempt.setId(id);
createContactAttempt.setMail(contact.getMail());
contactController.addContact(contact);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ConstraintViolationException e) {
createContactAttempt.lastException = e;
}
});
Then("^it should throw an ConstraintViolationException of contact$",
() -> {
Assertions.assertTrue(createContactAttempt.lastException instanceof ConstraintViolationException);
createContactAttempt.lastException = null;
});
When I'm testing to create a new contact after starting my spring boot app all is going well on the validation part, meaning I'm receiving the expected 400 error.
But when I'm calling the ContactController from my test context, it fails to valid and the contact is created.
I'm guessing it has something to do with spring doing some magic behind the scene, but what ?
Right now I'm initiating myself the cucumber application context like this (and I might be doing something wrong, I'm open to suggestion / good criticism) :
#RunWith(Cucumber.class)
#CucumberOptions(plugin = {"pretty", "html:FeaturesReport.html"},
features = {"src/test/resources/features"})
public class AllAcceptanceTest {
}
#CucumberContextConfiguration
#ContextConfiguration(classes = {BeanConfiguration.class})
public class ContextConfigurationTesting implements En {
}
#Configuration
#RequiredArgsConstructor
public class ControllerConfiguration {
#Bean
public ContactController contactController() {
return new ContactController(
... //every other bean the controller need
);
}
...
}
The #Valid annotation signals the caller of addContact that the object should be valid. By directly calling addContact you are bypassing that. You may want to consider using MockMvc to call your controller instead.
I would suggest using Spring boot to setup your application context instead. Spring Boot has a rich set of features that let you test many aspects of your application easily.
I ma trying to migrate some old code from WebSphere to Tomcat. The older code used Spring 3.2, now I upgraded the JARs to 5.2.2. But somehow object values just do not persist.
My controller class is:
#Controller
#Scope("session")
public class OperationController {
private GUIDataObject guiDO = null;
/**
* Constructor
*/
public OperationController() {
}
#RequestMapping(value="/readDataSource")
#ResponseBody
public String readDataSource() {
try {
String[] sources = guiDO.getDataSources();
.
.
.
Code to work on Array sources
.
.
.
return "ok";
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
/**
* Set the data sources in the Data Storage Area - these are passed as a "parameter" map
* in the request.
* #param webRequest : WebRequest which parameter map can be pulled from
* #return "ok"
*/
#RequestMapping(value="/setDataSources")
#ResponseBody
public String setDataSources(WebRequest webRequest) {
guiDO.setDatasources(webRequest.getParameterMap());
return "ok";
}
.
.
.
Lots of other code.
.
.
.
}
and the values are stored in the object:
public class GUIDataObject {
private String service;
private String uniqueProcessId;
private String userId;
private String vendor;
// record the data sources to read from
private Map<String, String[]> dataSources = null;
public GUIDataObject(String service, String uniqueProcessId, String userId, String vendor) {
super();
this.service = service;
this.uniqueProcessId = uniqueProcessId;
this.userId = userId;
this.vendor = vendor;
}
public void setDatasources(Map<String, String[]> datasources) {
this.dataSources = datasources;
}
public String[] getDataSources() throws Exception {
if (this.dataSources == null) {
throw new Exception("No datasources have been set from the GUI");
}
if (!this.dataSources.containsKey("source")) {
throw new Exception("No datasources have been set from the GUI");
}
return this.dataSources.get("source");
}
.
.
.
Lots of methods.
.
.
.
}
Now my problem is the dataSources Map is getting set fine. But when fetching the values they return empty. It errors out in the second if-block, so I can say at least its not null. There are other Maps/Strings in the object as well, but I cant really tell if they are being properly set or not, since, this is the first method that is being hit and it errors out after that. I can see that the values that are initialized in the constructor are being retained just fine. So cant really where it is going wrong.
The same code worked fine on WebSphere and Spring 3.2. Now I am not sure if there are any new configurations that I need to in order to get this to work. Since 3.2 is very very old. Any help on this would be appreciated.
The problem was the way webRequest.getParameterMap() works in WebSphere and Tomcat. In WebSphere it returns a concrete HashTable. But in Tomcat, it returns a org.apache.catalina.util.ParameterMap which is a subclass of HashMap. And somehow they just don't mix. Even casting throws a ClassCastException.
I got it to work by changing the dataSources to a HashMap.
private HashMap<String, String[]> dataSources = null;
and the set method to:
public void setDatasources(Map<String, String[]> datasources) {
if (this.dataSources == null) {
this.dataSources = new HashMap<String, String[]>();
this.dataSources.putAll(datasources);
} else {
this.dataSources.putAll(datasources);
}
Probably I could have left the dataSources as a Map and it still would have worked. But I didn't try it out.
I'm unsuccessfully trying to conditionally and dynamically pick which property to serialize to respond to each request with Jersey (using Jackson). The idea behind this is to securely access to properties of objects within a REST API.
I have several objects that I return in API calls that should show/hide fields depending in the user who is authenticated.
For example, lets say I have an object Car
public class Car implements Serializable {
private Long id;
private String VIN;
private String color;
...
}
Lets say that if an user with the ROLE_ADMIN is authenticated, all properties should be returned, but if there isn't a logged user only the first two need to be shown.
I was thinking on building something that's annotation based. Something like:
public class Car implements Serializable {
private Long id;
private String VIN;
#Secured({AccessRole.ROLE_ADMIN})
private String color;
...
}
In this case, the color property should only be returned if the access role of the requesting user matches the ones passed via the annotation.
But I'm unable to get a hook on where should I implement this logic.
What I'm trying to implement is a sort of #JsonIgnore but that's conditional and dynamic. All solutions I found so far are static.
Is this even possible?
Jersey has support for Entity Filtering. Aside from general filtering, it also supports Role-based Entity Filtering using (javax.annotation.security) annotations.
So you can use the #RolesAllowed, #PermitAll, and #DenyAll annotations on the domain model properties
public static class Model {
private String secured;
#RolesAllowed({"ADMIN"})
public String getSecured() { return this.secured; }
}
To make this work though, you need to have set the SecurityContext inside of a request filter. Jersey will look up the SecurityContext to validate the roles. You can read more about it in this post (Note: the entity filtering is separate from any real authorization that is mentioned in that post. But the post does explain about the SecurityContext).
Basically you will have something like (notice the last line where you set the SecurityContext).
#PreMatching
public static class SimpleAuthFilter implements ContainerRequestFilter {
private static final Map<String, User> userStore = new ConcurrentHashMap<>();
static {
userStore.put("peeskillet", new User("peeskillet", Arrays.asList("ADMIN", "USER")));
userStore.put("paulski", new User("paulski", Arrays.asList("USER")));
}
#Override
public void filter(ContainerRequestContext request) throws IOException {
final String authHeader = request.getHeaderString("Authorization");
final String username = authHeader.split("=")[1];
final User user = userStore.get(username);
if (user == null) {
throw new NotAuthorizedException("No good.");
}
request.setSecurityContext(new SimpleSecurityContext(user));
}
}
Where the SimpleSecurityContext is just a class of your own, where you need to override the isUserInRole method and check if the user has the role
private static class SimpleSecurityContext implements SecurityContext {
private final User user;
SimpleSecurityContext(User user) {
this.user = user;
}
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return user.getUsername();
}
};
}
#Override
public boolean isUserInRole(String role) {
return user.getRoles().contains(role);
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return "simple";
}
}
That's pretty much it. You will also need to register the SecurityEntityFilteringFeature with the application to make it all work.
See a complete test case in this Gist
You can register a custom MessageBodyWriter https://jersey.java.net/documentation/latest/user-guide.html#d0e6951
The MessageBodyWriter will use your custom logic to decide what to write.
It can be done with #JsonView as #dnault suggested.
http://www.baeldung.com/jackson-json-view-annotation
Your MessageBodyWriter will hold a jackson mapper and you will apply the writerWithView with the matching view class as described in the above link.
EDIT: see this one - Jackson Json serialization: exclude property respect to the role of the logged user
Stack is Spring Boot w/ Jetty/Jersey. Here's the resource method in question:
#GET
#Path("campaignTargets")
#Produces(MediaType.APPLICATION_JSON)
#Transactional(readOnly=true)
public List<CampaignTargetOutputDTO> getCampaignTargets(
#PathParam("businessUnitId") Integer id,
#QueryParam("name") String name,
#Pattern(regexp = DATE_VALIDATION_PATTERN) #QueryParam("startDate") String startDate,
#Pattern(regexp = DATE_VALIDATION_PATTERN) #QueryParam("endDate") String endDate,
#Pattern(regexp = INTEGER_CSV_VALIDATION_PATTERN) #QueryParam("targetTypeIds") String targetTypeIds,
#Pattern(regexp = ALPHANUM_CSV_VALIDATION_PATTERN) #QueryParam("statuses") String statuses) {
return ResourceUtil.entityOr404(campaignService.getAdvertiserCampaignTargets(id, name, startDate, endDate, targetTypeIds, statuses));
}
When Jersey intercepts the call to this method to perform the validation, it doesn't (always) get this method. The reason I know this is because I have taken the advice of the Jersey documentation and created the following ValidationConfig:
#Provider
public class ValidationConfigurationContextResolver implements
ContextResolver<ValidationConfig> {
#Context
private ResourceContext resourceContext;
#Override
public ValidationConfig getContext(Class<?> type) {
final ValidationConfig config = new ValidationConfig();
config.constraintValidatorFactory(
resourceContext.getResource(InjectingConstraintValidatorFactory.class));
config.parameterNameProvider(new CustomParameterNameProvider());
return config;
}
private static class CustomParameterNameProvider extends DefaultParameterNameProvider {
private static final Pattern PROXY_CLASS_PATTERN = Pattern.compile("(.*?)\\$\\$EnhancerBySpringCGLIB\\$\\$.*$");
public CustomParameterNameProvider() {
}
#Override
public List<String> getParameterNames(Method method) {
/*
* Since we don't have a full object here, there's no good way to tell if the method we are receiving
* is from a proxy or the resource object itself. Proxy objects have a class containing the string
* $$EnhancerBySpringCGLIB$$ followed by some random digits. These proxies don't have the same annotations
* on their method params as their targets, so they can actually interfere with this parameter naming.
*/
String className = method.getDeclaringClass().getName();
Matcher m = PROXY_CLASS_PATTERN.matcher(className);
if(m.matches()) {
try {
return getParameterNames(method.getDeclaringClass().getSuperclass().
getMethod(method.getName(), method.getParameterTypes()));
} catch (Exception e) {
return super.getParameterNames(method);
}
}
Annotation[][] annotationsByParam = method.getParameterAnnotations();
List<String> paramNames = new ArrayList<>(annotationsByParam.length);
for(Annotation[] annotations : annotationsByParam) {
String name = getParamName(annotations);
if(name == null) {
name = "arg" + (paramNames.size() + 1);
}
paramNames.add(name);
}
return paramNames;
}
private String getParamName(Annotation[] annotations) {
for(Annotation annotation : annotations) {
if(annotation.annotationType() == QueryParam.class) {
return ((QueryParam) annotation).value();
} else if(annotation.annotationType() == PathParam.class) {
return ((PathParam) annotation).value();
}
}
return null;
}
}
}
My main problem with this solution is that it requires a paragraph of comment to (hopefully) prevent future confusion. Otherwise it seems to work. Without this, I get uninformative parameter names like arg1 and so on, which I'd like to avoid. Another big problem with this solution is that it relies too heavily on the implementation of Aop proxying in Spring. The pattern may change and break this code at some point in the future and I may not be here to explain this code when the comment fails to illuminate its purpose. The weirdest thing about this is that it seems to be intermittent. Sometimes the parameter names are good and sometimes they're not. Any advice is appreciated.
It turns out this happens as a result of running the server from eclipse. I haven't quite figured out why, but running the server from the command line fixes the problem. If anyone can figure out why eclipse does this and how to turn off whatever "feature" of eclipse is causing this, I will upvote/accept your answer. For now the answer is, don't run the service in eclipse.
I'm building a RESTful API and want to provide developers with the option to choose which fields to return in the JSON response. This blog post shows examples of how several API's (Google, Facebook, LinkedIn) allow developers to customize the response. This is referred to as partial response.
An example might look like this:
/users/123?fields=userId,fullname,title
In the example above the API should return the userId, fullName and title fields for User "123".
I'm looking for ideas of how to implement this in my RESTful web service. I'm currently using CXF (edit: and Jackson) but willing to try another JAX-RS implementation.
Here's what I currently have. It returns a full User object. How can I return only the fields the API caller wants at runtime based on the "fields" paramaeter? I don't want to make the other fields Null. I simply don't want to return them.
#GET
#Path("/{userId}")
#Produces("application/json")
public User getUser(#PathParam("userId") Long userId,
#DefaultValue("userId,fullname,title") #QueryParam("fields") String fields) {
User user = userService.findOne(userId);
StringTokenizer st = new StringTokenizer(fields, ",");
while (st.hasMoreTokens()) {
// here's where i would like to select only the fields i want to return
}
return user;
}
UPDATE:
I followed unludo's link which then linked to this: http://wiki.fasterxml.com/JacksonFeatureJsonFilter
With that info I added #JsonFilter("myFilter") to my domain class. Then I modified my RESTful service method to return String instead of User as follows:
#GET
#Path("/{userId}")
#Produces("application/json")
public String getUser(#PathParam("userId") Long userId,
#DefaultValue("userId,fullname,title") #QueryParam("fields") String fields) {
User user = userService.findOne(userId);
StringTokenizer st = new StringTokenizer(fields, ",");
Set<String> filterProperties = new HashSet<String>();
while (st.hasMoreTokens()) {
filterProperties.add(st.nextToken());
}
ObjectMapper mapper = new ObjectMapper();
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));
try {
String json = mapper.filteredWriter(filters).writeValueAsString(user);
return json;
} catch (IOException e) {
e.printStackTrace();
return e.getMessage();
}
}
I need to do more testing but so far so good.
If you use Jackson (a great JSON lib - kind of the standard for Java I believe), you may use the #View annotation to filter what you want in the resulting object.
I understand that you want something dynamic so it's a bit more complicated. You will find what you are looking for here: http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html (look at 6. Fully dynamic filtering: #JsonFilter).
I would be interested in the solution you will find.
Creating an ObjectMapper instance inside the resource method for every request can have significant performance overhead. According to the Jackson performance best practices object mappers are expensive to create.
Instead you can customize the JAX-RS provider's Jackson object writer inside the resource method using the Jackson 2.3 ObjectWriterModifier/ObjectReaderModifier feature.
Here is an example shows how to register an ObjectWriterModifier thread local object that changes the set of the filters applied for the JAX-RS Jackson provider being used inside a resource method. Note that I have not tested the code against an JAX-RS implementation.
public class JacksonObjectWriterModifier2 {
private static class FilterModifier extends ObjectWriterModifier {
private final FilterProvider provider;
private FilterModifier(FilterProvider provider) {
this.provider = provider;
}
#Override
public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> responseHeaders,
Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException {
return w.with(provider);
}
}
#JsonFilter("filter1")
public static class Bean {
public final String field1;
public final String field2;
public Bean(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
}
public static void main(String[] args) throws IOException {
Bean b = new Bean("a", "b");
JacksonJsonProvider provider = new JacksonJsonProvider();
ObjectWriterInjector.set(new FilterModifier(new SimpleFilterProvider().addFilter("filter1",
SimpleBeanPropertyFilter.filterOutAllExcept("field1"))));
provider.writeTo(b, Bean.class, null, null, MediaType.APPLICATION_JSON_TYPE, null, System.out);
}
}
Output:
{"field1":"a"}
The Library jersey-entity-filtering Can do that :
https://github.com/jersey/jersey/tree/2.22.2/examples/entity-filtering-selectable
https://jersey.java.net/documentation/latest/entity-filtering.html
Exemple :
My Object
public class Address {
private String streetAddress;
private String region;
private PhoneNumber phoneNumber;
}
URL
people/1234?select=streetAddress,region
RETURN
{
"streetAddress": "2 square Tyson",
"region": "Texas"
}
Add to Maven
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-entity-filtering</artifactId>
<version>2.22.2</version>
</dependency>