How to get page's content info before replication(DELETE)? - osgi

In CQ5, I need to collect some info from page properties and child nodes properties befor replication(delete) occurs
If i implement EventHandler:
#Component(immediate = true)
#Service
#Property(name = EventConstants.EVENT_TOPIC, value = ReplicationAction.EVENT_TOPIC)
public class RedisVideoUpdaterService implements EventHandler {
..................
public void handleEvent(Event event) {
ReplicationAction action = ReplicationAction.fromEvent(event);
ReplicationActionType actionType = action.getType();
if (actionType.equals(ReplicationActionType.DELETE)) {
//process data collecting
}
...............
After handelEvent triggered there page is already deleted.
If i implement Preprocessor
#Component(immediate = true)
#Service
public class RedisVideoUpdaterService implements Preprocessor{
..................
public void preprocess(ReplicationAction action, ReplicationOptions replicationOptions) throws ReplicationException {
ReplicationActionType actionType = action.getType();
if (actionType.equals(ReplicationActionType.DELETE)) {
//process data collecting
}
...............
preprocess method is not triggered at all on delete action.
Both cases are OK for actiovation/deactivation.
How can I process page before it have been deleted?

As I noticed. Preppocessor triggered on delete only for activated pages. This behaviour is ok for me.

Related

How can I get a VaadinView-#QuarkusTest up and running?

Assume you have the provided Vaadin view (1) and you want to test it with the #QuarkusTest (2) similar as depicted here (3) but not for Vaadin-on-SpringBoot but for Vaadin-on-Quarkus.
I get a NPE:
Caused by: java.lang.NullPointerException:
Cannot invoke "io.quarkus.arc.InjectableContext.isActive()" because
the return value of "com.vaadin.quarkus.context.UIContextWrapper.getContext()" is null
Questions:
Does anyone know how to fix this?
Are Quarkus-Tests of this kind already supported by the vaadin quarkus extension?
Do I have to add a missing test-only-dependency I do not yet know of?
Sidenotes:
The View and Test had been added to the Quarkus-Vaadin-Sample-CRM provided here: https://github.com/mstahv/quarkus-crm.git
The NPE occurs with the original version setting (Vaadin=23.0.1|Quarkus=2.7.4.Final) as well as with an update (Vaadin=23.3.4|Quarkus=2.15.3.Final)
The Vaadin-Quarkus-Template does not (yet) come along with a sample #QuarkusTest.
(1)
#UIScoped
#Route(value = "testshowcase", layout = MainLayout.class)
#PageTitle("Testing showcase")
#PermitAll
public class TestingShowcaseView extends HorizontalLayout {
TextField textFieldInput = new TextField("Eingabe");
TextField textFieldOutput = new TextField("Ausgabe");
Button btnProcess = new Button("To Upper");
#Inject
TestingShowcaseService testingShowcaseService;
#PostConstruct
public void init() {
btnProcess.addClickListener(e -> {
textFieldOutput.setValue(testingShowcaseService.toUpper(textFieldInput.getValue()));
});
VerticalLayout barInput = new VerticalLayout(textFieldInput, btnProcess);
VerticalLayout barOutput = new VerticalLayout(textFieldOutput);
add(barInput, barOutput);
}
}
#RequestScoped
public class TestingShowcaseService {
public String toUpper(String whatever) {
if (whatever == null) {
return null;
} else {
return whatever.toUpperCase();
}
}
}
(2)
#QuarkusTest
public class TestingShowcaseViewQuarkusTest {
#Inject
TestingShowcaseView testingShowcaseView;
#Test
public void testServiceInteraction() {
String actualOutput;
actualOutput = testingShowcaseView.textFieldOutput.getValue();
Assertions.assertNull(actualOutput, "... was '%s'.".formatted(actualOutput));
testingShowcaseView.btnProcess.click();
actualOutput = testingShowcaseView.textFieldOutput.getValue();
Assertions.assertEquals("RubbeldieKatz", actualOutput);
}
}
(3) [https://vaadin.com/docs/latest/tutorial/unit-and-integration-testing/#creating-and-running-integration-tests-for-more-advanced-ui-logic]

Update list items in PagingLibrary w/o using Room (Network only)

I'm using Paging Library to load data from network using ItemKeyedDataSource. After fetching items user can edit them, this updates are done inside in Memory cache (no database like Room is used).
Now since the PagedList itself cannot be updated (discussed here) I have to recreate PagedList and pass it to the PagedListAdapter.
The update itself is no problem but after updating the recyclerView with the new PagedList, the list jumps to the beginning of the list destroying previous scroll position. Is there anyway to update PagedList while keeping scroll position (like how it works with Room)?
DataSource is implemented this way:
public class MentionKeyedDataSource extends ItemKeyedDataSource<Long, Mention> {
private Repository repository;
...
private List<Mention> cachedItems;
public MentionKeyedDataSource(Repository repository, ..., List<Mention> cachedItems){
super();
this.repository = repository;
this.teamId = teamId;
this.inboxId = inboxId;
this.filter = filter;
this.cachedItems = new ArrayList<>(cachedItems);
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Long> params, final #NonNull ItemKeyedDataSource.LoadInitialCallback<Mention> callback) {
Observable.just(cachedItems)
.filter(() -> return cachedItems != null && !cachedItems.isEmpty())
.switchIfEmpty(repository.getItems(..., params.requestedLoadSize).map(...))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
#Override
public void loadAfter(#NonNull LoadParams<Long> params, final #NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
repository.getOlderItems(..., params.key, params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
#Override
public void loadBefore(#NonNull LoadParams<Long> params, final #NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
repository.getNewerItems(..., params.key, params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
#NonNull
#Override
public Long getKey(#NonNull Mention item) {
return item.id;
}
}
The PagedList created like this:
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setInitialLoadSizeHint(preFetchedItems != null && !preFetchedItems.isEmpty()
? preFetchedItems.size()
: PAGE_SIZE * 2
).build();
pagedMentionsList = new PagedList.Builder<>(new MentionKeyedDataSource(mRepository, team.id, inbox.id, mCurrentFilter, preFetchedItems)
, config)
.setFetchExecutor(ApplicationThreadPool.getBackgroundThreadExecutor())
.setNotifyExecutor(ApplicationThreadPool.getUIThreadExecutor())
.build();
The PagedListAdapter is created like this:
public class ItemAdapter extends PagedListAdapter<Item, ItemAdapter.ItemHolder> { //Adapter from google guide, Nothing special here.. }
mAdapter = new ItemAdapter(new DiffUtil.ItemCallback<Mention>() {
#Override
public boolean areItemsTheSame(Item oldItem, Item newItem) {
return oldItem.id == newItem.id;
}
#Override
public boolean areContentsTheSame(Item oldItem, Item newItem) {
return oldItem.equals(newItem);
}
});
, and updated like this:
mAdapter.submitList(pagedList);
P.S: If there is a better way to update list items without using Room please share.
All you need to do is to invalidate current DataSource each time you update your data.
What I would do:
move networking logic from MentionKeyedDataSource to new class that extends PagedList.BoundaryCallback and set it when building your PagedList.
move all you data to some DataProvider that holds all your downloaded data and has reference to DataSource. Each time data is updated in DataProvider invalidate current DataSource
Something like that maybe
val dataProvider = PagedDataProvider()
val dataSourceFactory = InMemoryDataSourceFactory(dataProvider = dataProvider)
where
class PagedDataProvider : DataProvider<Int, DataRow> {
private val dataCache = mutableMapOf<Int, List<DataRow>>()
override val sourceLiveData = MutableLiveData<DataSource<Int, DataRow>>()
//implement data add/remove/update here
//and after each modification call
//sourceLiveData.value?.invalidate()
}
and
class InMemoryDataSourceFactory<Key, Value>(
private val dataProvider: DataProvider<Key, Value>
) : DataSource.Factory<Key, Value>() {
override fun create(): DataSource<Key, Value> {
val source = InMemoryDataSource(dataProvider = dataProvider)
dataProvider.sourceLiveData.postValue(source)
return source
}
}
This approach is very similar to what Room does - every time table row is updated - current DataSource is invalidated and DataSourceFactory creates new data source.
You can modify directly in your adapter if you called currentList like that
class ItemsAdapter(): PagedListAdapter<Item, ItemsAdapter.ViewHolder(ITEMS_COMPARATOR) {
...
fun changeItem(position: Int,newData:String) {
currentList?.get(position)?.data = newData
notifyItemChanged(position)
}
}

Adding custom data for an operation to Application Insights telemetry

I'm trying to add a bunch of custom data fields to every piece of telemetry I can, and this data is consistent across a single operation, but varies from operation to operation.
I have a custom ITelemetryInitializer, and within that I can do something like:
public class MyInitializer : ITelemetryInitializer
{
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = "PC";
}
}
But I don't understand how I'm suppose to push this data into this initializer.
I've added something like this:
public class MyInitializer : ITelemetryInitializer
{
private string mPlatform = "unknown";
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.Properties[ "platform" ] = mPlatform;
}
public void SetPlatform(string platform)
{
mPlatform = platform
}
}
And then at the controller level I do something like this:
foreach (var init in TelemetryConfiguration.Active.TelemetryInitializers)
{
var customInit = init as MyInitializer;
if (customInit != null)
{
customInit.SetPlatform(requestPlatform);
}
}
But this is horribly clunky, and prone to error (e.g. if a piece of telemetry gets sent before this function is called), and I'm not really sure if this is thread-safe.
What's the intended way of passing around this kind of data?
I think I've solved this now, the solution is to write to the properties of the TelemetryClient within the controller like this:
[Route( "[controller]" )]
public class MyController : Controller
{
private readonly TelemetryClient mTelemetryClient;
public MyController(
TelemetryClient TelemetryClientArg )
{
mTelemetryClient = TelemetryClientArg;
mTelemetryClient.Context.Properties.Remove("platform");
}
[HttpPost]
[Produces( "application/json" )]
public IActionResult Post( [FromBody] RequestClass RequestData )
{
mTelemetryClient.TrackTrace("Test trace 1"); // doesn't have platform set
mTelemetryClient.Context.Properties["platform"] = RequestData.platform;
mTelemetryClient.TrackTrace("Test trace 2"); // has platform set correctly
}
}
This seems to be safe as the controller constructor appears to be called before each http request is processed and the context within the TelemetryClient is unique per thread. I would like to get confirmation from the team that this is reasonable.

How can I log method expressions of JSF ajax requests

I have figured out how to log when a request is an ajax request and which page it is from, in a filter.
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="#form" listener="#{aboutYouController.findAddress}" .... )
How can I do this? My app has many ajax requests and I want to log which are being triggered.
public class TrackingFilter implements Filter {
private static Logger LOG = Logger.getLogger(TrackingFilter.class);
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
String pageHit = req.getRequestURI().substring(req.getContextPath().length()+1).replace(".xhtml", "");
if(!pageHit.contains("javax.faces.resource")){ // if is a url we want to log
if ("partial/ajax".equals(req.getHeader("Faces-Request"))) {
LOG.trace("ajax on URI: " + req.getRequestURI());
}
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="#form" listener="#{aboutYouController.findAddress}" ....)
This information is only available in the JSF component tree. The JSF component tree is only available after view build time. A view is only built when the request has been served by FacesServlet. Thus, a servlet filter is way too early as it runs before any servlet.
You'd better run the code after the restore view phase of a postback. The JSF component tree is guaranteed to be available during that moment. You can use FacesContext#isPostback() to check if the current request is a postback. You can use PartialViewContext#isAjaxRequest() to check if the current request is an ajax request. You can use the predefined javax.faces.source request parameter to obtain the client ID of the source component of the ajax request. You can use the predefined javax.faces.behavior.event request parameter to obtain the ajax event name (e.g. change, click, action, etc).
Obtaining the associated behavior listeners is in turn a story apart. This is easy on ActionSource2 components (e.g. <h|p:commandButton action="#{...}">) as the MethodExpression is just available by ActionSource2#getActionExpression(). However, this isn't easy on BehaviorBase taghandlers (e.g. <f|p:ajax listener="#{...}">) as this API doesn't have any method like getBehaviorListeners(). There are only methods to add and remove them, but not to obtain a list of them. So some nasty reflection trickery is necessary to access the private field with those listeners whose name is JSF implementation specific. In Mojarra it's listeners and in MyFaces it's _behaviorListeners. Both are fortunately assignable from List and it's the only field of that type, so we could just check for that. Once having hand of the BehaviorListener instance, then you still need to do another reflection trickery to obtain the MethodExpression field of that instance. Yuck.
All in all, here's how the trickery look like in flavor of a PhaseListener listening on afterPhase of RESTORE_VIEW:
public class AjaxActionLoggerPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (!(context.isPostback() && context.getPartialViewContext().isAjaxRequest())) {
return; // Not an ajax postback.
}
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String sourceClientId = params.get("javax.faces.source");
String behaviorEvent = params.get("javax.faces.behavior.event");
UIComponent source = context.getViewRoot().findComponent(sourceClientId);
List<String> methodExpressions = new ArrayList<>();
if (source instanceof ClientBehaviorHolder && behaviorEvent != null) {
for (ClientBehavior behavior : ((ClientBehaviorHolder) source).getClientBehaviors().get(behaviorEvent)) {
List<BehaviorListener> listeners = getField(BehaviorBase.class, List.class, behavior);
if (listeners != null) {
for (BehaviorListener listener : listeners) {
MethodExpression methodExpression = getField(listener.getClass(), MethodExpression.class, listener);
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
}
}
}
if (source instanceof ActionSource2) {
MethodExpression methodExpression = ((ActionSource2) source).getActionExpression();
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
System.out.println(methodExpressions); // Do your thing with it.
}
private static <C, F> F getField(Class<? extends C> classType, Class<F> fieldType, C instance) {
try {
for (Field field : classType.getDeclaredFields()) {
if (field.getType().isAssignableFrom(fieldType)) {
field.setAccessible(true);
return (F) field.get(instance);
}
}
} catch (Exception e) {
// Handle?
}
return null;
}
}
In order to get it to run, register as below in faces-config.xml:
<lifecycle>
<phase-listener>com.example.AjaxActionLoggerPhaseListener</phase-listener>
</lifecycle>
Above is tested and compatible with Mojarra and PrimeFaces and theoretically also compatible with MyFaces.
Update: in case you're using JSF utility library OmniFaces, or are open to, since version 2.4 you can use the new Components#getCurrentActionSource() utility method to find out the current action source component and Components#getActionExpressionsAndListeners() to get a list of all action methods and listeners registered on a given component. This is also useable on regular (non-ajax) requests. With that, the above PhaseListener example can be reduced as below:
public class FacesActionLoggerPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.PROCESS_VALIDATIONS;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
if (!event.getFacesContext().isPostback())) {
return;
}
UIComponent source = Components.getCurrentActionSource();
List<String> methodExpressions = Components.getActionExpressionsAndListeners(source);
System.out.println(methodExpressions); // Do your thing with it.
}
}

Apache Wicket: React on Ajax Request Before Model is Updated

I have a number of Wicket components on a page that use a PropertyModel to reflect properties of some beans. Using AjaxFormComponentUpdatingBehaviors, these components are automatically updated via Ajax when the user changes them.
When properties are changed, the beans I want to edit with my components fire PropertyChangeEvents that should trigger re-renders of certain components that listen to these events (implementing PropertyChangeListener):
Example:
User edits a TextField with a PropertyModel and an AjaxFormComponentUpdatingBehavior
An AJAX request is sent
Wicket dispatches the request to the AjaxFormComponentUpdatingBehavior
The behavior's onEvent updates the PropertyModel (unfortunately, this method is final)
The PropertyModel calls the backing bean's property setter
The backing bean fires and PropertyChangeEvent
Now I want all components listening for changes of the same backing bean to be notified
The behavior calls the abstract onUpdate, but now it's to late, the property change events are already handled.
Since my beans are not serializable, I cannot register the components permanently as event listeners. I either need to register proxy objects that somehow retrieve the component to notify, or register my components temporarily for the scope of the AJAX request.
What I would like to do is to hook into Wickets request cycle after the target page has been loaded but before the Ajax behavior updates the model, that would lead to the PropertyChangeEvent. Here I can register every component as a event listener on their backing beans (addPropertyChangeListener) so that they are notified if they need to be updated.
Then, in onEvent, each component can take measures to update itself using the AjaxRequestTarget if they received a PropertyChangeEvent before.
Finally, in onDetach, the components can unregister from their beans (removePropertyChangeListener).
Unfortunately, I found no built-in way to get a notification "on Ajax request". In my Ajax behavior's onUpdate methods, the model has already been updated and it is too late to register change listeners. I could implement my own behavior, but with the different component options (text fields, choice lists, etc.), this is quite an effort.
Did I miss something?
I don't quite understand exactly what you mean by "components registering as event listeners". Are you talking about registering IRequestCycleListeners?
Either way, perhaps Wicket's inter-component events can help you here. Every component implements the following interface:
public interface IEventSink
{
/**
* Called when an event is sent to this sink
*
* #param event
*/
void onEvent(IEvent<?> event);
}
You could subclass AjaxFormComponentUpdatingBehavior to fire an event after a model is updated like so:
public class AjaxUpdateEvent {
private final AjaxRequestTarget target;
public AjaxUpdateEvent(AjaxRequestTarget target) {
this.target = target;
}
public AjaxRequestTarget getAjaxRequestTarget() {
return target;
}
}
public class BeanModifiedEvent extends AjaxUpdateEvent {
private final Bean bean;
public BeanModifiedEvent(AjaxRequestTarget target, Bean theBean) {
super(target);
}
public Bean getBean() {
return bean;
}
}
public class CustomUpdatingBehavior extends AjaxFormComponentUpdatingBehavior {
protected abstract void onUpdate(AjaxRequestTarget target) {
Bean bean = getFormComponent().getModelObject();
getComponent().send(getComponent().getPage(), Broadcast.BREADTH, new BeanModifiedEvent(target, bean));
}
}
You can then catch the event in the required components and add them to the ajax request:
public class UserDetailsPanel extends Panel {
.....
#Override
public void onEvent(IEvent event) {
if(event.getPayload() instanceof BeanModifiedEvent) {
// if(whatever) to control whether to add or not
AjaxRequestTarget target = ((BeanModifiedEvent) event.getPayload()).getAjaxRequestTarget();
target.add(...);
}
}
Event doc:
17.2, "Wicket events infrastructure" section
18.3, "Built-in AJAX behaviors" section
You can override #getUpdateModel() to return false, then in #onUpdate() do whatever you want before calling getFormComponent().updateModel().
You could be overriding onModelChanging of each component you are using and firing your PropertyChangeEvent there. According to the documentation onModelChanging is called before
the model is changed.
#Override
protected void onModelChanging() {
super.onModelChanging();
oldModelObject = yourComponent.getModelObject();
//fire PropertyChangeEvent
}
This is what I came up with in the end.
I subclassed IContextProvider<AjaxRequestTarget, Page> to create a custom provider for AjaxRequestTarget objects. When an AjaxRequestTarget is requested, I broadcast it to the component tree using Wicket's event mechanism.
public class BroadcastingAjaxRequestTargetProvider implements IContextProvider<AjaxRequestTarget, Page> {
private final IContextProvider<AjaxRequestTarget, Page> parent;
public BroadcastingAjaxRequestTargetProvider(IContextProvider<AjaxRequestTarget, Page> parent) {
this.parent = parent;
}
#Override
public AjaxRequestTarget get(Page page) {
AjaxRequestTarget target = parent.get(page);
page.send(page, Broadcast.BREADTH, new AjaxRequestBegin(target));
return target;
}
}
The class AjaxRequestBegin is just a small payload object encapsulating the AjaxRequestTarget.
I register this provider in my Wicket application's init() method:
setAjaxRequestTargetProvider(new BroadcastingAjaxRequestTargetProvider(getAjaxRequestTargetProvider()));
Now each component gets notified when an AJAX request is handled, before Wicket dispatches it to a component or behavior. A component can override onEvent to register a PropertyChangeListener for the request:
public void onEvent(IEvent<?> event) {
final Object payload = event.getPayload();
if (payload instanceof AjaxRequestBegin) {
final AjaxRequestTarget target = ((AjaxRequestBegin) payload).getTarget()
AjaxPropertyChangeListener listener = new AjaxPropertyChangeListener(target);
target.addListener(listener);
getBean().addPropertyChangeListener(listener);
}
}
private class AjaxPropertyChangeListener implements PropertyChangeListener, AjaxRequestTarget.IListener {
private final AjaxRequestTarget target;
public AjaxPropertyChangeListener(AjaxRequestTarget target) {
this.target = target;
}
#Override
public void propertyChange(PropertyChangeEvent event) {
target.add(MyComponent.this);
}
#Override
public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target) {
}
#Override
public void onAfterRespond(Map<String, Component> map, IJavaScriptResponse response) {
getBean().removePropertyChangeListener(this);
}
}
Note that AjaxPropertyChangeListener also implements AjaxRequestTarget.IListener to unregister itself after the AJAX request has been completed.

Resources