Пример использования: Мы разрабатываем сайт закрытой группы пользователей AEM, где пользователи должны будут отправлять формы, запускающие рабочие процессы. Поскольку пользователи аутентифицированы, часть полезной нагрузки рабочего процесса должна включать пользователя, инициировавшего форму.
Я рассматриваю возможность использования для этого форм AEM, которые сохраняются на узлах под /content/usergenerated/content/forms/af/my-site, но пользователь не упоминается в полезной нагрузке (только пользователь службы). В этом случае есть два пользователя службы: служба рабочего процесса, выполняющая рабочий процесс, и служба fd, выполняющая обработку формы и первоначальное сохранение. НАПРИМЕР. следующий код вызывается из отчетов о шагах рабочего процесса 'fd-service'
workItem.getWorkflowData().getMetaDataMap().get("userId", String.class);
Чтобы обойти это ограничение,
Workflow initiated from publish AEM instance: All workflow instances are created using a service user when adaptive forms, interactive communications, or letters are submitted from AEM publish instance. In these cases, the user name of the logged-in user is not captured in the workflow instance data.
Я добавляю сервлет фильтра, чтобы перехватить отправку начальной формы перед сервлетом AEM Forms, используя оболочку запроса, чтобы изменить тело запроса, добавив исходный идентификатор пользователя.
Что касается форм, рабочих процессов и средств запуска ... Это в основном то, что у меня есть https://helpx.adobe.com/aem-forms/6/aem-workflows-submit-process-form.html
Я просмотрел следующие ресурсы:
Вот код моей обертки
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletInputStream;
import java.io.*;
public class FormSubmitRequestWrapper extends SlingHttpServletRequestWrapper {
String requestPayload;
private static final Logger log = LoggerFactory.getLogger(FormSubmitRequestWrapper.class);
public FormSubmitRequestWrapper(SlingHttpServletRequest slingRequest) {
super(slingRequest);
// read the original payload into the requestPayload variable
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
// read the payload into the StringBuilder
InputStream inputStream = slingRequest.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
// make an empty string since there is no payload
stringBuilder.append("");
}
} catch (IOException ex) {
log.error("Error reading the request payload", ex);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException iox) {
log.error("Error closing bufferedReader", iox);
}
}
}
requestPayload = stringBuilder.toString();
}
/**
* Override of the getInputStream() method which returns an InputStream that reads from the
* stored requestPayload string instead of from the request's actual InputStream.
*/
@Override
public ServletInputStream getInputStream ()
throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestPayload.getBytes());
ServletInputStream inputStream = new ServletInputStream() {
public int read ()
throws IOException {
return byteArrayInputStream.read();
}
};
return inputStream;
}
}
Вот мой фильтр
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.engine.EngineConstants;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Session;
import javax.servlet.*;
import java.io.IOException;
@Component(service = Filter.class,
immediate = true,
property = {
Constants.SERVICE_DESCRIPTION + "=Add the CUG userID to any UGC posts",
EngineConstants.SLING_FILTER_SCOPE + " = " + EngineConstants.FILTER_SCOPE_REQUEST,
Constants.SERVICE_RANKING + ":Integer=3000",
EngineConstants.SLING_FILTER_PATTERN + "=/content/forms/af/my-site.*"
})
public class DecorateUserGeneratedFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(DecorateUserGeneratedFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse ) response;
final SlingHttpServletRequest slingRequest= (SlingHttpServletRequest) request;
FormSubmitRequestWrapper wrappedRequest = new FormSubmitRequestWrapper(slingRequest);
log.info("starting ConfirmAlumniStatus workflow");
log.info(getCurrentUserId(slingRequest));
chain.doFilter(wrappedRequest, slingResponse);
}
@Override
public void destroy() {
}
public String getCurrentUserId(SlingHttpServletRequest request) {
ResourceResolver resolver = request.getResourceResolver();
Session session = resolver.adaptTo(Session.class);
String userId = session.getUserID();
return userId;
}
}
Когда отправка POST обрабатывается этим фильтром, я получаю сообщение об ошибке ниже, в котором говорится, что тело запроса уже прочитано. Таким образом, кажется, что рейтинг фильтра может быть недостаточно высоким.
25.06.2018 13:11:13.200 ERROR [0:0:0:0:0:0:0:1 [1529946669719] POST /content/forms/af/my-site/request-access/jcr:content/guideContainer.af.internalsubmit.jsp HTTP/1.1] org.apache.sling.engine.impl.SlingRequestProcessorImpl service: Uncaught Throwable java.lang.IllegalStateException: Request Data has already been read at org.apache.sling.engine.impl.request.RequestData.getInputStream(RequestData.java:669) at org.apache.sling.engine.impl.SlingHttpServletRequestImpl.getInputStream(SlingHttpServletRequestImpl.java:292) at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:136) at my.site.servlets.FormSubmitRequestWrapper.(FormSubmitRequestWrapper.java:26) at my.site.servlets.DecorateUserGeneratedFilter.doFilter(DecorateUserGeneratedFilter.java:75) at org.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:68) at org.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:73) at org.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:73) at com.cognifide.cq.includefilter.DynamicIncludeFilter.doFilter(DynamicIncludeFilter.java:82) at org.apache.sling.engine.impl.filter.AbstractSlingFilterChain.doFilter(AbstractSlingFilterChain.java:68) at org.apache.sling.engine.impl.debug.RequestProgressTrackerLogFilter.doFilter(RequestProgressTrackerLogFilter.java:10
Я не думаю, что рейтинг услуг работает. Когда я смотрю http: // локальный: 4502 / система / консоль / статус-slingfilter мой фильтр отображается, как показано. Судя по другим перечисленным фильтрам, я думаю, что крайнее левое число - это рейтинг фильтра. По какой-то причине мой фильтр имеет рейтинг 0, хотя я установил его как service.ranking = 700
0 : class my.site.servlets.DecorateUserGeneratedFilter (id: 8402, property: service.ranking=700); called: 0; time: 0ms; time/call: -1µs
Обновление: мне удалось исправить ранг фильтра, сделав его 700 по-прежнему выдающим исключение IllegalStateException. Установка 3000 решила эту проблему. Но когда из моей оболочки вызывается request.getInputStream (). Он возвращает ноль.
Я думаю, вы правы, что getInputStream уже вызывается. Обычно я соглашусь с тем, что использование атрибута было бы хорошим вариантом, но в этом случае я не контролирую сервлет. Итак, я полагаю, что если мы добавим его в тело запроса, то, возможно, сервлет обработает наши данные вместе с другими отправленными данными формы.
Я вижу, в таком случае кажется, что userId действительно не документирован? и может измениться в будущем имп. Я бы порекомендовал пойти другим путем с сервлетом, который запускает ваши рабочие процессы. (Я скоро отправлю ответ)
Верный. Это довольно неожиданная проблема, поскольку я ожидал, что идентификатор пользователя уже будет частью полезной нагрузки или метаданных формы. В нашем случае пользователь аутентифицирован, а идентификатор пользователя известен с помощью метода, который я включил в фильтр getCurrentUserId. Кажется, должен быть способ сохранить это для использования в рабочих процессах.
Я думаю, что формы должны изначально поддерживать запуск рабочих процессов. создание средства запуска для запуска рабочего процесса при изменении узлов при отправке формы кажется более сложным, чем нужно. Вы можете просто создать сервлет для обработки форм и в этом сервлете инициировать рабочий процесс.




Верная идея, неправильное место.
Короткий ответ заключается в том, что когда вы реализуете SlingHttpServletRequestWrapper, он обеспечивает обработку вызовов методов по умолчанию для исходного SlingHttpServletRequest, если вы добавляете параметр на лету, что вы хотите сделать, это убедиться, что методы, которые взаимодействуют с параметрами переопределены, так что вы можете быть уверены, что ваш добавлен. Поэтому при инициализации вызовите исходную карту параметров, скопируйте эти элементы в новую карту, которая включает ваши собственные значения.
Затем отключите любые методы, которые будут запрашивать эти значения
getParameter(String)
getParameterMap()
getParameterNames()
getParameterValues(String)
Не трогайте InputStream, он уже был обработан для получения переданных параметров.
Кроме того, это один из двух способов справиться с этим типом варианта использования, другой вариант - использовать SlingPOSTProcessors, как описано в документации. https://sling.apache.org/documentation/bundles/manipulating-content-the-slingpostservlet-servlets-post.html
который позволяет вам определять, что записывается в репозиторий, и изменять данные, включая, как и в вашем случае, дополнительное поле.
То, что вы пытаетесь сделать, может быть легким путем, но может не соответствовать требованиям будущего для новых выпусков AEM.
Вам нужен полный контроль над тем, как запускается ваш рабочий процесс !:
Таким образом, вам не придется возиться с пусковыми установками, и рабочие процессы запускаются вашими пользователями с использованием их идентификатора пользователя.
Надеюсь это поможет.
Большое спасибо за ваш вклад! Я вообще не думаю, что другой маршрут является «легким». Мне ваше предложение легче реализовать. На этом этапе я принимаю ваше предложение, поскольку оно устраняет некоторую неопределенность уравнения. Однако я не собираюсь принимать это как ответ, потому что мой исходный пост посвящен тому, как выполнять перенос запросов в Sling. Я все еще хочу знать, как это делается.
если вы ищете пример кода:
@SlingFilter(order = 1)
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
return;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
ServletRequest request = servletRequest;
if (servletRequest instanceof SlingHttpServletRequest) {
final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) servletRequest;
request = new SlingHttpServletRequestWrapper(slingRequest) {
String userId = getCurrentUserId(slingRequest);
};
}
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
return;
}
getInputStream, вероятно, был вызван до вашего фильтра. Я бы посоветовал сохранить идентификатор пользователя в атрибуте запроса, а затем получить и вернуть его в вашей оболочке. Надеюсь, это имеет смысл. Кроме того, должен ли userId быть в теле запроса для начала?