/* * Copyright (C) 2014-2015 University of Dundee & Open Microscopy Environment. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package omero.cmd.graphs; import java.lang.reflect.Constructor; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; import ome.model.IObject; import ome.security.ACLVoter; import ome.security.SystemTypes; import ome.services.delete.Deletion; import ome.services.graphs.GraphException; import ome.services.graphs.GraphPathBean; import ome.services.graphs.GraphPolicy; import ome.services.graphs.GraphPolicyRule; import ome.system.Roles; import omero.cmd.GraphModify2; import omero.cmd.Request; import omero.cmd.SkipHead; /** * Create request objects that are executed using the {@link ome.services.graphs.GraphPathBean}. * @author m.t.b.carroll@dundee.ac.uk * @since 5.1.0 */ public class GraphRequestFactory { private static final Logger LOGGER = LoggerFactory.getLogger(GraphRequestFactory.class); private final ACLVoter aclVoter; private final Roles securityRoles; private final SystemTypes systemTypes; private final GraphPathBean graphPathBean; private final Deletion deletionInstance; private final ImmutableSetMultimap<Class<? extends Request>, Class<? extends IObject>> allTargets; private final ImmutableMap<Class<? extends Request>, GraphPolicy> graphPolicies; private final ImmutableSetMultimap<String, String> unnullable; private final ImmutableSet<String> defaultExcludeNs; /** * Construct a new graph request factory. * @param aclVoter ACL voter for permissions checking * @param securityRoles the security roles * @param systemTypes for identifying the system types * @param graphPathBean the graph path bean * @param deletionInstance a deletion instance for deleting files * @param allTargets legal target object classes for all request classes that use the graph path bean * @param allRules rules for all request classes that use the graph path bean * @param unnullable properties that, while nullable, may not be nulled by a graph traversal operation * @param defaultExcludeNs the default value for an unset excludeNs field * @throws GraphException if the graph path rules could not be parsed */ public GraphRequestFactory(ACLVoter aclVoter, Roles securityRoles, SystemTypes systemTypes, GraphPathBean graphPathBean, Deletion deletionInstance, Map<Class<? extends Request>, List<String>> allTargets, Map<Class<? extends Request>, List<GraphPolicyRule>> allRules, List<String> unnullable, Set<String> defaultExcludeNs) throws GraphException { this.aclVoter = aclVoter; this.securityRoles = securityRoles; this.systemTypes = systemTypes; this.graphPathBean = graphPathBean; this.deletionInstance = deletionInstance; final ImmutableSetMultimap.Builder<Class<? extends Request>, Class<? extends IObject>> allTargetsBuilder = ImmutableSetMultimap.builder(); for (final Map.Entry<Class<? extends Request>, List<String>> rules : allTargets.entrySet()) { final Class<? extends Request> requestClass = rules.getKey(); for (final String targetClassName : rules.getValue()) { allTargetsBuilder.put(requestClass, graphPathBean.getClassForSimpleName(targetClassName)); } } this.allTargets = allTargetsBuilder.build(); final ImmutableMap.Builder<Class<? extends Request>, GraphPolicy> graphPoliciesBuilder = ImmutableMap.builder(); for (final Map.Entry<Class<? extends Request>, List<GraphPolicyRule>> rules : allRules.entrySet()) { graphPoliciesBuilder.put(rules.getKey(), GraphPolicyRule.parseRules(graphPathBean, rules.getValue())); } this.graphPolicies = graphPoliciesBuilder.build(); final ImmutableSetMultimap.Builder<String, String> unnullableBuilder = ImmutableSetMultimap.builder(); for (final String classProperty : unnullable) { final int period = classProperty.indexOf('.'); final String classNameSimple = classProperty.substring(0, period); final String property = classProperty.substring(period + 1); final String classNameFull = graphPathBean.getClassForSimpleName(classNameSimple).getName(); unnullableBuilder.put(classNameFull, property); } this.unnullable = unnullableBuilder.build(); this.defaultExcludeNs = ImmutableSet.copyOf(defaultExcludeNs); } /** * @return the graph path bean used by this instance */ public GraphPathBean getGraphPathBean() { return graphPathBean; } /** * Get the legal target object classes for the given request. * @param requestClass a request class * @return the legal target object classes for that type of request */ public <R extends GraphModify2> Set<Class<? extends IObject>> getLegalTargets(Class<R> requestClass) { final Set<Class<? extends IObject>> targetClasses = allTargets.get(requestClass); if (targetClasses.isEmpty()) { throw new IllegalArgumentException("no legal target classes defined for request class " + requestClass); } return targetClasses; } /** * Construct a request. * @param requestClass a request class * @return a new instance of that class */ public <R extends GraphModify2> R getRequest(Class<R> requestClass) { final R request; try { if (SkipHead.class.isAssignableFrom(requestClass)) { final Constructor<R> constructor = requestClass.getConstructor(GraphPathBean.class, GraphRequestFactory.class); request = constructor.newInstance(graphPathBean, this); } else { final Set<Class<? extends IObject>> targetClasses = getLegalTargets(requestClass); GraphPolicy graphPolicy = graphPolicies.get(requestClass); if (graphPolicy == null) { throw new IllegalArgumentException("no graph traversal policy rules defined for request class " + requestClass); } else { graphPolicy = graphPolicy.getCleanInstance(); } final Constructor<R> constructor = requestClass.getConstructor(ACLVoter.class, Roles.class, SystemTypes.class, GraphPathBean.class, Deletion.class, Set.class, GraphPolicy.class, SetMultimap.class); request = constructor.newInstance(aclVoter, securityRoles, systemTypes, graphPathBean, deletionInstance, targetClasses, graphPolicy, unnullable); } } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException e) { throw new IllegalArgumentException("cannot instantiate " + requestClass, e); } return request; } /** * @return an uninitialized child option instance */ public ChildOption createChildOption() { return new ChildOptionI(graphPathBean, defaultExcludeNs); } }