sling models custom injector header image

Sling Models: How to write Sling Model Injector

Once I needed to write several debugging servlets for some functionality. Those servlets had to accept a big pile of parameters and I was tired of having tens of rows like  String paramValue = request.getParameter(“paramName”). Luckily, I’ve already known Sling Models. So I’ve decided to find the way to inject these parameters into the model, so I could write just request.adaptTo(MyModel.class) . Unfortunately, I found nothing, so decided to write it myself. So in this article, I will show you how to write custom Sling Model injector on the example of Sling Model Request Parameter Injector.

Custom Sling Model Injector

To write it, we need to implement an OSGi service, which inherits from Injector interface.  It will force us to implement 2 methods:

  • getName – returns string, which will be used as logical name for the injector.
  • getValue – returns a value for an injection point.

The last one has several parameters and we will review some of them a bit later.

Sling Model Request Parameter Injector

Let’s imagine, that we have model RequestParamsInjected where we have several fields, which should be populated with request parameters.

package com.taradevko.aem.model;

import com.taradevko.aem.model.injector.annotation.RequestParameter;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;

import java.util.HashMap;
import java.util.Map;

@Model(adaptables = SlingHttpServletRequest.class)
public class RequestParamsInjected {
    private static final Map<Object, String> CONTENT = new HashMap<>();
    static {
        CONTENT.put("param1", "Content 1");
        CONTENT.put(963, "Content 2");
    }

    private String stringParam;

    private Integer integerParam;

    public String getStringContent() {
        return CONTENT.get(stringParam);
    }

    public String getIntegerContent() {
        return CONTENT.get(integerParam);
    }
}

For injecting values into the model fields, we need to annotate them. Actually, we can use existing annotations @Inject and @Source but it’s not flexible at all. We will write our own annotation!

package com.taradevko.aem.model.injector.annotation;

import org.apache.sling.models.annotations.Source;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@InjectAnnotation
@Source("request-parameter")
public @interface RequestParameter {
}

We mark an annotation to be used with fields only, however you can have it for methods and parameters as well if you specify them in @Target. Then we declare our annotation as custom injection annotation. And finally, we specify which logical name to be used with for annotation.

Now we can mark fields with new annotations:

package com.taradevko.aem.model;

...

@Model(adaptables = SlingHttpServletRequest.class)
public class RequestParamsInjected {

    ....

    @RequestParameter
    private String stringParam;

    @RequestParameter
    private Integer integerParam;

    ...
}

Without injector our annotation is useless. Let’s fix it:

package com.taradevko.aem.model.injector;

import com.taradevko.aem.model.injector.annotation.RequestParameter;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
import org.apache.sling.models.spi.Injector;
import org.osgi.framework.Constants;

import javax.servlet.ServletRequest;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;

@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = Integer.MIN_VALUE)
public class RequestParameterInjector implements Injector {
    @Override
    public String getName() {
        return "request-parameter";
    }

    @Override
    public Object getValue(final Object adaptable,
                           final String fieldName,
                           final Type type,
                           final AnnotatedElement annotatedElement,
                           final DisposalCallbackRegistry disposalCallbackRegistry) {

        if (adaptable instanceof ServletRequest) {
            final ServletRequest request = (ServletRequest) adaptable;
            if (type instanceof Class<?>) {
                Class<?> fieldClass = (Class<?>) type;
                return getValue(request, fieldClass, fieldName);
            }
        }
        return null;
    }

    private Object getValue(final ServletRequest request, final Class<?> fieldClass, final String fieldName) {
        String parameterValue = request.getParameter(fieldName);
        if (StringUtils.isBlank(parameterValue)) {
            return null;
        }

        if (fieldClass.equals(Integer.class)) {
            try {
                return Integer.parseInt(parameterValue);
            } catch (NumberFormatException ex) {

                //got exception, so not an integer, return null;
                return null;
            }
        }

        return parameterValue;
    }
}

Earlier we discussed briefly methods we override above. Now it’s time to go through the method getValue:

  • for now we will use first 3 arguments:
    • adaptable – object which Sling tries to adapt from. We expect it to be the request;
    • fieldName – name of the field. which has been annotated;
    • type – the declared type of the injection point;
  • first, we check if adaptable is request and type is Class;
  • then we call method getValue where we get parameter by field name and cast it to the integer if injection field is of type Integer;
  • finally, we return integer, string or null value.

For testing, let’s create simple component:

<pre data-sly-use.model="com.taradevko.aem.model.RequestParamsInjected">
    Your parameter brought next content:
    ${model.stringContent}
    ${model.integerContent}
</pre>

We are ready to test it:

sling model request parameter inject first example

As you can see, both parameters were injected into the fields successfully. But what if one of the parameters is not specified? Our model will not be adapted because both fields are required. Other annotations, like OSGiService, has nice attribute optional which can be used in such cases. Let’s add such feature for our annotation:

package com.taradevko.aem.model.injector.annotation;

...

public @interface RequestParameter {
 boolean optional() default false;
}

And now adapt model as well:

package com.taradevko.aem.model;

...

@Model(adaptables = SlingHttpServletRequest.class)
public class RequestParamsInjected {
...
    @RequestParameter(optional = true)
    private Integer integerParam;
...
}

Why not test it now?

sling model inject optional not work

Oops, does not work. But why?

Custom Inject Annotation Processor

It appears, that to make custom annotation fully working, we need to implement one more interface – InjectAnnotationProcessorFactory (note that in latest versions this interface is marked as deprecated and StaticInjectAnnotationProcessorFactory should be used instead).

Let’s implement it.

package com.taradevko.aem.model.injector;

...

@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = Integer.MIN_VALUE)
public class RequestParameterInjector implements Injector, InjectAnnotationProcessorFactory {
    
    ...

   
    @Override
    public InjectAnnotationProcessor createAnnotationProcessor(final Object adaptable, final AnnotatedElement element) {
        
        // check if the element has the expected annotation
        RequestParameter annotation = element.getAnnotation(RequestParameter.class);
        if (annotation != null) {
            return new RequestParameterAnnotationProcessor(annotation);
        }
        return null;
    }

    private static class RequestParameterAnnotationProcessor extends AbstractInjectAnnotationProcessor {

        private final RequestParameter annotation;

        RequestParameterAnnotationProcessor(RequestParameter annotation) {
            this.annotation = annotation;
        }

        @Override
        public Boolean isOptional() {
            return annotation.optional();
        }
    }
}

There, we create annotation processor, which overrides isOptional and returns corresponding value from the annotation. After this changes, it works:

sling model request parameter optional

Custom Annotation Attribute

Let’s imagine that now we have new requirement – we need to get parameter from the request, but we do not know its name. However, we have a regular expression which we can use to find it.

First, we need to update our annotation – add regexp attribute to it:

package com.taradevko.aem.model.injector.annotation;

...

public @interface RequestParameter {
    boolean optional() default false;
    String regexp() default "";
}

Then we need to update injector:

package com.taradevko.aem.model.injector;

...

public class RequestParameterInjector implements Injector, InjectAnnotationProcessorFactory {

    ...

    @Override
    public Object getValue(final Object adaptable,
                           final String fieldName,
                           final Type type,
                           final AnnotatedElement annotatedElement,
                           final DisposalCallbackRegistry disposalCallbackRegistry) {

        if (adaptable instanceof ServletRequest) {
            final ServletRequest request = (ServletRequest) adaptable;
            String parameterName = null;

            final RequestParameter annotation = annotatedElement.getAnnotation(RequestParameter.class);
            if (annotation != null && StringUtils.isNotBlank(annotation.regexp())) {
                parameterName = findParameterName(request, annotation.regexp());
            }

            if (type instanceof Class<?>) {
                Class<?> fieldClass = (Class<?>) type;
                return getValue(request, fieldClass, StringUtils.defaultString(parameterName, fieldName));
            }
        }
        return null;
    }

    private String findParameterName(final ServletRequest request, final String paramNameRegexp) {
        final Enumeration parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            final String parameterName = (String) parameterNames.nextElement();

            if (parameterName.matches(paramNameRegexp)) {
                return parameterName;
            }
        }

        return null;
    }

    ...
}

There, before we can get value, we need to find parameter’s name if regular expression is provided. To do this, we iterate over all parameters and match them with regexp from our annotation.

And add a new field with specified regular expression:

package com.taradevko.aem.model;

...

@Model(adaptables = SlingHttpServletRequest.class)
public class RequestParamsInjected {
    private static final Map<Object, String> CONTENT = new HashMap<>();
    static {
        CONTENT.put("param1", "Content 1");
        CONTENT.put(963, "Content 2");
        CONTENT.put("regexpparam", "Content for regexp");
    }

    ...

    @RequestParameter(regexp = "\\d\\w{2}\\d", optional = true)
    private String regexpParam;

    ...

    public String getRegexpContent() {
        return CONTENT.get(regexpParam);
    }
}

Finally, let’s adapt our test component:

<pre data-sly-use.model="com.taradevko.aem.model.RequestParamsInjected">
    Your parameter brought next content:
    string: ${model.stringContent}
    integer: ${model.integerContent}
    regexp: ${model.regexpContent}
</pre>

Test it? Test it!

sling models request parameter regular expression

Great, it works as expected.

Conclusion:

Even though we’ve implemented pretty basic Request Parameter injector, it can give you the insight of how to write more advanced version. Being able to write custom injectors gives nice tool for making your logic cleaner and more maintainable.

 

P.S.: will be happy to get feedback and questions from you and see you soon.

You can find the complete code in this GitHub repo.

8 thoughts on “Sling Models: How to write Sling Model Injector

  1. I am having an issue, all other controllers for example a class that has a field injected with @Inject also call the getValue of my new annotation. It seems that the @Source does not work. I am using aem 6.3.1.2. Any idea?

    1. JDRUWE, when you use @Inject, Sling does not know, which Injector should be used to inject the value, so it goes through all the Injectors and stops as soon as first non-null value is obtained.

  2. Если вы задумываетесь о сотрудничестве с Esperio, то ознакомьтесь предварительно с этим обзором. Брокер заявляет о себе, как об одном из лучших на рынке. Однако наше мнение по этому вопросу противоположное. Мы проведем подробный анализ предложений этой компании и изучим отзывы трейдеров, чтобы определить, насколько ей можно доверять.

    О компании
    Официальное название: Esperio;
    Адрес, контакты: First St. Vincent Bank Ltd Building, James Street, Kingstown, Сент-Винсент и Гренадины;
    Лицензия: нет;
    Как давно на рынке: с 2021;
    Услуги: трейдинг;
    Условия: леверидж до 1:1000, нет ограничений по минимальному депозиту;
    Торговый терминал: MetaTrader 4/5;
    Активы: контракты на разницу цен.
    Самые свежие отзывы о Esperio со всего интернета
    Несмотря на почти что вдохновляющее название, брокер не особо смог заслужить похвалу от трейдеров, особенно российских. Впрочем, судя по официальному сайту, именно на них он и ориентируется.

    Итак, автор с ником MEMFIS1990 уверяет, что никому не удастся заработать с этим брокером. Согласно его комментарию, Esperio просто не выводит деньги. Мужчина уверяет, что все положительные отзывы на официальном сайте компании — фейковые. Он советует трейдерам работать только с проверенными брокерами, а не ноунеймами.

    Следующий автор с ником Raspop нашел только один положительный момент у этого брокера. В отзыве он упоминает, что Esperio не скрывает отрицательной доходности по своим портфелям. В остальном мужчина тоже не советует связываться с этой компанией. Как минимум, потому что она мало кому известна и не регулируется авторитетными надзорными органами.

    Не все трейдеры оказали столь осторожными, чтобы не торговать с Esperio. Автор следующего отзыва потерял 2 тысячи долларов на этой платформе. Он пополнял счет через систему WebMoney. К сожалению, обращение к юристам не решило вопрос, процедура чарджбэка в этом случае оказалась недоступной. Клиенту пришлось смириться с потерей, так как все его обращения Esperio все равно проигнорировал.

    Еще одна девушка вообще описывает в отзыве порядок работы Esperio. Александра утверждает, что представители компании ищут потенциальных клиентов в социальных сетях. Изначально будущим жертвам предлагают работу, а именно, простое заполнение Excel таблиц. Потом их уговаривают приобрести курс обучения не менее чем за 50 тысяч рублей, и, наконец, самим начать торговлю на платформе Esperio. Естественно, все заканчивается сразу после пополнения. Аналитики, советующие открывать сделки, скорее рано, чем поздно, загоняют депозит трейдера в ноль.

    Подтверждает слова Александры Виктория. Она называет Esperio самым ужасным местом работы. Девушка пишет в отзыве, что после месяца оплачиваемой стажировки ей, в конце концов, ничего не перечислили. Зато она регулярно терпела оскорбления от руководства. Виктория не скрывает, что главной ее обязанностью был холодный обзвон и развод людей на деньги.

    Признаки обмана, мошенничества
    Когда реальные отзывы настолько отличаются от тех, что брокер размещает на своем официальном сайте, вывод очевиден. Вряд ли перед нами честный и прозрачный посредник. К тому же, мы нашли и другие спорные моменты.

    Срок существования проекта
    Трейдеров пытаются убедить в том, что Esperio появился в отрасли еще в 2012 году. Это утверждение можно прочесть в коротком описании самой компании, и заметить в футере сайта. Однако ничего общего с реальным положением вещей такие заявления не имеют. В отзывах авторы пишут об отсутствии узнаваемости бренда не просто так. До недавнего времени о таком посреднике никто не знал.

    Данные веб-архива показывают, что брокер занял сайт в июне 2021 года. В самом деле, сложно представить, чтобы за 10 лет работы действительно надежный брокер так и остался для большинства трейдеров неузнаваемым.
    Регистрация
    С юридическими данными у Esperio вообще произошла какая-то путаница. Брокер указывает в качестве управляющей компании некую OFG Cap LTD. При этом в реестр Сент-Винсент и Гренадин она должна быть внесена под номером 20603. Разумеется, мы не нашли ни одного упоминания о таком наборе цифр.

    Но хотя бы OFG Cap, правда, не LTD, а LLC все-таки существует. Кстати, она была инкорпорирована в 2022 году, даже позже, чем появился официальный сайт. Уж совсем никак не в 2012. В футере указана недостоверная информация.
    Добавить ко всему перечисленному стоит и то, что гренадинский офшор — не самое лучшее место в мире для регистрации бизнеса. Вернее, для мошенников, конечно, сойдет. А вот у клиентов, особенно трейдеров, могут возникнуть существенные проблемы при сотрудничестве с такими компаниями.

    Отсутствие документов
    Назваться брокером недостаточно. Esperio было бы неплохо еще получить официальное разрешение на свою деятельность. Но Сент-Винсент и Гренадины как раз предпочитают те посредники, которым не хочется думать о лицензиях. FSA (регулятор в этой юрисдикции) не выдает подобным онлайн дилинговым центрам лицензии. И вообще он не ведет никакого надзора за их деятельностью.

    На главной странице регулятора так и написано, что внесение в реестр — это не гарантия хорошей репутации компании. Российский Центробанк, например, вообще заблокировал сайт Esperio и внес фирму в черный список.
    Выводы
    Esperio зарегистрирован в офшорной зоне Сент-Винсент и Гренадин, что означает отсутствие контроля со стороны государственных органов. Кроме того, у него нет лицензии на осуществление брокерской деятельности. Торговать на таких площадках не стоит. Это легко обернется потерей капитала.

  3. Пока good
    Makar77 Сентябрь 11, 2023
    С самого начала, когда я начал здесь торговать я зарабатывал не много, и страшновато было, но сейчас я уже втянулся и доход у меня не плохой, я доволен. На парах пробовала сначала, потом и крипту начала осваиваить, на крипте конечно более выгодно. Заработок свой я выводил несколько раз и проблем не было. С поддержкой общался недавно, ребята понятливые и помощь по сути получил без воды. Бонусы разные есть, но еще не применял. Пока что брокером доволен вполне.

    не жалею, что начал здесь работать
    Kreont Сентябрь 18, 2023
    Поключился к трейдеру skaktrade. За несколько месяцев отличная прибыль. Впервые пробую копитрейдинг и вижу, что реально работает. Копируешь себе сделки и торгуешь без нервных срывов. Пока в плюсе….продолжаю дальше….

    деньги пришли быстро
    Supasertio Сентябрь 30, 2023
    В Эсперио я попал совершенно случайно как говорится за компанию. Зарегистрировался, ну пополнил счет. Неожидал, что быстро получу прибыль если честно. На Тесле поднялся, поставил на вывод половину своих денег . Я думал, что будут какие то трудности, но все прошло успешно. Деньги пришли через 2 дня. Нормально можно работать тут.

    время покажет
    Владимир Свой” Октябрь 18, 2023
    Верификация здесь быстрая, за это огромное спасибо. У предыдущего брокера, с которым у меня не сложилось я регистрировался целый день. Сами понимаете сколько нервов надо на это. А в Эсперио все оперативно и пошагово, никакой волокиты. Отличные условия для клиентов. Немного заработал уже и вывод проверил, пришли на второй день. Единственное, что в поддержку обращался два часа назад ответа пока нет…Увидим как дальше

    Трейдинг не мое
    Гарик Декабрь 27, 2023
    Мне трейдинг не подошёл, вообще не вижу себя в роли трейдера.
    Сколько бы я не учился, результат никакого нет.
    Заработал 10$, разве это успех? Не вижу смысла продолжать, только нервирую себя и даю надежды.
    Я здесь вывел 10 долларов и на этом конец.

  4. Зарабатывать тут вариантов куча
    Allo Ноябрь 27, 2022
    Компания предлагает много дополнительных услуг, много возможностей. Тут зарабатывать деньги вариантов масса, хоть самостоятельно торгуй, хоть пассивно инвестируй в фонд, в готовые инвестпортфели, в копирование сделок. Вы можете создать аж до 5 источников дохода, все на одной платформе. Есть партнерская программа, активно рекламируйте брокера (есть за что рекламировать между прочим), и получайте еще больше профита с рефералов. В общем, вариантов масса, и мошенничеством даже не пахнет.

    Копирую сделки с хорошей прибылью
    D1O Декабрь 5, 2022
    Эта брокерская компания богата на дополнительный функционал и сервис. Сначала я торговал на стандартном тарифе, с адекватными условиями. В принципе и сейчас приторговываю, но большую часть депозита я закинул в копирование сделок. Он тут сделан прекрасно и удобно. Сам настраиваешь свои обороты, свой риск-менеджмент, вся статистика перед лицом, в том числе и того трейдера, которого ты копируешь. Так что самое сложное, это по сути выбрать трейдера, который будет грамотно и прибыльно торговать. Я таких двух нашел, приносят мне в среднем по 12,48% ежемесячно, это уже после вычета всех комиссий.
    Сам я приторговываю на небольшую сумму, но не вывожу прибыль, реинвестирую, постепенно раскачивая счет. Предпочитаю все-таки, чтобы деньги работали за меня.
    В остальном брокер тоже хорош, как и по условиям, так и по качеству обслуживания.

    Да внесите уже эту кухню в ЧС
    Alilo Декабрь 5, 2022
    Давно пора уже внести эту поганую кухню в черный список. Развелось тут видите ли, всяких там компашек по форекс-торговле. Надоели уже скамить и кидать людей на деньги. Давно надо запретить весь этот балаган.

    Отличный дополнительный сервис
    1988 Декабрь 10, 2022
    Тут и обучающие курсы предоставят, и просто какие-то конкретные обучающие материалы. Я вот например изучал тему индикаторов, запросил много материала на эту тему, все прислали быстро на почту. Правда, я все равно так и не стал ими пользоваться, после изучения тема показалась гиблой, стандартная связка технического + фундаментального анализа работает более грамотно.
    Тут и аналитика топовая. Тоже ее использую, не прямо голую аналитику беру, а связываю ее со своей торговой стратегий. Получается еще улучшенная стратегия.

    Тут мега тяжело торговать в плюс
    SabaRahm Декабрь 17, 2022
    У меня большие проблемы с торговлей в Esperio. Во-первых, терминал хоть и метатрейдер 5, но лагает супер-сильно. Не знаю, в чем проблема, потому что попробовал поторговать на том же метатрейдере 5, но только у другого брокера, там такой проблемы не наблюдается, все работает здорово. То есть трабла именно в этйо конторе. Во-вторых, спреды + комиссии. И ладно бы спреды и комиссии в совокупности были мелкими, но спреды еще куда не шло, они плавающие, но вот комиссионные тут сильно бьют по карману. Так что как торговать в плюс у этого посредника, я не понимаю.

    Все работает просто супер
    marat Январь 7, 2023
    Что такое Esperio? Это топовый дилинговый центр с опытом ведения деятельности более 10 лет. Мало какие брокерские компании даже до 5-летнего стажа-то доживают, так как эта индустрия мега конкурентная, но факт того, что эта фирма смогла, говорит о многом.
    Сам я здесь торгую с конца 2021 года, то бишь чуть более, чем один год. Первые три месяца скорее привыкал, именно к комиссиям, к спредам. Сначала казалось, что это большие издержки, но потом подсчитал, что на самом деле издержки тут не выше, чем в среднем по рынку. Плавающие спреды весьма низкие, и практически никакого влияния не имеют, зато нет проблем с тем, что из-за спреда тебе закрывают позицию по стопу, хотя цена до этой отметки не заходила. Думаю, что многие форекс-трейдеры сталкивались с подобной проблемой, понимают, о чем я.
    Выводы прибыли начал делать где-то на 4-5 месяца. Сначала небольшие суммы, по 200-300 долларов 1-2 раза в месяц. В сентябре 2022 года вывел первый раз крупную сумму в 2000 долларов. Пришли деньги нормально. К слову, KYC я сразу же прошел после регистрации, так что от брокера ко мне вопросов совсем не было. Ну вот так и продолжаю торговать, выводы от 1000-2000 долларов стабильно, все приходит в срок и вовремя.

    Мошенники да и все
    vich Январь 11, 2023
    Чтобы было понятнее – 99,99% брокерских компаний из офшоров типо Гренадин, во-первых, кухонные которые, которые не могут позволить себе доступ к ECN, а во-вторых, обманывают людей, начиная с информации о своей деятельности, и заканчивая выводами денег клиентов.
    Я уверен на 100%, что у этого брокера есть много проблем, включая вывод средств. Он может давать выводить мелочь, до 100-200 баксов, но крупные суммы он вам с радостью забракует, так как они просто грабят его, ведь форекс тут не настоящий, а кухонный

    Офшорная грязная кухня
    Альфа Январь 12, 2023
    Мне вот кажется, что открывать депозит у брокера, который не имеет лицензии, да еще и зарегистрирован где-то в офшорной стране, не самая лучшая идея. Компании, которые находятся в Сент-Винсент и Гренадины, имеют слишком негативную репутацию. С такими брокерами лучше не начинать работать, так как они с легкостью, по щелчку пальцев, становятся скамами, и деньги из таких контор вытащить не представляется возможным.
    Именно такой грязной офшорной кухней является Esperio. Не представляю, какие гарантии может эта кухня предоставить, кроме тупой бумажки, неимеющей никакого значения из SVGFSA.
    Короче говоря, не вздумайте тут трейдить. Ничего нормального из этого не выйдет, лишитесь всех средств на изи-бризи.

Your thoughts are welcome