/* * Copyright 2015 Robert von Burg <eitch@eitchnet.ch> * * 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 li.strolch.policy; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import java.util.Map; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchComponent; import li.strolch.exception.StrolchPolicyException; import li.strolch.model.policy.JavaPolicyDef; import li.strolch.model.policy.KeyPolicyDef; import li.strolch.model.policy.PolicyDef; import li.strolch.model.policy.PolicyDefVisitor; import li.strolch.persistence.api.StrolchTransaction; import li.strolch.policy.StrolchPolicyFileParser.PolicyModel; import li.strolch.policy.StrolchPolicyFileParser.PolicyType; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.utils.collections.MapOfMaps; import li.strolch.utils.helper.XmlHelper; /** * <p> * This is the default Strolch {@link PolicyHandler} which implements {@link PolicyDefVisitor}. This * {@link PolicyHandler} parses a policy configuration file to handle the look-up of {@link KeyPolicyDef} * </p> * * This {@link StrolchComponent} uses two configuration properties: * <ul> * <li>{@link #PROP_READ_POLICY_FILE} - Default is false. If false then no configuration file is read. Useful if all * policy definitions are {@link JavaPolicyDef}</li> * <li>{@link #PROP_POLICY_CONFIG} - Default is {@link #DEF_STROLCH_POLICIES_XML}. If {@link #PROP_READ_POLICY_FILE} is * true, then this property is used to determine which configuration file to parse for the policy key mappings for * {@link KeyPolicyDef}</li> * </ul> * * @author Robert von Burg <eitch@eitchnet.ch> */ public class DefaultPolicyHandler extends StrolchComponent implements PolicyHandler, PolicyDefVisitor { private static final String PROP_POLICY_CONFIG = "policyConfig"; private static final String PROP_READ_POLICY_FILE = "readPolicyFile"; private static final String DEF_STROLCH_POLICIES_XML = "StrolchPolicies.xml"; private MapOfMaps<String, String, Class<? extends StrolchPolicy>> classByTypeMap; public DefaultPolicyHandler(ComponentContainer container, String componentName) { super(container, componentName); } @Override public void initialize(ComponentConfiguration configuration) throws Exception { if (configuration.getBoolean(PROP_READ_POLICY_FILE, Boolean.FALSE)) { File policyFile = configuration.getConfigFile(PROP_POLICY_CONFIG, DEF_STROLCH_POLICIES_XML, configuration.getRuntimeConfiguration()); parsePolicyFile(policyFile); } else { logger.warn("Not loading Policy configuration file, as disabled by config"); } super.initialize(configuration); } @Override public <T extends StrolchPolicy> T getPolicy(PolicyDef policyDef, StrolchTransaction tx) { try { Class<T> clazz = policyDef.accept(this); Constructor<T> constructor = clazz.getConstructor(ComponentContainer.class, StrolchTransaction.class); return constructor.newInstance(getContainer(), tx); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) { throw new StrolchPolicyException( MessageFormat.format("Failed to instantiate policy {0} due to {1}", policyDef, e.getMessage()), e); } } @Override public <T> Class<T> visit(JavaPolicyDef policyDef) throws ClassNotFoundException { String value = policyDef.getValue(); @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) Class.forName(value); return clazz; } @SuppressWarnings("unchecked") @Override public <T> Class<T> visit(KeyPolicyDef policyDef) throws ClassNotFoundException { Class<?> clazz = this.classByTypeMap.getElement(policyDef.getType(), policyDef.getValue()); if (clazz == null) throw new StrolchPolicyException(MessageFormat.format("No policy is configured for {0}", policyDef)); return (Class<T>) clazz; } @SuppressWarnings("unchecked") private void parsePolicyFile(File policyFile) { // first we parse the file StrolchPolicyFileParser xmlHandler = new StrolchPolicyFileParser(); XmlHelper.parseDocument(policyFile, xmlHandler); PolicyModel policyModel = xmlHandler.getPolicyModel(); // then we iterate the parsed model, validating that we can: // - access the API class // - instantiate the class // - assign the class to the API Map<String, PolicyType> policyTypes = policyModel.getPolicyTypes(); this.classByTypeMap = new MapOfMaps<>(); for (PolicyType policyType : policyTypes.values()) { String type = policyType.getType(); String api = policyType.getApi(); try { // get the API class Class<?> apiClass = Class.forName(api); // now iterate all configured policies for this API Map<String, String> policyByKeyMap = policyType.getPolicyByKeyMap(); for (String key : policyByKeyMap.keySet()) { String className = policyByKeyMap.get(key); try { // get the class Class<?> implClass = Class.forName(className); // assert API is a Policy if (!StrolchPolicy.class.isAssignableFrom(implClass)) { throw new StrolchPolicyException("Invalid " + StrolchPolicyFileParser.POLICY + " configuration for Type=" + type + " Key=" + key + " as " + className + " is not a " + StrolchPolicy.class.getName()); } if (!apiClass.isAssignableFrom(implClass)) { throw new StrolchPolicyException( "Invalid " + StrolchPolicyFileParser.POLICY + " configuration for Type=" + type + " Key=" + key + " as " + className + " is not assignable from " + api); } // store the implementation class logger.info("Loaded Policy " + type + " / " + key + " / " + className); this.classByTypeMap.addElement(type, key, (Class<? extends StrolchPolicy>) implClass); } catch (ClassNotFoundException e) { throw new StrolchPolicyException("Invalid " + StrolchPolicyFileParser.POLICY + " configuration for Type=" + type + " Key=" + key + " due to " + e.getMessage(), e); } } } catch (ClassNotFoundException e) { throw new StrolchPolicyException("Invalid " + StrolchPolicyFileParser.POLICY_TYPE + " configuration for Type=" + type + " due to " + e.getMessage(), e); } } } }