/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.schema; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.GetOperationOptionsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import org.apache.commons.collections.CollectionUtils; import javax.xml.namespace.QName; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; /** * @author semancik * */ public class GetOperationOptions extends AbstractOptions implements Serializable, Cloneable { private static final long serialVersionUID = 1L; /** * Specifies whether to return specific items. It is used for optimizations. * Some requests only needs a subset of items therefore fetching them all is a waste * of resources. Other requests may need expensive data that are not normally returned by default. * <p> * If no retrieve option is set in the entire options set then it * means that the whole object with a default set of properties has to be * returned. This is equivalent to specifying DEFAULT retrieve root option. * <p> * If there is at least one retrieve option in the set then the following rules apply: * <ul> * <li>Items marked as INCLUDE will be returned.</li> * <li>Any item marked as EXCLUDE may not be returned. (Note: Excluded items may still be returned if their retrieval is cheap.)</li> * <li>Items marked as DEFAULT will be returned if they would also be returned without any options (by default).</li> * <li>Items that are not marked (have no option or have null retrieve option) but their superitem is marked (have retrieve option) * behave in the same way as superitem. E.g. if a superitem is marked as * INCLUDE they will also be included in the result. This also applies transitively (e.g. superitem of superitem). * <li>If a superitem is marked as EXCLUDE and subitem is marked as INCLUDE then the behavior is undefined. Do not do this. Strange things may happen.</li> * <li>For items that are not marked in any way and for which the superitem is also not marked the "I do not care" behavior is assumed. * This means that they may be returned or they may be not. The implementation will return them if their retrieval is cheap * but they will be most likely omitted from the result.</li> * </ul> */ private RetrieveOption retrieve; /** * Resolve the object reference. This only makes sense with a (path-based) selector. */ private Boolean resolve; /** * Resolve the object reference names. (Currently applicable only as a top-level option.) * * EXPERIMENTAL. */ private Boolean resolveNames; /** * No not fetch any information from external sources, e.g. do not fetch account data from resource, * do not fetch resource schema, etc. * Such operation returns only the data stored in midPoint repository. */ private Boolean noFetch; /** * Avoid any smart processing of the data except for schema application. Do not synchronize the data, do not apply * any expressions, etc. */ private Boolean raw; /** * Tolerate "raw" data in returned object. In some cases, raw data are tolerated by default (e.g. if raw=true * and the object is ResourceType or ShadowType). But generally, toleration of raw data can be explicitly requested * by setting this flag to TRUE. */ private Boolean tolerateRawData; /** * Force to get object from the resource even if some of the error occurred. * If the any copy of the shadow is fetched, we can't delete this object * from the gui, for example */ private Boolean doNotDiscovery; private RelationalValueSearchQuery relationalValueSearchQuery; /** * This flag indicated if the "object not found" error is critical for * processing the original request. If it is not, we just ignore it and * don't log it. In other cases, error in logs may lead to misleading * information.. */ private Boolean allowNotFound; /** * Return read-only object. The returned object will be only read by the client. The client will not modify it. * Immutable object is returned if it is possible. * This option allows to turn on internal optimization (e.g. avoid cloning the values). It should be used * at all times when the client do not plan to modify the returned object. */ private Boolean readOnly; /** * Specifies the point in time for the returned data. This option controls whether fresh or cached data will * be returned or whether future data projection will be returned. MidPoint usually deals with fresh data * that describe situation at the current point in time. But the client code may want to get data from the * cache that may be possibly stale. Or the client code may want a projection about the future state of the * data (e.g. taking running asynchronous operation into consideration). * If this option is not specified then the current point in time is the default if no staleness option is * specified or if it is zero. If non-zero staleness option is specified then this option defaults to cached * data. */ private PointInTimeType pointInTimeType; /** * Requirement how stale or fresh the retrieved data should be. It specifies maximum age of the value in millisecods. * The default value is zero, which means that a fresh value must always be returned. This means that caches that do * not guarantee fresh value cannot be used. If non-zero value is specified then such caches may be used. In case that * Long.MAX_VALUE is specified then the caches are always used and fresh value is never retrieved. */ private Long staleness; /** * Should the results be made distinct. * Not all providers support this option. * * BEWARE: * - may bring a potentially huge performance penalty * - may interfere with paging (!) * * So please consider this option an EXPERIMENTAL, for now. */ private Boolean distinct; public RetrieveOption getRetrieve() { return retrieve; } public void setRetrieve(RetrieveOption retrieve) { this.retrieve = retrieve; } public static RetrieveOption getRetrieve(GetOperationOptions options) { if (options == null) { return null; } return options.retrieve; } public static GetOperationOptions createRetrieve(RetrieveOption retrieve) { GetOperationOptions options = new GetOperationOptions(); options.retrieve = retrieve; return options; } public static GetOperationOptions createRetrieve() { return createRetrieve(RetrieveOption.INCLUDE); } public static GetOperationOptions createDontRetrieve() { return createRetrieve(RetrieveOption.EXCLUDE); } public static GetOperationOptions createRetrieve(RelationalValueSearchQuery query) { GetOperationOptions options = new GetOperationOptions(); options.retrieve = RetrieveOption.INCLUDE; options.setRelationalValueSearchQuery(query); return options; } public Boolean getResolve() { return resolve; } public void setResolve(Boolean resolve) { this.resolve = resolve; } public static boolean isResolve(GetOperationOptions options) { if (options == null) { return false; } if (options.resolve == null) { return false; } return options.resolve; } public static GetOperationOptions createResolve() { GetOperationOptions opts = new GetOperationOptions(); opts.setResolve(true); return opts; } // TODO exact placement of this method public static Collection<SelectorOptions<GetOperationOptions>> resolveItemsNamed(Object... items) { Collection<SelectorOptions<GetOperationOptions>> rv = new ArrayList<>(items.length); for (Object item : items) { rv.add(SelectorOptions.create(pathForItem(item), createResolve())); } return rv; } public static Collection<SelectorOptions<GetOperationOptions>> retrieveItemsNamed(Object... items) { Collection<SelectorOptions<GetOperationOptions>> rv = new ArrayList<>(items.length); for (Object item : items) { rv.add(SelectorOptions.create(pathForItem(item), createRetrieve())); } return rv; } protected static ItemPath pathForItem(Object item) { final ItemPath path; if (item instanceof QName) { path = new ItemPath((QName) item); } else if (item instanceof ItemPath) { path = ((ItemPath) item); } else { throw new IllegalArgumentException("item has to be QName or ItemPath but is " + item); } return path; } public Boolean getNoFetch() { return noFetch; } public void setNoFetch(Boolean noFetch) { this.noFetch = noFetch; } public static boolean isNoFetch(GetOperationOptions options) { if (options == null) { return false; } if (options.noFetch == null) { return false; } return options.noFetch; } public static GetOperationOptions createNoFetch() { GetOperationOptions opts = new GetOperationOptions(); opts.setNoFetch(true); return opts; } // todo maybe at wrong place, but this might be quite useful public static Collection<SelectorOptions<GetOperationOptions>> createRetrieveNameOnlyOptions() { return SelectorOptions.createCollection(new ItemPath(ObjectType.F_NAME), createRetrieve(RetrieveOption.INCLUDE)); } public static Collection<SelectorOptions<GetOperationOptions>> createRetrieveAttributesOptions(QName... properties) { Collection<SelectorOptions<GetOperationOptions>> optionsCollection = new ArrayList<>(properties.length); for (QName property : properties) { optionsCollection.add(SelectorOptions.create(new ItemPath(property), createRetrieve(RetrieveOption.INCLUDE))); } return optionsCollection; } public Boolean getResolveNames() { return resolveNames; } public void setResolveNames(Boolean resolveNames) { this.resolveNames = resolveNames; } public static boolean isResolveNames(GetOperationOptions options) { if (options == null) { return false; } if (options.resolveNames == null) { return false; } return options.resolveNames; } public static GetOperationOptions createResolveNames() { GetOperationOptions opts = new GetOperationOptions(); opts.setResolveNames(true); return opts; } public Boolean getTolerateRawData() { return tolerateRawData; } public void setTolerateRawData(Boolean value) { this.tolerateRawData = value; } public static boolean isTolerateRawData(GetOperationOptions options) { if (options == null) { return false; } if (options.tolerateRawData == null) { return false; } return options.tolerateRawData; } public static GetOperationOptions createTolerateRawData() { GetOperationOptions opts = new GetOperationOptions(); opts.setTolerateRawData(true); return opts; } public Boolean getRaw() { return raw; } public void setRaw(Boolean raw) { this.raw = raw; } public static boolean isRaw(GetOperationOptions options) { if (options == null) { return false; } if (options.raw == null) { return false; } return options.raw; } public static GetOperationOptions createRaw() { GetOperationOptions opts = new GetOperationOptions(); opts.setRaw(true); return opts; } public static Collection<SelectorOptions<GetOperationOptions>> createRawCollection() { return SelectorOptions.createCollection(createRaw()); } public static Collection<SelectorOptions<GetOperationOptions>> createNoFetchCollection() { return SelectorOptions.createCollection(createNoFetch()); } public Boolean getDoNotDiscovery() { return doNotDiscovery; } public void setDoNotDiscovery(Boolean force) { this.doNotDiscovery = force; } public static boolean isDoNotDiscovery(GetOperationOptions options) { if (options == null) { return false; } if (options.doNotDiscovery == null) { return false; } return options.doNotDiscovery; } public static GetOperationOptions createDoNotDiscovery() { GetOperationOptions opts = new GetOperationOptions(); opts.setDoNotDiscovery(true); return opts; } public static GetOperationOptions createAllowNotFound() { GetOperationOptions opts = new GetOperationOptions(); opts.setAllowNotFound(true); return opts; } public Boolean getAllowNotFound() { return allowNotFound; } public void setAllowNotFound(Boolean allowNotFound) { this.allowNotFound = allowNotFound; } public static boolean isAllowNotFound(GetOperationOptions options) { if (options == null) { return false; } if (options.allowNotFound == null) { return false; } return options.allowNotFound; } public static GetOperationOptions createReadOnly() { GetOperationOptions opts = new GetOperationOptions(); opts.setReadOnly(true); return opts; } public Boolean getReadOnly() { return readOnly; } public void setReadOnly(Boolean readOnly) { this.readOnly = readOnly; } public static boolean isReadOnly(GetOperationOptions options) { if (options == null) { return false; } if (options.readOnly == null) { return false; } return options.readOnly; } public PointInTimeType getPointInTimeType() { return pointInTimeType; } public void setPointInTimeType(PointInTimeType pointInTimeType) { this.pointInTimeType = pointInTimeType; } public static GetOperationOptions createPointInTimeType(PointInTimeType pit) { GetOperationOptions opts = new GetOperationOptions(); opts.setPointInTimeType(pit); return opts; } public static PointInTimeType getPointInTimeType(GetOperationOptions options) { if (options == null) { return null; } if (options.getPointInTimeType() == null) { return null; } return options.getPointInTimeType(); } public Long getStaleness() { return staleness; } public void setStaleness(Long staleness) { this.staleness = staleness; } public static GetOperationOptions createStaleness(Long staleness) { GetOperationOptions opts = new GetOperationOptions(); opts.setStaleness(staleness); return opts; } public static GetOperationOptions createMaxStaleness() { GetOperationOptions opts = new GetOperationOptions(); opts.setStaleness(Long.MAX_VALUE); return opts; } public static long getStaleness(GetOperationOptions options) { if (options == null) { return 0L; } if (options.getStaleness() == null) { return 0L; } return options.getStaleness(); } public static boolean isMaxStaleness(GetOperationOptions options) { return GetOperationOptions.getStaleness(options) == Long.MAX_VALUE; } public Boolean getDistinct() { return distinct; } public void setDistinct(Boolean distinct) { this.distinct = distinct; } public static boolean isDistinct(GetOperationOptions options) { if (options == null) { return false; } if (options.distinct == null) { return false; } return options.distinct; } public static GetOperationOptions createDistinct() { GetOperationOptions opts = new GetOperationOptions(); opts.setDistinct(true); return opts; } public RelationalValueSearchQuery getRelationalValueSearchQuery() { return relationalValueSearchQuery; } public void setRelationalValueSearchQuery(RelationalValueSearchQuery relationalValueSearchQuery) { this.relationalValueSearchQuery = relationalValueSearchQuery; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof GetOperationOptions)) return false; GetOperationOptions that = (GetOperationOptions) o; return retrieve == that.retrieve && Objects.equals(resolve, that.resolve) && Objects.equals(resolveNames, that.resolveNames) && Objects.equals(noFetch, that.noFetch) && Objects.equals(raw, that.raw) && Objects.equals(tolerateRawData, that.tolerateRawData) && Objects.equals(doNotDiscovery, that.doNotDiscovery) && Objects.equals(relationalValueSearchQuery, that.relationalValueSearchQuery) && Objects.equals(allowNotFound, that.allowNotFound) && Objects.equals(readOnly, that.readOnly) && Objects.equals(pointInTimeType, that.pointInTimeType) && Objects.equals(staleness, that.staleness) && Objects.equals(distinct, that.distinct); } @Override public int hashCode() { return Objects .hash(retrieve, resolve, resolveNames, noFetch, raw, tolerateRawData, doNotDiscovery, relationalValueSearchQuery, allowNotFound, readOnly, staleness, distinct); } public GetOperationOptions clone() { GetOperationOptions clone = new GetOperationOptions(); clone.noFetch = this.noFetch; clone.doNotDiscovery = this.doNotDiscovery; clone.raw = this.raw; clone.resolve = this.resolve; clone.resolveNames = this.resolveNames; clone.retrieve = this.retrieve; clone.allowNotFound = this.allowNotFound; clone.readOnly = this.readOnly; clone.pointInTimeType = this.pointInTimeType; clone.staleness = this.staleness; clone.distinct = this.distinct; if (this.relationalValueSearchQuery != null) { clone.relationalValueSearchQuery = this.relationalValueSearchQuery.clone(); } return clone; } @Override public String toString() { StringBuilder sb = new StringBuilder("GetOperationOptions("); appendFlag(sb, "resolve", resolve); appendFlag(sb, "resolveNames", resolveNames); appendFlag(sb, "noFetch", noFetch); appendFlag(sb, "raw", raw); appendFlag(sb, "doNotDiscovery", doNotDiscovery); appendVal(sb, "retrieve", retrieve); appendFlag(sb, "allowNotFound", allowNotFound); appendFlag(sb, "readOnly", readOnly); appendVal(sb, "pointInTimeType", pointInTimeType); appendVal(sb, "staleness", staleness); appendVal(sb, "distinct", distinct); appendVal(sb, "relationalValueSearchQuery", relationalValueSearchQuery); removeLastComma(sb); sb.append(")"); return sb.toString(); } public static Collection<SelectorOptions<GetOperationOptions>> fromRestOptions(List<String> options, List<String> include, List<String> exclude) { if (CollectionUtils.isEmpty(options) && CollectionUtils.isEmpty(include) && CollectionUtils.isEmpty(exclude)) { return null; } Collection<SelectorOptions<GetOperationOptions>> rv = new ArrayList<>(); GetOperationOptions rootOptions = fromRestOptions(options); if (rootOptions != null) { rv.add(SelectorOptions.create(rootOptions)); } for (ItemPath includePath : ItemPath.fromStringList(include)) { rv.add(SelectorOptions.create(includePath, GetOperationOptions.createRetrieve())); } for (ItemPath excludePath : ItemPath.fromStringList(exclude)) { rv.add(SelectorOptions.create(excludePath, GetOperationOptions.createDontRetrieve())); } return rv; } public static GetOperationOptions fromRestOptions(List<String> options) { if (options == null || options.isEmpty()){ return null; } GetOperationOptions rv = new GetOperationOptions(); for (String option : options) { if (GetOperationOptionsType.F_RAW.getLocalPart().equals(option)) { rv.setRaw(true); } if (GetOperationOptionsType.F_NO_FETCH.getLocalPart().equals(option)) { rv.setNoFetch(true); } if (GetOperationOptionsType.F_NO_DISCOVERY.getLocalPart().equals(option)) { rv.setDoNotDiscovery(true); } if (GetOperationOptionsType.F_RESOLVE_NAMES.getLocalPart().equals(option)) { rv.setResolveNames(true); } } return rv; } }