I tried to implement a custom config type and it worked. However, when I use the custom type with a group of config using the #ConfigProperties it fails to automatically recognize the property by its name and instead treats the property as an object with a nested property.
How can I implement such a behavior correctly? (I am new to Quarkus, so please correct me if I am doing something wrong here)
Here is a code snippet that converts a custom type:
public class Percentage {
private double percentage;
public Percentage() {}
public Percentage(double percentage) {
this.percentage = percentage;
}
public void setPercentage(double percentage) {
this.percentage = percentage;
}
public double getPercentage() {
return this.percentage;
}
}
#Priority(300)
public class PercentageConverter implements Converter<Percentage> {
#Override
public Percentage convert(String value) {
int percentIndex = value.indexOf("%");
return new Percentage(Double.parseDouble(value.substring(0, percentIndex - 1)));
}
}
/// this works ------
public class Hello {
#ConfigProperty(name = "custom.vat")
Percentage vat;
public Hello () {
}
// .....
}
/// however, this fails
#ConfigProperties(prefix = "custom")
public class CustomConfig {
public Percentage vat;
public Percentage profit;
}
javax.enterprise.inject.spi.DeploymentException: No config value of type [double] exists for: custom.vat.percentage
at io.quarkus.arc.runtime.ConfigRecorder.validateConfigProperties(ConfigRecorder.java:39)
Unfortunately, I believe this does not work because Quarkus #ConfigProperties, handles these cases as if they were subgroups and try to map nested properties with the configuration (and not use the Converter).
Feel free to open up an issue in Quarkus GH if you feel this should change: https://github.com/quarkusio/quarkus/issues
Alternately, you can use SR Config #ConfigMapping: https://smallrye.io/docs/smallrye-config/mapping/mapping.html. It covers a few more cases, including direct Conversion and in the future it may replace Quarkus #ConfigProperties.
Related
I have a class containing constants:
namespace Test.AppService
{
public static class Const
{
public const bool Tmr = false;
public const int Pti = 10;
...
I was wondering if this would be a good candidate for dependency injection or would it be better to leave it as it is and just add using for Test.AppService into every page? Would appreciate advice on this.
Reading your comment about needing to use a different set of constants if that is something you see happening then Dependency injection makes sense. For example if you are using different environments like DEV, QA, Release comes to mind.
You would need to declare an interface with all your public fields. Implement that Interface in different classes with all the possible different scenarios. Then you can register your interface and the class with your desired set of values that you would be able to swap as needed.
For example:
public interface IConfiguration
{
public string ConnectionString {get;}
}
public class QaValues : IConfiguration
{
public string ConnectionString
{ get
{
return "qaconnection";
}
}
}
public class ReleaseValues : IConfiguration
{
public string ConnectionString
{ get
{
return "releaseconnection";
}
}
}
DependencyService.Register<IConfiguration,QaValues>();
In our Workspace customization I need to check if the voice.mark-done-on-release option is set to true. Using a decompiler I can see that this option is exposed in Genesyslab.Desktop.Modules.Voice.VoiceOptions object as property VoiceMarkDoneOnRelease - but how can I get to that?
I can see that all I would need to do is get the value from the ConfigManager but it would be nice to reference the public property instead so that if it ever changes the compiler will know about it.
namespace Genesyslab.Desktop.Modules.Voice
{
public class VoiceOptions : Options
{
...
public bool VoiceMarkDoneOnRelease
{
get
{
return this.configManager.GetValueAsBoolean("voice.mark-done-on-release", false);
}
}
The best way I could find is to inject the IConfigManager and instantiate your own instance of VoiceOptions:
using Genesyslab.Desktop.Infrastructure.Configuration;
namespace YourNamespace
{
public class YourClass
{
private readonly IConfigManager _genesysConfigManager;
public CAMSessionService(IConfigManager genesysConfigManager)
{
_genesysConfigManager = genesysConfigManager;
}
private VoiceOptions GetVoiceOptions()
{
return VoiceOptions.CreateNewInstance(_genesysConfigManager);
}
}
I am fairly new to Neo4J; I am developing a project for learning purposes on which I am facing an issue that I am not managing to solve. My model might be somewhat relational DB influenced, but design issues aside, I believe however that what I am attempting should technically be done.
I have a NodeEntity Foo with an nested object Bar, converted to- and from String via ConversionService. In effect, Bar contains only one single String field, making the mapping trivial.
#NodeEntity
public class Foo {
#GraphId
private Long id;
#Indexed
private Bar bar;
...
}
public class Bar {
private String value;
...
}
When returning from a fairly simple Cypher query defined as follows on my repository:
#RepositoryRestResource(...)
public interface FooRepository
extends PagingAndSortingRepository<Foo, Long> {
...
#Query ("MATCH (foo) RETURN foo.bar")
Iterable<Bar> listBars ();
...
}
Conversion is configured as follows:
#Configuration
#ComponentScan(value = "de.h7r.playground.sd.neo4j",
excludeFilters = #ComponentScan.Filter({ Configuration.class }))
public class PKanbanConfiguration {
#Bean
public ConversionServiceFactoryBean conversionService ()
throws Exception {
final ConversionServiceFactoryBean csfb = new ConversionServiceFactoryBean ();
csfb.setConverters (getConverters ());
return csfb;
}
private Set<Converter> getConverters () {
return Sets.newHashSet (new BarConverter.ToString (), new BarConverter.FromString ());
}
}
Where the code for BarConverter is as follows.
public class BarConverter {
public static class FromString <S extends String, P extends Bar>
implements Converter<S, P> {
#Override
public P convert (final S source) {
// sets value into new instance of Bar and returns
}
}
public static class ToString <P extends Bar, S extends String>
implements Converter<P, S> {
#Override
public S convert (final P source) {
// gets value from Bar and returns
}
}
}
I am getting the following exception.
org.springframework.data.mapping.model.MappingException: Unknown persistent entity test.domain.Bar
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:178)
...
Bar is indeed not a persitent entity nor should in my understanding be one. I grasp that this might have something to do with the defined return type of listBars; on the other hand, the repository if of Foos, so I had expected it to work. I would very much not like to fetch the whole set of nodes and then filter only the nested objects; the same way I would not like to have Bar replaced by String on Foo, since their type might have semantic relevance.
I am a bit lost as to how to returning all the property values for the existing nodes, specially since this query works as expected from neo4j-shell, so I see this as a pure Spring mapping issue.
I can add any further information that might prove helpful upon request.
Any help is very much appreciated.
I'm using Spring 4.0.0.RELEASE, Spring Data Commons 1.7.0.M1, Spring Hateoas 0.8.0.RELEASE
My resource is a simple POJO:
public class UserResource extends ResourceSupport { ... }
My resource assembler converts User objects to UserResource objects:
#Component
public class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> {
public UserResourceAssembler() {
super(UserController.class, UserResource.class);
}
#Override
public UserResource toResource(User entity) {
// map User to UserResource
}
}
Inside my UserController I want to retrieve Page<User> from my service and then convert it to PagedResources<UserResource> using PagedResourcesAssembler, like displayed here: https://stackoverflow.com/a/16794740/1321564
#RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(#PageableDefault Pageable p, PagedResourcesAssembler assembler) {
Page<User> u = service.get(p)
return assembler.toResource(u);
}
This doesn't call UserResourceAssembler and simply the contents of User are returned instead of my custom UserResource.
Returning a single resource works:
#Autowired
UserResourceAssembler assembler;
#RequestMapping(value="{id}", method=RequestMethod.GET)
UserResource getById(#PathVariable ObjectId id) throws NotFoundException {
return assembler.toResource(service.getById(id));
}
The PagedResourcesAssembler wants some generic argument, but then I can't use T toResource(T), because I don't want to convert my Page<User> to PagedResources<User>, especially because User is a POJO and no Resource.
So the question is: How does it work?
EDIT:
My WebMvcConfigurationSupport:
#Configuration
#ComponentScan
#EnableHypermediaSupport
public class WebMvcConfig extends WebMvcConfigurationSupport {
#Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(pageableResolver());
argumentResolvers.add(sortResolver());
argumentResolvers.add(pagedResourcesAssemblerArgumentResolver());
}
#Bean
public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
return new HateoasPageableHandlerMethodArgumentResolver(sortResolver());
}
#Bean
public HateoasSortHandlerMethodArgumentResolver sortResolver() {
return new HateoasSortHandlerMethodArgumentResolver();
}
#Bean
public PagedResourcesAssembler<?> pagedResourcesAssembler() {
return new PagedResourcesAssembler<Object>(pageableResolver(), null);
}
#Bean
public PagedResourcesAssemblerArgumentResolver pagedResourcesAssemblerArgumentResolver() {
return new PagedResourcesAssemblerArgumentResolver(pageableResolver(), null);
}
/* ... */
}
SOLUTION:
#Autowired
UserResourceAssembler assembler;
#RequestMapping(value="", method=RequestMethod.GET)
PagedResources<UserResource> get(#PageableDefault Pageable p, PagedResourcesAssembler pagedAssembler) {
Page<User> u = service.get(p)
return pagedAssembler.toResource(u, assembler);
}
You seem to have already found out about the proper way to use but I'd like to go into some of the details here a bit for others to find as well. I went into similar detail about PagedResourceAssembler in this answer.
Representation models
Spring HATEOAS ships with a variety of base classes for representation models that make it easy to create representations equipped with links. There are three types of classes provided out of the box:
Resource - an item resource. Effectively to wrap around some DTO or entity that captures a single item and enriches it with links.
Resources - a collection resource, that can be a collection of somethings but usually are a collection of Resource instances.
PagedResources - an extension of Resources that captures additional pagination information like the number of total pages etc.
All of these classes derive from ResourceSupport, which is a basic container for Link instances.
Resource assemblers
A ResourceAssembler is now the mitigating component to convert your domain objects or DTOs into such resource instances. The important part here is, that it turns one source object into one target object.
So the PagedResourcesAssembler will take a Spring Data Page instance and transform it into a PagedResources instance by evaluating the Page and creating the necessary PageMetadata as well as the prev and next links to navigate the pages. By default - and this is probably the interesting part here - it will use a plain SimplePagedResourceAssembler (an inner class of PRA) to transform the individual elements of the page into nested Resource instances.
To allow to customize this, PRA has additional toResource(…) methods that take a delegate ResourceAssembler to process the individual items. So you end up with something like this:
class UserResource extends ResourceSupport { … }
class UserResourceAssembler extends ResourceAssemblerSupport<User, UserResource> { … }
And the client code now looking something like this:
PagedResourcesAssembler<User> parAssembler = … // obtain via DI
UserResourceAssembler userResourceAssembler = … // obtain via DI
Page<User> users = userRepository.findAll(new PageRequest(0, 10));
// Tell PAR to use the user assembler for individual items.
PagedResources<UserResource> pagedUserResource = parAssembler.toResource(
users, userResourceAssembler);
Outlook
As of the upcoming Spring Data Commons 1.7 RC1 (and Spring HATEOAS 0.9 transitively) the prev and next links will be generated as RFC6540 compliant URI templates to expose the pagination request parameters configured in the HandlerMethodArgumentResolvers for Pageable and Sort.
The configuration you've shown above can be simplified by annotating the config class with #EnableSpringDataWebSupport which would let you get rid off all the explicit bean declarations.
I wanted to convert list of Resources to page. but when giving it PagedResourcesAssembler it was eating up the internal links.
This will get your List paged.
public class JobExecutionInfoResource extends ResourceSupport {
private final JobExecutionInfo jobExecution;
public JobExecutionInfoResource(final JobExecutionInfo jobExecution) {
this.jobExecution = jobExecution;
add(ControllerLinkBuilder.linkTo(methodOn(JobsMonitorController.class).get(jobExecution.getId())).withSelfRel()); // add your own links.
}
public JobExecutionInfo getJobExecution() {
return jobExecution;
}
}
Paged resource Providing ResourceAssembler telling Paged resource to use it, which does nothing simply return's it back as it is already a resource list that is passed.
private final PagedResourcesAssembler<JobExecutionInfoResource> jobExecutionInfoResourcePagedResourcesAssembler;
public static final PageRequest DEFAULT_PAGE_REQUEST = new PageRequest(0, 20);
public static final ResourceAssembler<JobExecutionInfoResource, JobExecutionInfoResource> SIMPLE_ASSEMBLER = entity -> entity;
#GetMapping("/{clientCode}/{propertyCode}/summary")
public PagedResources<JobExecutionInfoResource> getJobsSummary(#PathVariable String clientCode, #PathVariable String propertyCode,
#RequestParam(required = false) String exitStatus,
#RequestParam(required = false) String jobName,
Pageable pageRequest) {
List<JobExecutionInfoResource> listOfResources = // your code to generate the list of resource;
int totalCount = 10// some code to get total count;
Link selfLink = linkTo(methodOn(JobsMonitorController.class).getJobsSummary(clientCode, propertyCode, exitStatus, jobName, DEFAULT_PAGE_REQUEST)).withSelfRel();
Page<JobExecutionInfoResource> page = new PageImpl<>(jobExecutions, pageRequest, totalCount);
return jobExecutionInfoResourcePagedResourcesAssembler.toResource(page, SIMPLE_ASSEMBLER, selfLink);
}
ALTERNATIVE WAY
Another way is use the Range HTTP header (read more in RFC 7233). You can define HTTP header this way:
Range: resources=20-41
That means, you want to get resource from 20 to 41 (including). This way allows consuments of API receive exactly defined resources.
It is just alternative way. Range is often used with another units (like bytes etc.)
RECOMMENDED WAY
If you wanna work with pagination and have really applicable API (hypermedia / HATEOAS included) then I recommend add Page and PageSize to your URL. Example:
http://host.loc/articles?Page=1&PageSize=20
Then, you can read this data in your BaseApiController and create some QueryFilter object in all your requests:
{
var requestHelper = new RequestHelper(Request);
int page = requestHelper.GetValueFromQueryString<int>("page");
int pageSize = requestHelper.GetValueFromQueryString<int>("pagesize");
var filter = new QueryFilter
{
Page = page != 0 ? page : DefaultPageNumber,
PageSize = pageSize != 0 ? pageSize : DefaultPageSize
};
return filter;
}
Your api should returns some special collection with information about number of items.
public class ApiCollection<T>
{
public ApiCollection()
{
Data = new List<T>();
}
public ApiCollection(int? totalItems, int? totalPages)
{
Data = new List<T>();
TotalItems = totalItems;
TotalPages = totalPages;
}
public IEnumerable<T> Data { get; set; }
public int? TotalItems { get; set; }
public int? TotalPages { get; set; }
}
Your model classes can inherit some class with pagination support:
public abstract class ApiEntity
{
public List<ApiLink> Links { get; set; }
}
public class ApiLink
{
public ApiLink(string rel, string href)
{
Rel = rel;
Href = href;
}
public string Href { get; set; }
public string Rel { get; set; }
}
I have the following class:
public static class TestSomething {
Integer test;
public TestSomething(Integer test) {
this.test = test;
}
// getter and setter for test
}
Ok, now create a collection of this class and serialize it with Gson:
Collection<TestSomething> tests = Arrays.asList(
new TestSomething(1),
new TestSomething(2),
new TestSomething(3)
);
String json = new Gson().toJson(tests, new TypeToken<Collection<TestSomething>>() {}.getType());
After this, the String json is set to
[{"test":1},{"test":2},{"test":3}]
Which is great.
But now, all of my model classes inherit from a generic type Identifiable<T> which provides just two methods T getId() and void setId(T). So I change the TestSomething-class from above to
public static class TestSomething extends Identifiable<Long> {
// same as above
}
When I try to put this through Gson.toJson(), Gson ends up with the following Exception:
java.lang.UnsupportedOperationException: Expecting parameterized type, got class path.to.TestSomething.
Are you missing the use of TypeToken idiom?
See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
at com.google.gson.TypeInfoFactory.getActualType(TypeInfoFactory.java:97)
...
So, what do I have to do to get this work?
I don't know the answer, but I know that generic type resolution is a tricky thing to get right: specifically full type resolution from interface with type parameter T up through to generic parameter declaration (T=Long). In these cases it is not enough to check for Method object's parameters but also resolve generic type parameters. This is most likely what causes issues; it may be a bug in Gson.
Since you are serializing things, perhaps you could just omit any type declarations? Although your TypeToken is correct for the use case, maybe it confuses Gson.
But just in case you could not make Gson work with this, I know that of other JSON libraries Jackson can handle such cases correctly.
Perhaps this issue was resolved in one of the Gson releases newer than what the original questioner was using, because the example in the original question now serializes as expected.
// output:
// [{"test":1},{"test":2},{"test":3}]
import java.util.Arrays;
import java.util.Collection;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class Foo
{
public static void main(String[] args)
{
Collection<TestSomething> tests = Arrays.asList(
new TestSomething(1),
new TestSomething(2),
new TestSomething(3));
String json = new Gson().toJson(tests, new TypeToken<Collection<TestSomething>>() {}.getType());
System.out.println(json);
}
}
class TestSomething extends Identifiable<Long>
{
Integer test;
public TestSomething(Integer test)
{
this.test = test;
}
#Override
Long getId()
{
return new Long(test);
}
#Override
void setId(Long t)
{
this.test = (int)(t.longValue());
}
}
abstract class Identifiable<T>
{
abstract T getId();
abstract void setId(T t);
}