ACS-8843 Investigate Java 21 compatibility (#2960)

* [ACS-8843] Delegate creation of proxy to one place

Co-authored-by: Kacper Magdziarz <kacper.magdziarz@hyland.com>
This commit is contained in:
Piotr Żurek
2024-10-04 14:31:56 +02:00
committed by GitHub
parent 60a31112ea
commit 674fa8d7e0
3 changed files with 327 additions and 275 deletions

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -23,133 +23,127 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.permissions; package org.alfresco.repo.security.permissions;
import java.util.Collection; import java.util.Collection;
import org.springframework.aop.IntroductionAdvisor; import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
/** * Interface for collection-based results that describe permission filtering behaviour around cut-off limits.
* Interface for collection-based results that describe permission filtering *
* behaviour around cut-off limits. * @author Derek Hulley
* * @since 4.0
* @author Derek Hulley */
* @since 4.0 public interface PermissionCheckCollection<T>
*/ {
public interface PermissionCheckCollection<T> /**
{ * Get the desired number of results. Permission checks can stop once the number of return objects reaches this number.
/** *
* Get the desired number of results. Permission checks can stop once the number of * @return the number of results desired
* return objects reaches this number. */
* int getTargetResultCount();
* @return the number of results desired
*/ /**
int getTargetResultCount(); * Get the maximum time for permission checks to execute before cutting the results off. <br/>
* Zero: Ignore this value.
/** *
* Get the maximum time for permission checks to execute before cutting the results off. * @return the time allowed for permission checks before cutoff
* <br/>Zero: Ignore this value. */
* long getCutOffAfterTimeMs();
* @return the time allowed for permission checks before cutoff
*/ /**
long getCutOffAfterTimeMs(); * Get the maximum number of permission checks to perform before cutting the results off
*
/** * @return the maximum number of permission checks before cutoff
* Get the maximum number of permission checks to perform before cutting the results off */
* int getCutOffAfterCount();
* @return the maximum number of permission checks before cutoff
*/ /**
int getCutOffAfterCount(); * Helper 'introduction' to allow simple addition of the {@link PermissionCheckCollection} interface to existing collections.
*
/** * @param <T>
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckCollection} interface to * the type of the <code>Collection</code> in use
* existing collections. *
* * @author Derek Hulley
* @param <T> the type of the <code>Collection</code> in use * @since 4.0
* */
* @author Derek Hulley @SuppressWarnings("serial")
* @since 4.0 class PermissionCheckCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckCollection<T>
*/ {
@SuppressWarnings("serial") private final int targetResultCount;
public static class PermissionCheckCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckCollection<T> private final long cutOffAfterTimeMs;
{ private final int cutOffAfterCount;
private final int targetResultCount;
private final long cutOffAfterTimeMs; private PermissionCheckCollectionMixin(int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
private final int cutOffAfterCount; {
super();
private PermissionCheckCollectionMixin(int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount) this.targetResultCount = targetResultCount;
{ this.cutOffAfterTimeMs = cutOffAfterTimeMs;
super(); this.cutOffAfterCount = cutOffAfterCount;
this.targetResultCount = targetResultCount; if (cutOffAfterTimeMs <= 0)
this.cutOffAfterTimeMs = cutOffAfterTimeMs; {
this.cutOffAfterCount = cutOffAfterCount; cutOffAfterTimeMs = 0;
if (cutOffAfterTimeMs <= 0) }
{ if (cutOffAfterCount <= 0)
cutOffAfterTimeMs = 0; {
} cutOffAfterCount = 0;
if (cutOffAfterCount <= 0) }
{ }
cutOffAfterCount = 0;
} @Override
} public int getTargetResultCount()
{
@Override return targetResultCount;
public int getTargetResultCount() }
{
return targetResultCount; @Override
} public long getCutOffAfterTimeMs()
{
@Override return cutOffAfterTimeMs;
public long getCutOffAfterTimeMs() }
{
return cutOffAfterTimeMs; @Override
} public int getCutOffAfterCount()
{
@Override return cutOffAfterCount;
public int getCutOffAfterCount() }
{
return cutOffAfterCount; /**
} * Helper method to create a {@link PermissionCheckCollection} from an existing <code>Collection</code>
*
/** * @param <TT>
* Helper method to create a {@link PermissionCheckCollection} from an existing <code>Collection</code> * the type of the <code>Collection</code>
* * @param collection
* @param <TT> the type of the <code>Collection</code> * the <code>Collection</code> to proxy
* @param collection the <code>Collection</code> to proxy * @param targetResultCount
* @param targetResultCount the desired number of results or default to the collection size * the desired number of results or default to the collection size
* @param cutOffAfterTimeMs the number of milliseconds to wait before cut-off or zero to use the system default * @param cutOffAfterTimeMs
* time-based cut-off. * the number of milliseconds to wait before cut-off or zero to use the system default time-based cut-off.
* @param cutOffAfterCount the number of permission checks to process before cut-off or zero to use the system default * @param cutOffAfterCount
* count-based cut-off. * the number of permission checks to process before cut-off or zero to use the system default count-based cut-off.
* @return a <code>Collection</code> of the same type but including the * @return a <code>Collection</code> of the same type but including the {@link PermissionCheckCollection} interface
* {@link PermissionCheckCollection} interface */
*/ @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") public static <TT> Collection<TT> create(
public static final <TT> Collection<TT> create( Collection<TT> collection,
Collection<TT> collection, int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount) {
{ if (targetResultCount <= 0)
if (targetResultCount <= 0) {
{ targetResultCount = collection.size();
targetResultCount = collection.size(); }
} // Create the mixin
// Create the mixin DelegatingIntroductionInterceptor mixin = new PermissionCheckCollectionMixin<>(
DelegatingIntroductionInterceptor mixin = new PermissionCheckCollectionMixin<Integer>( targetResultCount,
targetResultCount, cutOffAfterTimeMs,
cutOffAfterTimeMs, cutOffAfterCount);
cutOffAfterCount); // Create the advisor
// Create the advisor IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckCollection.class);
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckCollection.class); // Create Proxy
// Proxy return (Collection<TT>) ProxyFactoryUtils.createProxy(collection, advisor);
ProxyFactory pf = new ProxyFactory(collection); }
pf.addAdvisor(advisor); }
Object proxiedObject = pf.getProxy(); }
// Done
return (Collection<TT>) proxiedObject;
}
}
}

View File

@@ -2,161 +2,160 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.permissions; package org.alfresco.repo.security.permissions;
import java.util.Collection; import java.util.Collection;
import org.springframework.aop.IntroductionAdvisor; import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
/** * Interface for collection-based results that carry extra information about the state of permission cut-offs.
* Interface for collection-based results that carry extra information *
* about the state of permission cut-offs. * @author Derek Hulley
* * @since 4.0
* @author Derek Hulley */
* @since 4.0 public interface PermissionCheckedCollection<T>
*/ {
public interface PermissionCheckedCollection<T> /**
{ * Check if the results have been truncated by permission check limits.
/** *
* Check if the results have been truncated by permission check limits. * @return <tt>true</tt> - if the results (usually a collection) have been cut off by permission check limits
* */
* @return <tt>true</tt> - if the results (usually a collection) have been boolean isCutOff();
* cut off by permission check limits
*/ /**
boolean isCutOff(); * Get the number of objects in the original (unfiltered) collection that did <b>not</b> have any permission checks.
*
/** * @return number of entries from the original collection that were not checked
* Get the number of objects in the original (unfiltered) collection that did */
* <b>not</b> have any permission checks. int sizeUnchecked();
*
* @return number of entries from the original collection that were not checked /**
*/ * Get the number of objects in the original (unfiltered) collection.
int sizeUnchecked(); *
* @return number of entries in the original, pre-checked collection
/** */
* Get the number of objects in the original (unfiltered) collection. int sizeOriginal();
*
* @return number of entries in the original, pre-checked collection /**
*/ * Helper 'introduction' to allow simple addition of the {@link PermissionCheckedCollection} interface to existing collections.
int sizeOriginal(); *
* @param <T>
/** * the type of the <code>Collection</code> in use
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckedCollection} interface to *
* existing collections. * @author Derek Hulley
* * @since 4.0
* @param <T> the type of the <code>Collection</code> in use */
* @SuppressWarnings("serial")
* @author Derek Hulley class PermissionCheckedCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckedCollection<T>
* @since 4.0 {
*/ private final boolean isCutOff;
@SuppressWarnings("serial") private final int sizeUnchecked;
public static class PermissionCheckedCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckedCollection<T> private final int sizeOriginal;
{
private final boolean isCutOff; private PermissionCheckedCollectionMixin(boolean isCutOff, int sizeUnchecked, int sizeOriginal)
private final int sizeUnchecked; {
private final int sizeOriginal; super();
private PermissionCheckedCollectionMixin(boolean isCutOff, int sizeUnchecked, int sizeOriginal) this.isCutOff = isCutOff;
{ this.sizeUnchecked = sizeUnchecked;
super(); this.sizeOriginal = sizeOriginal;
this.isCutOff = isCutOff; }
this.sizeUnchecked = sizeUnchecked;
this.sizeOriginal = sizeOriginal; @Override
} public boolean isCutOff()
@Override {
public boolean isCutOff() return isCutOff;
{ }
return isCutOff;
} @Override
@Override public int sizeUnchecked()
public int sizeUnchecked() {
{ return sizeUnchecked;
return sizeUnchecked; }
}
@Override @Override
public int sizeOriginal() public int sizeOriginal()
{ {
return sizeOriginal; return sizeOriginal;
} }
/**
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code> /**
* by applying the same values as present on a potentially permission-checked source. If the * Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code> by applying the same values as present on a potentially permission-checked source. If the existing checked source is <b>NOT</b> permission-checked, then the collection will not be decorated.
* existing checked source is <b>NOT</b> permission-checked, then the collection will not be *
* decorated. * @param <TT>
* * the type of the <code>Collection</code>
* @param <TT> the type of the <code>Collection</code> * @param collection
* @param collection the <code>Collection</code> to proxy * the <code>Collection</code> to proxy
* @param checkedSource a collection that might implement {@link PermissionCheckedCollection} * @param checkedSource
* @return a <code>Collection</code> of the same type but including the * a collection that might implement {@link PermissionCheckedCollection}
* {@link PermissionCheckedCollection} interface * @return a <code>Collection</code> of the same type but including the {@link PermissionCheckedCollection} interface
*/ */
public static final <TT> Collection<TT> create( public static <TT> Collection<TT> create(
Collection<TT> collection, Collection<?> checkedSource) Collection<TT> collection, Collection<?> checkedSource)
{ {
if (checkedSource instanceof PermissionCheckedCollection) if (checkedSource instanceof PermissionCheckedCollection)
{ {
PermissionCheckedCollection<?> source = (PermissionCheckedCollection<?>) checkedSource; PermissionCheckedCollection<?> source = (PermissionCheckedCollection<?>) checkedSource;
return create(collection, source.isCutOff(), source.sizeUnchecked(), source.sizeOriginal()); return create(collection, source.isCutOff(), source.sizeUnchecked(), source.sizeOriginal());
} }
else else
{ {
return collection; return collection;
} }
} }
/**
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code> /**
* * Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code>
* @param <TT> the type of the <code>Collection</code> *
* @param collection the <code>Collection</code> to proxy * @param <TT>
* @param isCutOff <tt>true</tt> if permission checking was cut off before completion * the type of the <code>Collection</code>
* @param sizeUnchecked number of entries from the original collection that were not checked * @param collection
* @param sizeOriginal number of entries in the original, pre-checked collection * the <code>Collection</code> to proxy
* @return a <code>Collection</code> of the same type but including the * @param isCutOff
* {@link PermissionCheckedCollection} interface * <tt>true</tt> if permission checking was cut off before completion
*/ * @param sizeUnchecked
@SuppressWarnings("unchecked") * number of entries from the original collection that were not checked
public static final <TT> Collection<TT> create( * @param sizeOriginal
Collection<TT> collection, * number of entries in the original, pre-checked collection
boolean isCutOff, int sizeUnchecked, int sizeOriginal) * @return a <code>Collection</code> of the same type but including the {@link PermissionCheckedCollection} interface
{ */
// Create the mixin @SuppressWarnings("unchecked")
DelegatingIntroductionInterceptor mixin = new PermissionCheckedCollectionMixin<Integer>( public static <TT> Collection<TT> create(
isCutOff, Collection<TT> collection,
sizeUnchecked, boolean isCutOff, int sizeUnchecked, int sizeOriginal)
sizeOriginal {
); // Create the mixin
// Create the advisor DelegatingIntroductionInterceptor mixin = new PermissionCheckedCollectionMixin<>(
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckedCollection.class); isCutOff,
// Proxy sizeUnchecked,
ProxyFactory pf = new ProxyFactory(collection); sizeOriginal);
pf.addAdvisor(advisor); // Create the advisor
Object proxiedObject = pf.getProxy(); IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckedCollection.class);
// Create Proxy
// Done return (Collection<TT>) ProxyFactoryUtils.createProxy(collection, advisor);
return (Collection<TT>) proxiedObject; }
} }
} }
}

View File

@@ -0,0 +1,59 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.security.permissions;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;
class ProxyFactoryUtils
{
private ProxyFactoryUtils()
{}
/**
* Delegate creation of {@link ProxyFactory} and proxy to have control over it in one place.
*
* @param collection
* given collection for ProxyFactory.
* @param advisor
* given advisor for ProxyFactory.
* @return the proxy object.
*/
protected static Object createProxy(Collection<?> collection, IntroductionAdvisor advisor)
{
ProxyFactory pf = new ProxyFactory(collection);
pf.addAdvisor(advisor);
if (pf.isInterfaceProxied(List.class) && pf.isInterfaceProxied(Deque.class))
{
pf.removeInterface(Deque.class);
}
return pf.getProxy();
}
}