Get parent json string inside DeserializationProblemHandler (Jackson) - java

I'm working with a remote API which can change and I have no control over it.
This is why I have written myself a small jackson module that has a DeserializationProblemHandler implementation which logs those "unknown properties".
This works just fine, but I want to make my life easier and log the actual received JSON in the logs as well.
So having the following JSON example:
{ "myUnknownProperty": "value123", "myIgnoredProperty": true, "myKnownProperty": true }
A log should look something similar to this:
WARN: Found unexpected property while parsing json: /myUnknownProperty (type: com.foo.PartiallyIgnoredPOJO); JSON { "myUnknownProperty": "value123", "myIgnoredProperty": true, "myKnownProperty": true }
Here is the test case I currently have:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.IOException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class ExampleTest
{
// Test
#Test
public void mockTest() throws JsonProcessingException
{
Logger logger = mock(Logger.class);
try
{
new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.registerModule(new UnknownPropertyModule(logger))
.readValue(partiallyIgnoredSONSample, PartiallyIgnoredPOJO.class);
}
finally
{
verify(logger).warn(UnknownPropertyModule.MESSAGE,
"/myUnknownProperty",
PartiallyIgnoredPOJO.class,
partiallyIgnoredSONSample);
}
}
// Implementations
private static class UnknownPropertyDeserializationProblemHandler extends DeserializationProblemHandler
{
private final Logger logger;
private final String format;
UnknownPropertyDeserializationProblemHandler(final Logger logger, final String format)
{
this.logger = logger;
this.format = format;
}
#Override
public boolean handleUnknownProperty(final DeserializationContext context,
final JsonParser parser,
final JsonDeserializer<?> deserializer,
final Object beanOrClass,
final String propertyName) throws IOException
{
final Class<?> type = beanOrClass.getClass();
String jsonPath = parser.getParsingContext().pathAsPointer().toString();
String json = "__How_to_get_the_entire_JSON_from_the_root___";
logger.warn(format, jsonPath, type, json);
return false;
}
}
private static class UnknownPropertyModule extends Module
{
public static final String MESSAGE = "Found unexpected property while parsing json: {} (type: {}); JSON: {}";
private final Logger logger;
public UnknownPropertyModule(#Nullable final Logger logger)
{
this.logger = logger != null ? logger : LoggerFactory.getLogger(getClass().getName());
}
#Override
public String getModuleName()
{
return getClass().getSimpleName();
}
#Override
public Version version()
{
return Version.unknownVersion();
}
#Override
public void setupModule(Module.SetupContext context)
{
context.addDeserializationProblemHandler(new UnknownPropertyDeserializationProblemHandler(logger, MESSAGE));
}
}
// Example json & mapping.
private static final String partiallyIgnoredSONSample = "{ \"myUnknownProperty\": \"value123\", \"myIgnoredProperty\": true, \"myKnownProperty\": true }";
#JsonIgnoreProperties("myIgnoredProperty")
private static class PartiallyIgnoredPOJO
{
private final boolean myKnownProperty;
#JsonCreator
public PartiallyIgnoredPOJO(#JsonProperty("myKnownProperty") boolean myKnownProperty)
{
this.myKnownProperty = myKnownProperty;
}
public boolean getMyKnownProperty()
{
return myKnownProperty;
}
}
}
From what I've seen in the debugger, you could use parser.getParsingContext().getParent() to get to the json root, but I'm unsure how to get the actual buffered data. Any ideas?

Related

How does a Micronaut controller determine its base URL

For example if I have the following controller:
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
#Controller("/test")
public class TestController {
#Get()
#Produces(MediaType.TEXT_PLAIN)
public String index() {
// How should this be implemented?
return "???";
}
}
and I run it on my-server, then I would like the index method to return http://my-server:8080.
Asof Micronaut V1.2.0, you can use the HttpHostResolver interface, for example:
import io.micronaut.http.*;
import io.micronaut.http.annotation.*;
import io.micronaut.http.server.util.HttpHostResolver;
import io.micronaut.web.router.RouteBuilder;
#Controller("/test")
public class TestController {
private final HttpHostResolver httpHostResolver;
private final RouteBuilder.UriNamingStrategy uriNamingStrategy;
public TestController(
HttpHostResolver httpHostResolver,
RouteBuilder.UriNamingStrategy uriNamingStrategy
) {
this.httpHostResolver = httpHostResolver;
this.uriNamingStrategy = uriNamingStrategy;
}
#Get()
#Produces(MediaType.TEXT_PLAIN)
public String index(HttpRequest httpRequest) {
return httpHostResolver.resolve(httpRequest) +
uriNamingStrategy.resolveUri(TestController.class);
}
}
This seems to work:
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.web.router.RouteBuilder;
import java.net.*;
#Controller("/test")
public class TestController {
protected final String baseUrl;
public TestController(EmbeddedServer embeddedServer, RouteBuilder.UriNamingStrategy uns)
throws MalformedURLException {
final String host = embeddedServer.getHost();
final int port = embeddedServer.getPort();
final String file = uns.resolveUri(TestController.class);
baseUrl = new URL("http", host, port, file).toString();
}
#Get()
#Produces(MediaType.TEXT_PLAIN)
public String index() {
return baseUrl;
}
}
I'm not sure whether it's idiomatic, or whether it works in all cases. If someone posts a better answer I'll accept that.
If you want the controller to respond at / then use #Controller("/") instead of #Controller("/test").
package com.tech.api;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.PathVariable;
import java.util.List;
import javax.inject.Inject;
#Controller("/")
public class ModelDefinitionsApi {
#Get(uri="/modelName", produces = MediaType.APPLICATION_JSON)
public String getModel(#PathVariable String modelName) {
return "modelName";
}
}
http://my-server:8080 => main controller url
http://my-server:8080/modelName => for getModel method

#Valid when creating objects with jackson without controller

I have a model that I validate with #Valid in my Controllers when requests are send from the front-end:
#NotNull
#Size(min=1, message="Name should be at least 1 character.")
private String name;
#NotNull
#Pattern(regexp = "^https://github.com/.+/.+$", message = "Link to github should match https://github.com/USER/REPOSITORY")
private String github;
but now I am also creating a object with Jackson's ObjectMapper without the controller. Is there a way to register this validation in the ObjectMapper or should I just check the variables in the setters?
You can extend BeanDeserializer and validate object after deserialisation. To register this bean use SimpleModule.
Simple bean deserialiser with validation:
class BeanValidationDeserializer extends BeanDeserializer {
private final static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private final Validator validator = factory.getValidator();
public BeanValidationDeserializer(BeanDeserializerBase src) {
super(src);
}
#Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Object instance = super.deserialize(p, ctxt);
validate(instance);
return instance;
}
private void validate(Object instance) {
Set<ConstraintViolation<Object>> violations = validator.validate(instance);
if (violations.size() > 0) {
StringBuilder msg = new StringBuilder();
msg.append("JSON object is not valid. Reasons (").append(violations.size()).append("): ");
for (ConstraintViolation<Object> violation : violations) {
msg.append(violation.getMessage()).append(", ");
}
throw new ConstraintViolationException(msg.toString(), violations);
}
}
}
We can use it as below:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.File;
import java.io.IOException;
import java.util.Set;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
SimpleModule validationModule = new SimpleModule();
validationModule.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (deserializer instanceof BeanDeserializer) {
return new BeanValidationDeserializer((BeanDeserializer) deserializer);
}
return deserializer;
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(validationModule);
System.out.println(mapper.readValue(jsonFile, Pojo.class));
}
}
class Pojo {
#NotNull
#Size(min = 1, message = "Name should be at least 1 character.")
private String name;
#NotNull
#Pattern(regexp = "^https://github.com/.+/.+$", message = "Link to github should match https://github.com/USER/REPOSITORY")
private String github;
// getters, setters, toString()
}
For valid JSON payload:
{
"name": "Jackson",
"github": "https://github.com/FasterXML/jackson-databind"
}
prints:
Pojo{name='Jackson', github='https://github.com/FasterXML/jackson-databind'}
For invalid JSON payload:
{
"name": "",
"github": "https://git-hub.com/FasterXML/jackson-databind"
}
prints:
Exception in thread "main" javax.validation.ConstraintViolationException: JSON object is not valid. Reasons (2): Name should be at least 1 character., Link to github should match https://github.com/USER/REPOSITORY,
at BeanValidationDeserializer.validate(JsonApp.java:110)
at BeanValidationDeserializer.deserialize(JsonApp.java:97)
See also:
Java Bean Validation Basics
Deserialize to String or Object using Jackson
Jackson custom serialization and deserialization
I will also post how I managed to do it. Creating class that implements validator:
public class UserValidator implements Validator {
private static final int MINIMUM_NAME_LENGTH = 6;
#Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "Name must be at least 7 characters long.");
User foo = (User) target;
if(foo.getGithub().length() > 0 && !extensionSpec.getGithub().matches("^(www|http:|https:)+//github.com/.+/.+$")){
errors.rejectValue("github", "Github must match http://github.com/:user/:repo");
}
if (errors.getFieldErrorCount("name") == 0 && foo.getName().trim().length() < MINIMUM_NAME_LENGTH) {
errors.rejectValue("name", "Name must be at least 7 characters");
}
}
}
Then creating databinder with the deserialized object, taking the binding result and then validating the object:
ObjectMapper mapper = new ObjectMapper();
User foo = mapper.readValue(FooJson, User.class);
Validator validator = new ObjectValidator();
BindingResult bindingResult = new DataBinder(foo).getBindingResult();
validator.validate(foo, bindingResult);
if(bindingResult.hasErrors()){
throw new BindException(bindingResult);
}
Also if you want to take the errorCodes in the body of the response:
#ExceptionHandler
ResponseEntity handleBindException(BindException e){
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getBindingResult().getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getCode)
.toArray());
}

Mocked EhCache NullPointerException in JUnit 5 test

I am writing Unit tests for a service I want to test. Several methods try to retrieve values from an EhCache.
I tried mocking them with Mockito and simply have the get(String key) method of Cache return null, since I want to ignore the caching for these tests.
My test class:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.jysk.dbl.esldataservice.model.Preis;
import com.jysk.dbl.esldataservice.service.PreisService;
import com.jysk.dbl.esldataservice.service.external.PimDataService;
import com.jysk.dbl.esldataservice.service.external.SapCarService;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
public class PreisServiceTest {
#Mock
private SapCarService sapCarService;
#Mock
private ArticleDataService articleDataService;
#Mock
private CacheManager cacheManager;
#Mock
private Cache cache;
#InjectMocks
#Resource
private PreisService preisService;
#BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
when(this.cacheManager.getCache(anyString())).thenReturn(this.cache);
when(this.cache.get(anyString())).then(null);
}
protected static final String TEST_STORE_IDENTIFIER = "1234";
private static final String ARTICLE_IDENTIFIER_1 = "12345001";
private static final String ARTICLE_IDENTIFIER_2 = "54321001";
private final Preis p1 = new Preis(ARTICLE_IDENTIFIER_1, 10.00, 15.00, "01", "01", "01");
private final Preis p2 = new Preis(ARTICLE_IDENTIFIER_2, 20.00, 25.00, "02", "02", "02");
#Test
void testGetPreisReturnsOneCorrectPreis() {
when(this.sapCarService.getPreise(Arrays.asList(ARTICLE_IDENTIFIER_1), TEST_STORE_IDENTIFIER, true)).thenReturn(Arrays.asList(this.p1));
final List<Preis> actual = this.preisService.getPreis(ARTICLE_IDENTIFIER_1, TEST_STORE_IDENTIFIER);
verify(this.sapCarService, times(1)).getPreise(anyList(), anyString(), anyBoolean());
assertNotNull(actual);
assertEquals(1, actual.size());
assertEquals(this.p1, actual);
}
}
My implementation:
private Preis searchPreisInCache(String key) {
final Element preisOptional = this.cacheManager.getCache("preis").get(key); // NPE here
if (preisOptional != null) {
final Preis preis = (Preis) preisOptional.getObjectValue();
logger.info(String.format("Preis with key '%s' found in cache 'preis'.", key));
return preis;
}
return null;
}
The stackTrace showed, that the NPE gets thrown inside the net.sf.ehcache.Cache class:
public final Element get(Object key) throws IllegalStateException, CacheException {
getObserver.begin(); // NPE thrown here
checkStatus();
if (disabled) {
getObserver.end(GetOutcome.MISS_NOT_FOUND);
return null;
}
Element element = compoundStore.get(key);
if (element == null) {
getObserver.end(GetOutcome.MISS_NOT_FOUND);
return null;
} else if (isExpired(element)) {
tryRemoveImmediately(key, true);
getObserver.end(GetOutcome.MISS_EXPIRED);
return null;
} else if (!skipUpdateAccessStatistics(element)) {
element.updateAccessStatistics();
}
getObserver.end(GetOutcome.HIT);
return element;
}
Is there any easy solution for this problem, if I simply want the Cache to return null, whenever it's called?
Mockito can't mock final methods and classes without some configuration. As Morfic pointed out, it is posible with Mockito v2.x, like explained here and here.
Basically, you have to add a text file named org.mockito.plugins.MockMaker under the directory src/test/resources/mockito-extensions with the content mock-maker-inline and tada, Mockito can mock final methods and classes.
However, this uses a different engine with different limitations, so be aware of that.

Jackson ignore property of object in list property

One can ignore a property of a class using #JsonIgnorePropertiesas a class level annotation. But let's say you have the following two classes;
class Client {
#JsonIgnoreProperties ("owner")
List<Vehicle> vehicles;
}
Class Vehicle {
#JsonIgnoreProperties ("vehicles")
Client owner;
}
I thought this would work. But apparently The JsonIgnoreProperties doesn't work on the list. I imagine it checks java.util.List for property òwner`but it doesn't find it.
What i want is a filter based on which object is retrieved first. If the Vehicle was retrieved first, show the owner but don't show the vehicles that Client has. And if the Clientis retrieved first, show vehicles but don't show each Vehicle's owner. This was too avoid the unending recursion.
My question is how can i ignore the property owner in the objects of Vehicle in the list vehicles in Client?
EDIT
Using #Kushan answer.
#JsonIgnoreProperties ("owner")
Class Vehicle {
Client owner;
}
This will always ignore the owner information. What I'm intending to get is if I have Clientobject and i'm getting it's vehicles, I don't need the resulting owner information of the Vehiclesince i already have the Clientobject. But if i have a Vehicleobject to begin with, I will need it's owner property excluding all the vehicles that Client has.
I'm not sure you are said,so I described all possible features to filter data when the data serializing in the Tests/Examples section.for examples:#JsonIgnore,#JsonIgnoreProperties,#JsonIgnoreType,#JsonView,#JsonFilter and serializing data by mixin/rootType.You could choose one which you like.
Tests/Examples
package com.holi.jackson;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.jayway.jsonassert.JsonAssert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static com.fasterxml.jackson.annotation.PropertyAccessor.FIELD;
import static com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS;
import static com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter.serializeAllExcept;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Created by holi on 3/20/17.
*/
public class Jackson2SerializingFilteringTest {
private final User bob = new User("bob", "123456", "bob#example.com");
private final ObjectMapper jackson2 = new ObjectMapper();
#BeforeEach
void configure() {
jackson2.registerModules(configuration());
//write {} for empty bean
jackson2.disable(FAIL_ON_EMPTY_BEANS);
jackson2.setFilterProvider(serializeAll());
jackson2.setVisibility(FIELD, Visibility.ANY);
}
private Module configuration() {
return new SimpleModule() {
#Override
public void setupModule(SetupContext context) {
context.insertAnnotationIntrospector(filterIdAsClassName());
}
};
}
private NopAnnotationIntrospector filterIdAsClassName() {
return new NopAnnotationIntrospector() {
#Override
public Object findFilterId(Annotated ann) {
return ann.getRawType().getName();
}
#Override
public Version version() {
return super.version();
}
};
}
private SimpleFilterProvider serializeAll() {
SimpleFilterProvider filters = new SimpleFilterProvider();
//serialize all if filter not found,rather than throws an exception
filters.setFailOnUnknownId(false);
return filters;
}
#Test
void serializingAll() throws JsonProcessingException {
String json = jackson2.writer().writeValueAsString(bob);
JsonAssert.with(json)
.assertThat("$[*]", hasSize(3))
.assertEquals("name", bob.name)
.assertEquals("password", bob.password)
.assertEquals("mail", bob.mail);
}
#Test
void serializingAllExceptIgnoredProperties() throws JsonProcessingException {
#JsonIgnoreProperties({"name", "version"})
class JSONRoot {
String name = "jackson";
String version = "2.0";
}
JSONRoot root = new JSONRoot();
String json = jackson2.writer().writeValueAsString(root);
assertEquals("{}", json);
}
#Test
void serializingAllExceptIgnoredProperty() throws JsonProcessingException {
class JSONRoot {
String name = "jackson";
#JsonIgnore
String version = "2.0";
}
JSONRoot root = new JSONRoot();
String json = jackson2.writer().writeValueAsString(root);
JsonAssert.with(json)
.assertThat("$[*]", hasSize(1))
.assertEquals("name", root.name);
}
#Test
void serializingAllExceptIgnoredType() throws JsonProcessingException {
#JsonIgnoreType
class Ignored {
}
class JSONRoot {
Ignored ignored = new Ignored();
}
String json = jackson2.writer().writeValueAsString(new JSONRoot());
assertEquals("{}", json);
}
#Test
void serializingMixInAnnotations() throws JsonProcessingException {
jackson2.addMixIn(User.class, ExcludingMail.class);
String json = jackson2.writer().writeValueAsString(bob);
JsonAssert.with(json)
.assertThat("$[*]", hasSize(2))
.assertEquals("name", bob.name)
.assertEquals("password", bob.password);
}
#Test
void serializingUseFiltersToExcludingProperties() throws JsonProcessingException {
String json = jackson2.writer(excludes("password")).writeValueAsString(bob);
JsonAssert.with(json)
.assertThat("$[*]", hasSize(2))
.assertEquals("name", bob.name)
.assertEquals("mail", bob.mail);
}
#Test
void serializingRootTypePropertiesOnly() throws JsonProcessingException {
String json = jackson2.writerFor(Contactable.class).writeValueAsString(bob);
JsonAssert.with(json)
.assertThat("$[*]", hasSize(1))
.assertEquals("mail", bob.mail);
}
#Test
void serializingViewPropertiesOnly() throws JsonProcessingException {
String json = jackson2.writerWithView(Public.class).writeValueAsString(bob);
JsonAssert.with(json)
.assertThat("$[*]", hasSize(2))
.assertEquals("name", bob.name)
.assertEquals("mail", bob.mail);
}
private SimpleFilterProvider excludes(String... propertyNames) {
SimpleFilterProvider filters = serializeAll();
filters.addFilter(User.class.getName(), serializeAllExcept(propertyNames));
return filters;
}
private class User implements Contactable {
final String name;
#JsonView(Privacy.class)
String password;
#JsonView(Public.class)
final String mail;
public User(String name, String password, String mail) {
this.name = name;
this.password = password;
this.mail = mail;
}
public String getMail() {
return mail;
}
}
#interface Public {
}
#interface Privacy {
}
private interface Contactable {
String getMail();
}
#JsonIgnoreProperties("mail")
private class ExcludingMail {
}
}

How to get SlingRequest from saxon TransformerFactory

I want to get a current SlingRequest.getRequestURI() and pass it to new LinkTransformer(links, repository, <<requestedUrl>>). So I tried to add an annotation #Reference SlingRequest slingRequest and it throws me exception. Here is my code:
package com.my.transformer;
import com.my.transformer.impl.LinkTransformer;
import org.apache.felix.scr.annotations.*;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.rewriter.Transformer;
import org.apache.sling.rewriter.TransformerFactory;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.RepositoryException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
#Service(value = TransformerFactory.class)
#Component(immediate = true, metatype = true,
label="My Link Rewriter",
name= "com.my.transformer.LinkTransformerFactory",
description="Rewrite certain links")
public class LinkTransformerFactory implements TransformerFactory {
private static final Logger log = LoggerFactory.getLogger(LinkTransformerFactory.class);
#Property(
value = "mylinktransformer",
propertyPrivate = true)
static final String PIPELINE_TYPE = "pipeline.type";
#Property(unbounded= PropertyUnbounded.ARRAY,description="....")
private static final String LINKS = "links";
private ArrayList<String> links;
#Reference
SlingRepository repository;
#Activate
protected void activate(ComponentContext context)
throws RepositoryException {
final Dictionary<?, ?> properties = context.getProperties();
String[] prop = (String[])properties.get(LINKS);
links = new ArrayList<String>(Arrays.asList(prop));
log.info("LinkTransformerFactory.activate");
}
#Override
public Transformer createTransformer() {
return new LinkTransformer(links, repository);
}
}
Thank you
You cannot obtain it in the TransformerFactory, but you don't need it to give it to the LinkTransformer. The Transformer will receive it in the init method.
The init method of Transformer receives a ProcessingContext, you can use ProcessingContext.getRequest() to get it

Categories

Resources