Custom properties in jackson schema using multiple SchemaFactoryWrapper instances - java

I am using ValdationSchemaFactoryWarapper class to generate validation properties in JSON schema and I need to add my own custom properties to the schema too. If I develop my custom SchemaFactoryWrapper, I can only use one or the other but not the two at the same time.
I was able to achieve what I wanted by extending ValidationSchemaFactoryWrapper by using the below code, however, I was wondering whether there is perhaps a simpler way to do it, i.e. to use two separate instances of SchemaFactoryWrapper instances.
public class ConfigSchemaExtension {
public static class ConfigSchemaFactoryWrapper extends ValidationSchemaFactoryWrapper {
private static class SchemaFactoryWrapperFactory extends WrapperFactory {
private SchemaFactoryWrapperFactory() {
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
SchemaFactoryWrapper wrapper = new ConfigSchemaFactoryWrapper();
wrapper.setProvider(p);
return wrapper;
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
SchemaFactoryWrapper wrapper = new ConfigSchemaFactoryWrapper();
wrapper.setProvider(p);
wrapper.setVisitorContext(rvc);
return wrapper;
}
}
public ConfigSchemaFactoryWrapper() {
super(new AnnotationConstraintResolver());
schemaProvider = new ConfigJsonSchemaFactory();
visitorFactory = new FormatVisitorFactory(new SchemaFactoryWrapperFactory());
}
}
public static class ConfigJsonSchemaFactory extends JsonSchemaFactory {
#Override
public com.fasterxml.jackson.module.jsonSchema.types.StringSchema stringSchema() {
return new ConfigStringSchema();
}
}
public static class ConfigStringSchema extends StringSchema {
#JsonProperty
private String myLink;
#Override
public void enrichWithBeanProperty(BeanProperty beanProperty) {
super.enrichWithBeanProperty(beanProperty);
Reference ref = beanProperty.getAnnotation(Reference.class);
if(ref != null) this.myLink = ref.value();
}
}
#Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#JacksonAnnotation
public #interface Reference
{
String value() default "";
}
}```

Related

Using a constraint with a parmeterized constructor in Spring Boot application

I have a Spring Boot application where an interface has a constraint:
#Constraint(
validatedBy = {
MyValidator.class
})
public #interface MyInterface {
...
}
I'm using Togglz to enable/disable some features and one class where I want to implement some Togglz code is in MyValidator.
public class MyValidator
implements MyInterface<
MyInterface, TDLDetails> {
private FeatureManager featureManager;
public static final Feature FEATURE_ONE =
new NamedFeature("FEATURE_ONE ");
public MyValidator(FeatureManager featureManager) {
this.featureManager = featureManager;
}
#Override
public void initialize(MyInterface arg0) {}
#Override
public boolean isValid(TDLDetails tdlDetails, ConstraintValidatorContext ctx)
{
if (!featureManager.isActive(FEATURE_ONE)) {
if (tdlDetails.getType().equals(TDLType.ANA)) {
return (tdlDetails.getPlaceOfIssue() != null);
}
}
return true;
}
}
Am I wrong to have the parameterized constructor? It seems I need it for Togglz but I'm not sure how it should be used by #Constraint if it takes a parameter. What's the correct way to do this?

Guice MapBinder with list values

I have a service which needs injection of mulitmap - Map<String, List<Enricher>>
public class EnrichService {
private Map<String, List<Enricher>> typeEnrichers;
#Inject
public EnrichService(Map<String, List<Enricher>> typeEnrichers) {
this.typeEnrichers = typeEnrichers;
}
public void enrich(Entity entity) {
List<Enricher> enrichers = typeEnrichers.get(entity.type);
//.. enriching entity with enrichers
}
}
class Entity {
String id;
String type = "shapedColorful";
String color;
String shape;
}
interface Enricher {
void enrich(Entity entity);
}
class ColorEnricher implements Enricher {
#Inject
private ColorService colorService;
public void enrich(Entity entity) {
entity.color = colorService.getColor(entity.id);
}
}
class ShapeEnricher implements Enricher {
#Inject
private ShapeService shapeService;
public void enrich(Entity entity) {
entity.shape = shapeService.getShape(entity.id);
}
}
I need help with configuring typeEnrichers binder in juice
Here is what I'm trying, but stuck
bind(ColorService).to(ColorServiceImpl.class);
bind(ShapeService).to(ShapeServiceImpl.class);
MapBinder<RelationType, List<Enricher>> mapBinder = MapBinder.newMapBinder(
binder(),
new TypeLiteral<String>() {},
new TypeLiteral<List<Enricher>>() {});
mapBinder.addBinding("shapedColorful", to(/*how to bind list of Enrichers here??*/))
Any help, how I can bind such multimap?
You are trying mix together MapBinder with Multibinder.
I would suggest you to create a Provider for each MapBinder relation. Actually Multibinder is a List Provider itself, to be specific its RealMultibinder implementation unfortunatelly is package private and forbidden from use. If it would not be package private maybe we could use it this way. Most likely it would not work anyway... Imho, it would be nice.
bind(ColorService).to(ColorServiceImpl.class);
bind(ShapeService).to(ShapeServiceImpl.class);
MapBinder<RelationType, List<Enricher>> mapBinder = MapBinder.newMapBinder(
binder(),
new TypeLiteral<String>() {},
new TypeLiteral<List<Enricher>>() {});
mapBinder.addBinding("shapedColorful", toProvider(Multibinder.newSetBinder(this.binder(), Enricher.class).addBinding().to(ColorService.class).addBinding().to(ShapeService.class).asEagerSingleton()))
You can still create a provider and use it:
public class ShapeColorfulProvider implements Provider<List<Enricher>> {
#Inject private ColorService colorService;
#Inject private ShapeService shapeService;
public List<Enricher> get() {
return Lists.newArrayList(colorService,shapeService);
}
}
then
mapBinder.addBinding("shapedColorful", toProvider(ShapeColorfulProvider.class))

Export an object with Spring JMX annotations

I'm trying to export some information with Spring JMX annotation driven (I have no xml :D). I have achieved to export Strings and Integer types but I haven't been able to export any object of a class I created.
This is my #Configuration class.
#Configuration
#EnableMBeanExport
#ComponentScan({"my.packages"})
public class AppManager {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppManager.class);
context.refresh();
try {
TimeUnit.MINUTES.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Bean(name = "jmxExporter")
public Exporter jmxExporter() {
return new Exporter();
}
}
This is some class I have with some attributes I want to get.
public class MyClass implements Serializable {
private int param1;
private int param2;
private int param3;
public MyClass() {
// calculate all params
}
public int getParam1() {
return this.param1;
}
public int getParam2() {
return this.param2;
}
public int getParam3() {
return this.param3;
}
}
This is the class that exports it's attributes.
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
#ManagedResource(objectName = "my.packages:type=JMX,name=Exporter")
public class Exporter {
#ManagedAttribute
public String getString() { //This works!
return "Test string";
}
#ManagedAttribute
public MyClass getMyClass() { //This does not work
return new MyClass();
}
}
I need to create MyClass object every time because it has some real-time information that I can't export separately.
In JConsole the value of attribute is "Unavailable".
I'm pretty new to JMX and obviously missing something.
Thank you for your help!
I resolved it by returning CompositeData.
#ManagedAttribute
public CompositeData getMyClass() {
return createCompositeDataForMyClass();
}
I built a CompositeDataSupport for that and it worked.
return new CompositeDataSupport(compositeType, itemNames, itemValues);
Where compositeType is a CompositeType, itemNames is a String[] and itemValues is an Object[].
The CompositeType can be built with something like this
new CompositeType(typeName, typeDescription, itemNames, itemDescriptions, itemTypes);
typeName, typeDescription are Strings. itemNames and itemDescriptions are String[]. itemTypes is an OpenType[]. SimpleType and CompositeType can be used to build OpenType.
All this objects must be imported with
import javax.management.openmbean.*;

Using #Produces with #Qualifier

Is there any way to force the use of producer method if using #Qualifier? I have a #Produces factory method but the constructor is still being called, which is an issue because I need the InjectionPoint to read parameters. Using the code below RRRRRRRR is always printed out.
#ProductTypeA
public class ProductA implements Product
{
public String test="testA";
private ProductA()
{
System.out.println("RRRRRRRRRRRRRRRRRRRR");
this.test = "testB";
}
private ProductA(InjectionPoint injectionpoint)
{
System.out.println("TTTTTTTTTTTTTTTT");
this.test="testC";
}
#Produces
public ProductA getProductA(InjectionPoint injectionpoint)
{
this.test="testD";
System.out.println("-----------------------------");
System.out.println("injectionpoint.getAnnotated() = "+injectionpoint.getAnnotated());
return new ProductA(injectionpoint);
}
#Override
public LinkedList<Feature> getFeatures()
{
LinkedList<Feature> rtn = new LinkedList<Feature>();
rtn.add( new Feature("AAA","111") );
return rtn;
}
#Override
public String toString()
{
return "ProductA []";
}
}
Qualifier:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface ProductTypeA
{
#Nonbinding
String testfield() default "23";
}
The Qualifier should be in the producer method:
#Produces
#ProductTypeA
public ProductA getProductA(InjectionPoint injectionpoint)
{
this.test="testD";
System.out.println("-----------------------------");
System.out.println("injectionpoint.getAnnotated() = "+injectionpoint.getAnnotated());
return new ProductA(injectionpoint);
}

Polymorphism in XStream serialization and deserialization

I have these classes:
#XStreamAlias("person")
public class PersonConfig {
private AnimalConfig animalConfig;
}
public interface AnimalConfig {}
#XStreamAlias("dog");
public class DogConfig extend AnimalConfig {}
#XStreamAlias("cat");
public class CatConfig extend AnimalConfig {}
And I would like to be able to deserialize this xml with the classes above:
<person>
<dog/>
<person>
As well as deserialize this xml too, with the same classes:
<person>
<cat/>
<person>
So that in both cases, the PersonConfig's field animalConfig is filled. In the first XML with a DogConfig instance and in the second XML with a CatConfig instance.
Is this possible by adding some annotation to make this work?
It seems XStream does not allow you to do it easily.
Your question is similar to this one, asking for managing something like a xsd:choice with XStream.
If you don't necessarily need to use XStream, JAXB will allow you to do it easily :
#XmlRootElement(name="person")
public class PersonConfig {
private AnimalConfig animalConfig;
#XmlElementRefs({
#XmlElementRef(name="cat", type=CatConfig.class),
#XmlElementRef(name="dog", type=DogConfig.class)
})
public AnimalConfig getAnimalConfig() {
return animalConfig;
}
public void setAnimalConfig(AnimalConfig animalConfig) {
this.animalConfig = animalConfig;
}
}
After some researches, listing all available classes for your property can be avoided if you choose to use the XmlAdapter.
In Blaise Doughan link, the example uses an abstract class, not an interface.
Edit :
As Blaise Doughan said in its comment, #XmlElementRef is better suited for this purpose. Code has been updated accordingly.
You can write a converter.
public class CustomConverter implements Converter {
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
// TODO: Get annotation value from object 'source' with name of tag via Reflection.
// Or add a method to the AnimalConfig interface giving you tag name to put to serialization output.
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
// TODO: use reflection to create animal object based on what you xml tag you have at hahd.
return context.convertAnother(context.currentObject(), SomeAnimalClazz.class);
}
public boolean canConvert(Class type) {
return type.equals(AnimalConfig.class);
}
}
There's a disadvantage: polymorphism will require you to use Java Reflection API and performance degradation.
This is quite easy. You just have to do it right and not like my previous speakers. When you process the annotations, XStream can assign those classes.
#XStreamAlias("person")
public class PersonConfig {
private AnimalConfig animalConfig;
public String toXml() {
XStream xstream = new XStream();
xstream.processAnnotations(DogConfig.class);
xstream.processAnnotations(CatConfig.class);
return xstream.toXML(this);
}
}
public interface AnimalConfig {}
#XStreamAlias("dog");
public class DogConfig implements AnimalConfig {}
#XStreamAlias("cat");
public class CatConfig implements AnimalConfig {}
It works out of the box, with out any annotations...
private static interface Test {
String getName();
Params getParams();
}
private static interface Params {
}
private static class OneParams implements Params {
private String oneValue;
public String getOneValue() {
return oneValue;
}
public void setOneValue(String oneValue) {
this.oneValue = oneValue;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OneParams [oneValue=");
builder.append(oneValue);
builder.append("]");
return builder.toString();
}
}
private static class TwoParams implements Params {
private String twoValue;
public String getTwoValue() {
return twoValue;
}
public void setTwoValue(String twoValue) {
this.twoValue = twoValue;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("TwoParams [twoValue=");
builder.append(twoValue);
builder.append("]");
return builder.toString();
}
}
private static class OneTest implements Test {
private String name;
private Params params;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public Params getParams() {
return params;
}
public void setParams(Params params) {
this.params = params;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OneTest [name=");
builder.append(name);
builder.append(", params=");
builder.append(params);
builder.append("]");
return builder.toString();
}
}
---- now deserialize like this...
System.out
.println(ser
.deserialize("<XStreamTest_-OneTest><name>OneTest</name><params class=\"XStreamTest$OneParams\"><oneValue>1</oneValue></params></XStreamTest_-OneTest>"));
System.out
.println(ser
.deserialize("<XStreamTest_-OneTest><name>TwoTest</name><params class=\"XStreamTest$TwoParams\"><twoValue>2</twoValue></params></XStreamTest_-OneTest>"));

Categories