[MAGNOLIA-9108] Support queryResults wrapper from AbstractContentDecorator Created: 25/Sep/23  Updated: 25/Sep/23

Status: Open
Project: Magnolia
Component/s: None
Affects Version/s: 6.2.39
Fix Version/s: None

Type: Improvement Priority: Neutral
Reporter: Viet Nguyen Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Relates
Template:
Acceptance criteria:
Empty
Task DoD:
[ ]* Doc/release notes changes? Comment present?
[ ]* Downstream builds green?
[ ]* Solution information and context easily available?
[ ]* Tests
[ ]* FixVersion filled and not yet released
[ ]  Architecture Decision Record (ADR)

 Description   

Customer want to replace all 'ß' with 'ss' for certain sites (not for all) and have currently solved this by wrapping the session using a custom ContentDecorator (wrapped javax.jcr.repository providing wrapped sessions using info.magnolia.repositoryProvider.getUnderlyingRepository()).

While this works for nodes retrieved by path or id it doesn't work for nodes retrieved by queries, since they are not wrapped in the info.magnolia.jcr.decoration.ContentDecoratorWorkspaceWrapper.

So we can achieved this by wrapping it ourself, but I think it would be best, if you could apply this to core implementation:

import info.magnolia.jcr.decoration.AbstractContentDecorator;
import info.magnolia.jcr.decoration.ContentDecorator;
import info.magnolia.jcr.decoration.ContentDecoratorWorkspaceWrapper;
import info.magnolia.jcr.iterator.WrappingNodeIterator;
import lombok.experimental.Delegate;
import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Workspace;
import javax.jcr.query.*;

public abstract class AbstractQueryContentDecorator extends AbstractContentDecorator {

	@Override
	public Workspace wrapWorkspace(final Workspace workspace) {
		return new QueryContentDecoratorWorkspaceWrapper(workspace, this);
	}

	private static class QueryContentDecoratorWorkspaceWrapper extends ContentDecoratorWorkspaceWrapper {
		private final ContentDecorator contentDecorator;

		private QueryContentDecoratorWorkspaceWrapper(final Workspace wrapped, final ContentDecorator contentDecorator) {
			super(wrapped, contentDecorator);
			this.contentDecorator = contentDecorator;
		}

		@Override
		public QueryManager getQueryManager() throws RepositoryException {
			return new QueryManagerWrapper(super.getQueryManager(), contentDecorator);
		}
	}

	private static class QueryManagerWrapper implements QueryManager {
		private interface Create {
			Query createQuery(String statement, String language);
			Query getQuery(Node node);
		}

		@Delegate(excludes = QueryManagerWrapper.Create.class)
		private final QueryManager wrapped;
		private final ContentDecorator contentDecorator;

		private QueryManagerWrapper(final QueryManager wrapped, final ContentDecorator contentDecorator) {
			this.wrapped = wrapped;
			this.contentDecorator = contentDecorator;
		}

		@Override
		public Query createQuery(final String statement, final String language) throws RepositoryException {
			return new QueryWrapper(wrapped.createQuery(statement, language), contentDecorator);
		}

		@Override
		public Query getQuery(final Node node) throws RepositoryException {
			return new QueryWrapper(wrapped.getQuery(node), contentDecorator);
		}
	}

	private static class QueryWrapper implements Query {
		private interface Execute {
			QueryResult execute();
		}

		@Delegate(excludes = QueryWrapper.Execute.class)
		private final Query wrapped;
		private final ContentDecorator contentDecorator;

		private QueryWrapper(final Query wrapped, final ContentDecorator contentDecorator) {
			this.wrapped = wrapped;
			this.contentDecorator = contentDecorator;
		}

		@Override
		public QueryResult execute() throws InvalidQueryException, RepositoryException {
			return new QueryResultWrapper(wrapped.execute(), contentDecorator);
		}
	}

	private static class QueryResultWrapper implements QueryResult {
		private interface Iterator {
			RowIterator getRows();
			NodeIterator getNodes();
		}

		@Delegate(excludes = Iterator.class)
		private final QueryResult wrapped;
		private final ContentDecorator contentDecorator;

		private QueryResultWrapper(final QueryResult wrapped, final ContentDecorator contentDecorator) {
			this.wrapped = wrapped;
			this.contentDecorator = contentDecorator;
		}

		@Override
		public RowIterator getRows() throws RepositoryException {
			return new RowIteratorAdapter(wrapped.getRows()) {
				@Override
				public Row nextRow() {
					return new RowWrapper(super.nextRow(), contentDecorator);
				}
			};
		}

		@Override
		public NodeIterator getNodes() throws RepositoryException {
			return new WrappingNodeIterator(wrapped.getNodes(), contentDecorator);
		}
	}

	private static class RowWrapper implements Row {
		private interface Iterator {
			Node getNode();
			Node getNode(String selectorName);
		}

		@Delegate(excludes = RowWrapper.Iterator.class)
		private final Row wrapped;
		private final ContentDecorator contentDecorator;

		private RowWrapper(final Row wrapped, final ContentDecorator contentDecorator) {
			this.wrapped = wrapped;
			this.contentDecorator = contentDecorator;
		}

		@Override
		public Node getNode() throws RepositoryException {
			return contentDecorator.wrapNode(wrapped.getNode());
		}

		@Override
		public Node getNode(final String selectorName) throws RepositoryException {
			return contentDecorator.wrapNode(wrapped.getNode(selectorName));
		}
	}
}


 Comments   
Comment by Viet Nguyen [ 25/Sep/23 ]

This could also be an option instead of applying above big change:

public abstract class AbstractQueryContentDecorator extends AbstractContentDecorator {
	private final boolean wrapQueryResult;

	public AbstractQueryContentDecorator(final boolean wrapQueryResult) {
		this.wrapQueryResult = wrapQueryResult;
	}

	@Override
	public Workspace wrapWorkspace(final Workspace workspace) {
		if(wrapQueryResult) {
			return new QueryContentDecoratorWorkspaceWrapper(workspace, this);
		}
		return super.wrapWorkspace(workspace);
	}
	...
}
Generated at Mon Feb 12 04:38:40 CET 2024 using Jira 9.4.2#940002-sha1:46d1a51de284217efdcb32434eab47a99af2938b.