diff options
Diffstat (limited to 'policy-management/src/main/java')
11 files changed, 3610 insertions, 3386 deletions
diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java index 7f68930e..c582a1c0 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java @@ -30,67 +30,67 @@ import org.onap.policy.drools.core.PolicyContainer; import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; /** - * Drools Controller is the abstractions that wraps the drools layer (policy-core) + * Drools Controller is the abstractions that wraps the drools layer (policy-core). */ public interface DroolsController extends Startable, Lockable { /** - * No Group ID identifier + * No Group ID identifier. */ public static final String NO_GROUP_ID = "NO-GROUP-ID"; /** - * No Artifact ID identifier + * No Artifact ID identifier. */ public static final String NO_ARTIFACT_ID = "NO-ARTIFACT-ID"; /** - * No version identifier + * No version identifier. */ public static final String NO_VERSION = "NO-VERSION"; /** - * Factory to track and manage drools controllers + * Factory to track and manage drools controllers. */ public static final DroolsControllerFactory factory = new IndexedDroolsControllerFactory(); /** - * get group id + * get group id. * * @return group id */ public String getGroupId(); /** - * get artifact id + * get artifact id. * * @return artifact id */ public String getArtifactId(); /** - * get version + * get version. * * @return version */ public String getVersion(); /** - * return the policy session names + * return the policy session names. * * @return policy session */ public List<String> getSessionNames(); /** - * return the policy full session names + * return the policy full session names. * * @return policy session */ public List<String> getCanonicalSessionNames(); /** - * offers an event to this controller for processing + * offers an event to this controller for processing. * * @param topic topic associated with the event * @param event the event @@ -100,10 +100,10 @@ public interface DroolsController extends Startable, Lockable { public boolean offer(String topic, String event); /** - * delivers "event" to "sink" + * delivers "event" to "sink". * * @param sink destination - * @param event + * @param event event * @return true if successful, false if a failure has occurred. * @throws IllegalArgumentException when invalid or insufficient properties are provided * @throws IllegalStateException when the engine is in a state where this operation is not @@ -114,32 +114,37 @@ public interface DroolsController extends Startable, Lockable { public boolean deliver(TopicSink sink, Object event); /** + * Get recent source events. * - * @return the most recent received events + * @return the most recent received events. */ public Object[] getRecentSourceEvents(); /** + * Get recent sink events. * * @return the most recent delivered events */ public String[] getRecentSinkEvents(); /** + * Get container. + * * @return the underlying policy container */ public PolicyContainer getContainer(); /** - * Supports this encoder? + * Does it owns the coder. * - * @param encodedObject - * @return + * @param coderClass the encoder object + * @param modelHash the hash for the model + * @return true it owns it */ public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash); /** - * fetches a class from the model + * fetches a class from the model. * * @param className the class to fetch * @return the actual class object, or null if not found @@ -147,12 +152,12 @@ public interface DroolsController extends Startable, Lockable { public Class<?> fetchModelClass(String className); /** - * is this controller Smart? + * is this controller Smart. */ public boolean isBrained(); /** - * update the new version of the maven jar rules file + * update the new version of the maven jar rules file. * * @param newGroupId - new group id * @param newArtifactId - new artifact id @@ -162,14 +167,13 @@ public interface DroolsController extends Startable, Lockable { * * @throws Exception from within drools libraries * @throws LinkageError from within drools libraries - * @throws ArgumentException bad parameter passed in */ public void updateToVersion(String newGroupId, String newArtifactId, String newVersion, List<TopicCoderFilterConfiguration> decoderConfigurations, List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError; /** - * gets the classnames of facts as well as the current count + * gets the classnames of facts as well as the current count. * * @param sessionName the session name * @return map of class to count @@ -177,16 +181,15 @@ public interface DroolsController extends Startable, Lockable { public Map<String, Integer> factClassNames(String sessionName); /** - * gets the count of facts for a given session + * gets the count of facts for a given session. * * @param sessionName the session name * @return the fact count - * @throws IllegalArgumentException */ public long factCount(String sessionName); /** - * gets all the facts of a given class for a given session + * gets all the facts of a given class for a given session. * * @param sessionName the session identifier * @param className the class type @@ -196,7 +199,7 @@ public interface DroolsController extends Startable, Lockable { public List<Object> facts(String sessionName, String className, boolean delete); /** - * gets the facts associated with a query for a give session for a given queried entity + * gets the facts associated with a query for a give session for a given queried entity. * * @param sessionName the session * @param queryName the query identifier @@ -209,9 +212,8 @@ public interface DroolsController extends Startable, Lockable { Object... queryParams); /** - * halts and permanently releases all resources + * halts and permanently releases all resources. * - * @throws IllegalStateException */ public void halt(); } diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java index b3aaaa7f..6dcae635 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java @@ -26,13 +26,13 @@ import java.util.HashMap; import java.util.List; import java.util.Properties; -import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; -import org.onap.policy.drools.controller.internal.MavenDroolsController; -import org.onap.policy.drools.controller.internal.NullDroolsController; import org.onap.policy.common.endpoints.event.comm.Topic; import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; +import org.onap.policy.drools.controller.internal.MavenDroolsController; +import org.onap.policy.drools.controller.internal.NullDroolsController; import org.onap.policy.drools.properties.DroolsProperties; import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; @@ -45,12 +45,12 @@ import org.slf4j.LoggerFactory; /** * Drools Controller Factory to manage controller creation, destruction, and retrieval for - * management interfaces + * management interfaces. */ public interface DroolsControllerFactory { /** - * Constructs a Drools Controller based on properties + * Constructs a Drools Controller based on properties. * * @param properties properties containing initialization parameters * @param eventSources list of event sources @@ -64,7 +64,7 @@ public interface DroolsControllerFactory { List<? extends TopicSink> eventSinks) throws LinkageError; /** - * Explicit construction of a Drools Controller + * Explicit construction of a Drools Controller. * * @param groupId maven group id of drools artifact * @param artifactId maven artifact id of drools artifact @@ -81,31 +81,31 @@ public interface DroolsControllerFactory { List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError; /** - * Releases the Drools Controller from operation + * Releases the Drools Controller from operation. * * @param controller the Drools Controller to shut down */ public void shutdown(DroolsController controller); /** - * Disables all Drools Controllers from operation + * Disables all Drools Controllers from operation. */ public void shutdown(); /** - * Destroys and releases resources for a Drools Controller + * Destroys and releases resources for a Drools Controller. * * @param controller the Drools Controller to destroy */ public void destroy(DroolsController controller); /** - * Destroys all Drools Controllers + * Destroys all Drools Controllers. */ public void destroy(); /** - * Gets the Drools Controller associated with the maven group and artifact id + * Gets the Drools Controller associated with the maven group and artifact id. * * @param groupId maven group id of drools artifact * @param artifactId maven artifact id of drools artifact @@ -117,7 +117,7 @@ public interface DroolsControllerFactory { public DroolsController get(String groupId, String artifactId, String version); /** - * returns the current inventory of Drools Controllers + * returns the current inventory of Drools Controllers. * * @return a list of Drools Controllers */ @@ -128,22 +128,22 @@ public interface DroolsControllerFactory { /* ---------------- implementation ----------------- */ /** - * Factory of Drools Controllers indexed by the Maven coordinates + * Factory of Drools Controllers indexed by the Maven coordinates. */ class IndexedDroolsControllerFactory implements DroolsControllerFactory { /** - * logger + * logger. */ private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class); /** - * Policy Controller Name Index + * Policy Controller Name Index. */ protected HashMap<String, DroolsController> droolsControllers = new HashMap<>(); /** - * Null Drools Controller + * Null Drools Controller. */ protected NullDroolsController nullDroolsController = new NullDroolsController(); @@ -186,8 +186,66 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { return this.build(groupId, artifactId, version, topics2DecodedClasses2Filters, topics2EncodedClasses2Filters); } + @Override + public DroolsController build(String newGroupId, String newArtifactId, String newVersion, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError { + + if (newGroupId == null || newGroupId.isEmpty()) { + throw new IllegalArgumentException("Missing maven group-id coordinate"); + } + + if (newArtifactId == null || newArtifactId.isEmpty()) { + throw new IllegalArgumentException("Missing maven artifact-id coordinate"); + } + + if (newVersion == null || newVersion.isEmpty()) { + throw new IllegalArgumentException("Missing maven version coordinate"); + } + + String controllerId = newGroupId + ":" + newArtifactId; + DroolsController controllerCopy = null; + synchronized (this) { + /* + * The Null Drools Controller for no maven coordinates is always here so when no + * coordinates present, this is the return point + * + * assert (controllerCopy instanceof NullDroolsController) + */ + if (droolsControllers.containsKey(controllerId)) { + controllerCopy = droolsControllers.get(controllerId); + if (controllerCopy.getVersion().equalsIgnoreCase(newVersion)) { + return controllerCopy; + } + } + } + + if (controllerCopy != null) { + /* + * a controller keyed by group id + artifact id exists but with different version => + * version upgrade/downgrade + */ + + controllerCopy.updateToVersion(newGroupId, newArtifactId, newVersion, decoderConfigurations, + encoderConfigurations); + + return controllerCopy; + } + + /* new drools controller */ + + DroolsController controller = new MavenDroolsController(newGroupId, newArtifactId, newVersion, + decoderConfigurations, encoderConfigurations); + + synchronized (this) { + droolsControllers.put(controllerId, controller); + } + + return controller; + } + /** - * find out decoder classes and filters + * find out decoder classes and filters. * * @param properties properties with information about decoders * @param topicEntities topic sources @@ -235,12 +293,12 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { // 1. first the topic - String aTopic = topic.getTopic(); + String firstTopic = topic.getTopic(); // 2. check if there is a custom decoder for this topic that the user prefers to use // instead of the ones provided in the platform - String customGson = properties.getProperty(propertyTopicEntityPrefix + aTopic + String customGson = properties.getProperty(propertyTopicEntityPrefix + firstTopic + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_GSON_SUFFIX); CustomGsonCoder customGsonCoder = null; @@ -253,7 +311,7 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { } } - String customJackson = properties.getProperty(propertyTopicEntityPrefix + aTopic + String customJackson = properties.getProperty(propertyTopicEntityPrefix + firstTopic + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_JACKSON_SUFFIX); CustomJacksonCoder customJacksonCoder = null; @@ -269,25 +327,27 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { // 3. second the list of classes associated with each topic String eventClasses = properties - .getProperty(propertyTopicEntityPrefix + aTopic + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX); + .getProperty(propertyTopicEntityPrefix + firstTopic + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX); if (eventClasses == null || eventClasses.isEmpty()) { - // TODO warn + logger.warn("There are no event classes for topic {}", firstTopic); continue; } List<PotentialCoderFilter> classes2Filters = new ArrayList<>(); - List<String> aTopicClasses = new ArrayList<>(Arrays.asList(eventClasses.split("\\s*,\\s*"))); + List<String> topicClasses = new ArrayList<>(Arrays.asList(eventClasses.split("\\s*,\\s*"))); - for (String aClass : aTopicClasses) { + for (String theClass : topicClasses) { // 4. third, for each coder class, get the list of field filters String filter = properties - .getProperty(propertyTopicEntityPrefix + aTopic + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX - + "." + aClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX); + .getProperty(propertyTopicEntityPrefix + firstTopic + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX + + "." + theClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX); List<Pair<String, String>> filters = new ArrayList<>(); @@ -295,7 +355,7 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { // 4. topic -> class -> with no filters JsonProtocolFilter protocolFilter = JsonProtocolFilter.fromRawFilters(filters); - PotentialCoderFilter class2Filters = new PotentialCoderFilter(aClass, protocolFilter); + PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter); classes2Filters.add(class2Filters); continue; } @@ -330,12 +390,12 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { } JsonProtocolFilter protocolFilter = JsonProtocolFilter.fromRawFilters(filters); - PotentialCoderFilter class2Filters = new PotentialCoderFilter(aClass, protocolFilter); + PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter); classes2Filters.add(class2Filters); } TopicCoderFilterConfiguration topic2Classes2Filters = - new TopicCoderFilterConfiguration(aTopic, classes2Filters, customGsonCoder, customJacksonCoder); + new TopicCoderFilterConfiguration(firstTopic, classes2Filters, customGsonCoder, customJacksonCoder); topics2DecodedClasses2Filters.add(topic2Classes2Filters); } @@ -343,64 +403,6 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { } @Override - public DroolsController build(String newGroupId, String newArtifactId, String newVersion, - List<TopicCoderFilterConfiguration> decoderConfigurations, - List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError { - - if (newGroupId == null || newGroupId.isEmpty()) { - throw new IllegalArgumentException("Missing maven group-id coordinate"); - } - - if (newArtifactId == null || newArtifactId.isEmpty()) { - throw new IllegalArgumentException("Missing maven artifact-id coordinate"); - } - - if (newVersion == null || newVersion.isEmpty()) { - throw new IllegalArgumentException("Missing maven version coordinate"); - } - - String controllerId = newGroupId + ":" + newArtifactId; - DroolsController controllerCopy = null; - synchronized (this) { - /* - * The Null Drools Controller for no maven coordinates is always here so when no - * coordinates present, this is the return point - * - * assert (controllerCopy instanceof NullDroolsController) - */ - if (droolsControllers.containsKey(controllerId)) { - controllerCopy = droolsControllers.get(controllerId); - if (controllerCopy.getVersion().equalsIgnoreCase(newVersion)) { - return controllerCopy; - } - } - } - - if (controllerCopy != null) { - /* - * a controller keyed by group id + artifact id exists but with different version => - * version upgrade/downgrade - */ - - controllerCopy.updateToVersion(newGroupId, newArtifactId, newVersion, decoderConfigurations, - encoderConfigurations); - - return controllerCopy; - } - - /* new drools controller */ - - DroolsController controller = new MavenDroolsController(newGroupId, newArtifactId, newVersion, - decoderConfigurations, encoderConfigurations); - - synchronized (this) { - droolsControllers.put(controllerId, controller); - } - - return controller; - } - - @Override public void destroy(DroolsController controller) { unmanage(controller); controller.halt(); @@ -419,11 +421,9 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory { } /** - * unmanage the drools controller + * unmanage the drools controller. * - * @param controller - * @return - * @throws IllegalArgumentException + * @param controller the controller */ protected void unmanage(DroolsController controller) { if (controller == null) { diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java index 7b44817a..707a8e77 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java @@ -35,11 +35,11 @@ import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.FactHandle; import org.kie.api.runtime.rule.QueryResults; import org.kie.api.runtime.rule.QueryResultsRow; +import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.drools.controller.DroolsController; import org.onap.policy.drools.core.PolicyContainer; import org.onap.policy.drools.core.PolicySession; import org.onap.policy.drools.core.jmx.PdpJmx; -import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.drools.features.DroolsControllerFeatureAPI; import org.onap.policy.drools.protocol.coders.EventProtocolCoder; import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; @@ -57,806 +57,857 @@ import org.slf4j.LoggerFactory; * Drools containers instantiated using Maven. */ public class MavenDroolsController implements DroolsController { - - /** - * logger - */ - private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class); - - /** - * Policy Container, the access object to the policy-core layer - */ - @JsonIgnore - protected final PolicyContainer policyContainer; - - /** - * alive status of this drools controller, - * reflects invocation of start()/stop() only - */ - protected volatile boolean alive = false; - - /** - * locked status of this drools controller, - * reflects if i/o drools related operations are permitted, - * more specifically: offer() and deliver(). - * It does not affect the ability to start and stop - * underlying drools infrastructure - */ - protected volatile boolean locked = false; - - /** - * list of topics, each with associated decoder classes, each - * with a list of associated filters. - */ - protected List<TopicCoderFilterConfiguration> decoderConfigurations; - - /** - * list of topics, each with associated encoder classes, each - * with a list of associated filters. - */ - protected List<TopicCoderFilterConfiguration> encoderConfigurations; - - /** - * recent source events processed - */ - protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<>(10); - - /** - * recent sink events processed - */ - protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10); - - /** - * original Drools Model/Rules classloader hash - */ - protected int modelClassLoaderHash; - - /** - * Expanded version of the constructor - * - * @param groupId maven group id - * @param artifactId maven artifact id - * @param version maven version - * @param decoderConfigurations list of topic -> decoders -> filters mapping - * @param encoderConfigurations list of topic -> encoders -> filters mapping - * - * @throws IllegalArgumentException invalid arguments passed in - */ - public MavenDroolsController(String groupId, - String artifactId, - String version, - List<TopicCoderFilterConfiguration> decoderConfigurations, - List<TopicCoderFilterConfiguration> encoderConfigurations) { - - logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version); - - if (groupId == null || groupId.isEmpty()) - throw new IllegalArgumentException("Missing maven group-id coordinate"); - - if (artifactId == null || artifactId.isEmpty()) - throw new IllegalArgumentException("Missing maven artifact-id coordinate"); - - if (version == null || version.isEmpty()) - throw new IllegalArgumentException("Missing maven version coordinate"); - - this.policyContainer= new PolicyContainer(groupId, artifactId, version); - this.init(decoderConfigurations, encoderConfigurations); - - logger.debug("{}: instantiation completed ", this); - } - - /** - * init encoding/decoding configuration - * @param decoderConfigurations list of topic -> decoders -> filters mapping - * @param encoderConfigurations list of topic -> encoders -> filters mapping - */ - protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations, - List<TopicCoderFilterConfiguration> encoderConfigurations) { - - this.decoderConfigurations = decoderConfigurations; - this.encoderConfigurations = encoderConfigurations; - - this.initCoders(decoderConfigurations, true); - this.initCoders(encoderConfigurations, false); - - this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode(); - } - - @Override - public void updateToVersion(String newGroupId, String newArtifactId, String newVersion, - List<TopicCoderFilterConfiguration> decoderConfigurations, - List<TopicCoderFilterConfiguration> encoderConfigurations) - throws LinkageError { - - logger.info("{}: updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion); - - if (newGroupId == null || newGroupId.isEmpty()) - throw new IllegalArgumentException("Missing maven group-id coordinate"); - - if (newArtifactId == null || newArtifactId.isEmpty()) - throw new IllegalArgumentException("Missing maven artifact-id coordinate"); - - if (newVersion == null || newVersion.isEmpty()) - throw new IllegalArgumentException("Missing maven version coordinate"); - - if (newGroupId.equalsIgnoreCase(DroolsController.NO_GROUP_ID) || - newArtifactId.equalsIgnoreCase(DroolsController.NO_ARTIFACT_ID) || - newVersion.equalsIgnoreCase(DroolsController.NO_VERSION)) { - throw new IllegalArgumentException("BRAINLESS maven coordinates provided: " + - newGroupId + ":" + newArtifactId + ":" + - newVersion); - } - - if (newGroupId.equalsIgnoreCase(this.getGroupId()) && - newArtifactId.equalsIgnoreCase(this.getArtifactId()) && - newVersion.equalsIgnoreCase(this.getVersion())) { - logger.warn("Al in the right version: " + newGroupId + ":" + - newArtifactId + ":" + newVersion + " vs. " + this); - return; - } - - if (!newGroupId.equalsIgnoreCase(this.getGroupId()) || - !newArtifactId.equalsIgnoreCase(this.getArtifactId())) { - throw new IllegalArgumentException("Group ID and Artifact ID maven coordinates must be identical for the upgrade: " + - newGroupId + ":" + newArtifactId + ":" + - newVersion + " vs. " + this); - } - - /* upgrade */ - String messages = this.policyContainer.updateToVersion(newVersion); - if (logger.isWarnEnabled()) - logger.warn(this + "UPGRADE results: " + messages); - - /* - * If all sucessful (can load new container), now we can remove all coders from previous sessions - */ - this.removeCoders(); - - /* - * add the new coders - */ - this.init(decoderConfigurations, encoderConfigurations); - - if (logger.isInfoEnabled()) - logger.info("UPDATE-TO-VERSION: completed " + this); - } - - /** - * initialize decoders for all the topics supported by this controller - * Note this is critical to be done after the Policy Container is - * instantiated to be able to fetch the corresponding classes. - * - * @param coderConfigurations list of topic -> decoders -> filters mapping - */ - protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations, - boolean decoder) { - - if (logger.isInfoEnabled()) - logger.info("INIT-CODERS: " + this); - - if (coderConfigurations == null) { - return; - } - - - for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) { - String topic = coderConfig.getTopic(); - - CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder(); - if (coderConfig.getCustomGsonCoder() != null && - coderConfig.getCustomGsonCoder().getClassContainer() != null && - !coderConfig.getCustomGsonCoder().getClassContainer().isEmpty()) { - - String customGsonCoderClass = coderConfig.getCustomGsonCoder().getClassContainer(); - if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), - customGsonCoderClass)) { - throw makeRetrieveEx(customGsonCoderClass); - } else { - if (logger.isInfoEnabled()) - logClassFetched(customGsonCoderClass); - } - } - - CustomJacksonCoder customJacksonCoder = coderConfig.getCustomJacksonCoder(); - if (coderConfig.getCustomJacksonCoder() != null && - coderConfig.getCustomJacksonCoder().getClassContainer() != null && - !coderConfig.getCustomJacksonCoder().getClassContainer().isEmpty()) { - - String customJacksonCoderClass = coderConfig.getCustomJacksonCoder().getClassContainer(); - if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), - customJacksonCoderClass)) { - throw makeRetrieveEx(customJacksonCoderClass); - } else { - if (logger.isInfoEnabled()) - logClassFetched(customJacksonCoderClass); - } - } - - List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters(); - if (coderFilters == null || coderFilters.isEmpty()) { - continue; - } - - for (PotentialCoderFilter coderFilter : coderFilters) { - String potentialCodedClass = coderFilter.getCodedClass(); - JsonProtocolFilter protocolFilter = coderFilter.getFilter(); - - if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), - potentialCodedClass)) { - throw makeRetrieveEx(potentialCodedClass); - } else { - if (logger.isInfoEnabled()) - logClassFetched(potentialCodedClass); - } - - if (decoder) - EventProtocolCoder.manager.addDecoder(this.getGroupId(), this.getArtifactId(), - topic, potentialCodedClass, protocolFilter, - customGsonCoder, - customJacksonCoder, - this.policyContainer.getClassLoader().hashCode()); - else - EventProtocolCoder.manager.addEncoder(this.getGroupId(), this.getArtifactId(), - topic, potentialCodedClass, protocolFilter, - customGsonCoder, - customJacksonCoder, - this.policyContainer.getClassLoader().hashCode()); - } - } - } - - /** - * Logs an error and makes an exception for an item that cannot be retrieved. - * @param itemName - * @return a new exception - */ - private IllegalArgumentException makeRetrieveEx(String itemName) { - logger.error(itemName + " cannot be retrieved"); - return new IllegalArgumentException(itemName + " cannot be retrieved"); - } - - /** - * Logs the name of the class that was fetched. - * @param className - */ - private void logClassFetched(String className) { - logger.info("CLASS FETCHED " + className); - } - - - /** - * remove decoders. - */ - protected void removeDecoders(){ - if (logger.isInfoEnabled()) - logger.info("REMOVE-DECODERS: " + this); - - if (this.decoderConfigurations == null) { - return; - } - - - for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) { - String topic = coderConfig.getTopic(); - EventProtocolCoder.manager.removeDecoders - (this.getGroupId(), this.getArtifactId(), topic); - } - } - - /** - * remove decoders. - */ - protected void removeEncoders() { - - if (logger.isInfoEnabled()) - logger.info("REMOVE-ENCODERS: " + this); - - if (this.encoderConfigurations == null) - return; - - - for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) { - String topic = coderConfig.getTopic(); - EventProtocolCoder.manager.removeEncoders - (this.getGroupId(), this.getArtifactId(), topic); - } - } - - - @Override - public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) { - if (!ReflectionUtil.isClass - (this.policyContainer.getClassLoader(), coderClass.getCanonicalName())) { - logger.error(this + coderClass.getCanonicalName() + " cannot be retrieved. "); - return false; - } - - if (modelHash == this.modelClassLoaderHash) { - if (logger.isInfoEnabled()) - logger.info(coderClass.getCanonicalName() + - this + " class loader matches original drools controller rules classloader " + - coderClass.getClassLoader()); - return true; - } else { - if (logger.isWarnEnabled()) - logger.warn(this + coderClass.getCanonicalName() + " class loaders don't match " + - coderClass.getClassLoader() + " vs " + - this.policyContainer.getClassLoader()); - return false; - } - } - - @Override - public boolean start() { - - if (logger.isInfoEnabled()) - logger.info("START: " + this); - - synchronized (this) { - if (this.alive) - return true; - - this.alive = true; - } - - return this.policyContainer.start(); - } - - @Override - public boolean stop() { - - logger.info("STOP: " + this); - - synchronized (this) { - if (!this.alive) - return true; - - this.alive = false; - } - - return this.policyContainer.stop(); - } - - @Override - public void shutdown() { - logger.info("{}: SHUTDOWN", this); - - try { - this.stop(); - this.removeCoders(); - } catch (Exception e) { - logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e); - } finally { - this.policyContainer.shutdown(); - } - - } - - @Override - public void halt() { - logger.info("{}: HALT", this); - - try { - this.stop(); - this.removeCoders(); - } catch (Exception e) { - logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e); - } finally { - this.policyContainer.destroy(); - } - } - - /** - * removes this drools controllers and encoders and decoders from operation - */ - protected void removeCoders() { - logger.info("{}: REMOVE-CODERS", this); - - try { - this.removeDecoders(); - } catch (IllegalArgumentException e) { - logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e); - } - - try { - this.removeEncoders(); - } catch (IllegalArgumentException e) { - logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e); - } - } - - @Override - public boolean isAlive() { - return this.alive; - } - - @Override - public boolean offer(String topic, String event) { - logger.debug("{}: OFFER: {} <- {}", this, topic, event); - - if (this.locked) - return true; - - if (!this.alive) - return true; - - // 0. Check if the policy container has any sessions - - if (this.policyContainer.getPolicySessions().isEmpty()) { - // no sessions - return true; - } - - // 1. Now, check if this topic has a decoder: - - if (!EventProtocolCoder.manager.isDecodingSupported(this.getGroupId(), - this.getArtifactId(), - topic)) { - - logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this, - topic, this.getGroupId(), this.getArtifactId()); - return true; - } - - // 2. Decode - - Object anEvent; - try { - anEvent = EventProtocolCoder.manager.decode(this.getGroupId(), - this.getArtifactId(), - topic, - event); - } catch (UnsupportedOperationException uoe) { - logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic, - event, uoe.getMessage(), uoe); - return true; - } catch (Exception e) { - logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic, - event, e.getMessage(), e); - return true; - } - - synchronized(this.recentSourceEvents) { - this.recentSourceEvents.add(anEvent); - } - - // increment event count for Nagios monitoring - PdpJmx.getInstance().updateOccured(); - - // Broadcast - - if (logger.isInfoEnabled()) - logger.info(this + "BROADCAST-INJECT of " + event + " FROM " + topic + " INTO " + this.policyContainer.getName()); - - for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { - try { - if (feature.beforeInsert(this, anEvent)) - return true; - } catch (Exception e) { - logger.error("{}: feature {} before-insert failure because of {}", - this, feature.getClass().getName(), e.getMessage(), e); - } - } - - boolean successInject = this.policyContainer.insertAll(anEvent); - if (!successInject) - logger.warn(this + "Failed to inject into PolicyContainer " + this.getSessionNames()); - - for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { - try { - if (feature.afterInsert(this, anEvent, successInject)) - return true; - } catch (Exception e) { - logger.error("{}: feature {} after-insert failure because of {}", - this, feature.getClass().getName(), e.getMessage(), e); - } - } - - return true; - } - - @Override - public boolean deliver(TopicSink sink, Object event) { - - if (logger.isInfoEnabled()) - logger.info(this + "DELIVER: " + event + " FROM " + this + " TO " + sink); - - if (sink == null) - throw new IllegalArgumentException - (this + " invalid sink"); - - if (event == null) - throw new IllegalArgumentException - (this + " invalid event"); - - if (this.locked) - throw new IllegalStateException - (this + " is locked"); - - if (!this.alive) - throw new IllegalStateException - (this + " is stopped"); - - String json = - EventProtocolCoder.manager.encode(sink.getTopic(), event, this); - - synchronized(this.recentSinkEvents) { - this.recentSinkEvents.add(json); - } - - return sink.send(json); - - } - - @Override - public String getVersion() { - return this.policyContainer.getVersion(); - } - - @Override - public String getArtifactId() { - return this.policyContainer.getArtifactId(); - } - - @Override - public String getGroupId() { - return this.policyContainer.getGroupId(); - } - - /** - * @return the modelClassLoaderHash - */ - public int getModelClassLoaderHash() { - return modelClassLoaderHash; - } - - @Override - public synchronized boolean lock() { - logger.info("LOCK: " + this); - - this.locked = true; - return true; - } - - @Override - public synchronized boolean unlock() { - logger.info("UNLOCK: " + this); - - this.locked = false; - return true; - } - - @Override - public boolean isLocked() { - return this.locked; - } - - @JsonIgnore - @Override - public PolicyContainer getContainer() { - return this.policyContainer; - } - - @JsonProperty("sessions") - @Override - public List<String> getSessionNames() { - return getSessionNames(true); - } - - @JsonProperty("sessionCoordinates") - @Override - public List<String> getCanonicalSessionNames() { - return getSessionNames(false); - } - - /** - * get session names - * @param abbreviated true for the short form, otherwise the long form - * @return session names - */ - protected List<String> getSessionNames(boolean abbreviated) { - List<String> sessionNames = new ArrayList<>(); - try { - for (PolicySession session: this.policyContainer.getPolicySessions()) { - if (abbreviated) - sessionNames.add(session.getName()); - else - sessionNames.add(session.getFullName()); - } - } catch (Exception e) { - logger.warn("Can't retrieve CORE sessions: " + e.getMessage(), e); - sessionNames.add(e.getMessage()); - } - return sessionNames; - } - - /** - * provides the underlying core layer container sessions - * - * @return the attached Policy Container - */ - protected List<PolicySession> getSessions() { - List<PolicySession> sessions = new ArrayList<>(); - sessions.addAll(this.policyContainer.getPolicySessions()); - return sessions; - } - - /** - * provides the underlying core layer container session with name sessionName - * - * @param sessionName session name - * @return the attached Policy Container - * @throws IllegalArgumentException when an invalid session name is provided - * @throws IllegalStateException when the drools controller is in an invalid state - */ - protected PolicySession getSession(String sessionName) { - if (sessionName == null || sessionName.isEmpty()) - throw new IllegalArgumentException("A Session Name must be provided"); - - List<PolicySession> sessions = this.getSessions(); - for (PolicySession session : sessions) { - if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName())) - return session; - } - - throw invalidSessNameEx(sessionName); - } - - private IllegalArgumentException invalidSessNameEx(String sessionName) { - return new IllegalArgumentException("Invalid Session Name: " + sessionName); - } - - @Override - public Map<String,Integer> factClassNames(String sessionName) { - if (sessionName == null || sessionName.isEmpty()) - throw invalidSessNameEx(sessionName); - - Map<String,Integer> classNames = new HashMap<>(); - - PolicySession session = getSession(sessionName); - KieSession kieSession = session.getKieSession(); - - Collection<FactHandle> facts = session.getKieSession().getFactHandles(); - for (FactHandle fact : facts) { - try { - String className = kieSession.getObject(fact).getClass().getName(); - if (classNames.containsKey(className)) - classNames.put(className, classNames.get(className) + 1); - else - classNames.put(className, 1); - } catch (Exception e) { - logger.warn("Object cannot be retrieved from fact {}", fact, e); - } - } - - return classNames; - } - - @Override - public long factCount(String sessionName) { - if (sessionName == null || sessionName.isEmpty()) - throw invalidSessNameEx(sessionName); - - PolicySession session = getSession(sessionName); - return session.getKieSession().getFactCount(); - } - - @Override - public List<Object> facts(String sessionName, String className, boolean delete) { - if (sessionName == null || sessionName.isEmpty()) - throw invalidSessNameEx(sessionName); - - if (className == null || className.isEmpty()) - throw new IllegalArgumentException("Invalid Class Name: " + className); - - Class<?> factClass = - ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className); - if (factClass == null) - throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className); - - PolicySession session = getSession(sessionName); - KieSession kieSession = session.getKieSession(); - - List<Object> factObjects = new ArrayList<>(); - - Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass)); - for (FactHandle factHandle : factHandles) { - try { - factObjects.add(kieSession.getObject(factHandle)); - if (delete) - kieSession.delete(factHandle); - } catch (Exception e) { - logger.warn("Object cannot be retrieved from fact {}", factHandle, e); - } - } - - return factObjects; - } - - @Override - public List<Object> factQuery(String sessionName, String queryName, String queriedEntity, boolean delete, Object... queryParams) { - if (sessionName == null || sessionName.isEmpty()) - throw invalidSessNameEx(sessionName); - - if (queryName == null || queryName.isEmpty()) - throw new IllegalArgumentException("Invalid Query Name: " + queryName); - - if (queriedEntity == null || queriedEntity.isEmpty()) - throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity); - - PolicySession session = getSession(sessionName); - KieSession kieSession = session.getKieSession(); - - boolean found = false; - for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) { - for (Query q : kiePackage.getQueries()) { - if (q.getName() != null && q.getName().equals(queryName)) { - found = true; - break; - } - } - } - if (!found) - throw new IllegalArgumentException("Invalid Query Name: " + queryName); + + /** + * logger. + */ + private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class); + + /** + * Policy Container, the access object to the policy-core layer. + */ + @JsonIgnore + protected final PolicyContainer policyContainer; + + /** + * alive status of this drools controller, + * reflects invocation of start()/stop() only. + */ + protected volatile boolean alive = false; + + /** + * locked status of this drools controller, + * reflects if i/o drools related operations are permitted, + * more specifically: offer() and deliver(). + * It does not affect the ability to start and stop + * underlying drools infrastructure + */ + protected volatile boolean locked = false; + + /** + * list of topics, each with associated decoder classes, each + * with a list of associated filters. + */ + protected List<TopicCoderFilterConfiguration> decoderConfigurations; + + /** + * list of topics, each with associated encoder classes, each + * with a list of associated filters. + */ + protected List<TopicCoderFilterConfiguration> encoderConfigurations; + + /** + * recent source events processed. + */ + protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<>(10); + + /** + * recent sink events processed. + */ + protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10); + + /** + * original Drools Model/Rules classloader hash. + */ + protected int modelClassLoaderHash; + + /** + * Expanded version of the constructor. + * + * @param groupId maven group id + * @param artifactId maven artifact id + * @param version maven version + * @param decoderConfigurations list of topic -> decoders -> filters mapping + * @param encoderConfigurations list of topic -> encoders -> filters mapping + * + * @throws IllegalArgumentException invalid arguments passed in + */ + public MavenDroolsController(String groupId, + String artifactId, + String version, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) { + + logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version); + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException("Missing maven group-id coordinate"); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException("Missing maven artifact-id coordinate"); + } + + if (version == null || version.isEmpty()) { + throw new IllegalArgumentException("Missing maven version coordinate"); + } + + this.policyContainer = new PolicyContainer(groupId, artifactId, version); + this.init(decoderConfigurations, encoderConfigurations); + + logger.debug("{}: instantiation completed ", this); + } + + /** + * init encoding/decoding configuration. + * + * @param decoderConfigurations list of topic -> decoders -> filters mapping + * @param encoderConfigurations list of topic -> encoders -> filters mapping + */ + protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) { + + this.decoderConfigurations = decoderConfigurations; + this.encoderConfigurations = encoderConfigurations; + + this.initCoders(decoderConfigurations, true); + this.initCoders(encoderConfigurations, false); + + this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode(); + } + + @Override + public void updateToVersion(String newGroupId, String newArtifactId, String newVersion, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) + throws LinkageError { + + logger.info("updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion); + + if (newGroupId == null || newGroupId.isEmpty()) { + throw new IllegalArgumentException("Missing maven group-id coordinate"); + } + + if (newArtifactId == null || newArtifactId.isEmpty()) { + throw new IllegalArgumentException("Missing maven artifact-id coordinate"); + } + + if (newVersion == null || newVersion.isEmpty()) { + throw new IllegalArgumentException("Missing maven version coordinate"); + } + + if (newGroupId.equalsIgnoreCase(DroolsController.NO_GROUP_ID) + || newArtifactId.equalsIgnoreCase(DroolsController.NO_ARTIFACT_ID) + || newVersion.equalsIgnoreCase(DroolsController.NO_VERSION)) { + throw new IllegalArgumentException("BRAINLESS maven coordinates provided: " + + newGroupId + ":" + newArtifactId + ":" + + newVersion); + } + + if (newGroupId.equalsIgnoreCase(this.getGroupId()) + && newArtifactId.equalsIgnoreCase(this.getArtifactId()) + && newVersion.equalsIgnoreCase(this.getVersion())) { + logger.warn("Al in the right version: " + newGroupId + ":" + + newArtifactId + ":" + newVersion + " vs. " + this); + return; + } + + if (!newGroupId.equalsIgnoreCase(this.getGroupId()) + || !newArtifactId.equalsIgnoreCase(this.getArtifactId())) { + throw new IllegalArgumentException( + "Group ID and Artifact ID maven coordinates must be identical for the upgrade: " + + newGroupId + ":" + newArtifactId + ":" + + newVersion + " vs. " + this); + } + + /* upgrade */ + String messages = this.policyContainer.updateToVersion(newVersion); + if (logger.isWarnEnabled()) { + logger.warn("{} UPGRADE results: {}", this, messages); + } + + /* + * If all sucessful (can load new container), now we can remove all coders from previous sessions + */ + this.removeCoders(); + + /* + * add the new coders + */ + this.init(decoderConfigurations, encoderConfigurations); + + if (logger.isInfoEnabled()) { + logger.info("UPDATE-TO-VERSION: completed " + this); + } + } + + /** + * initialize decoders for all the topics supported by this controller + * Note this is critical to be done after the Policy Container is + * instantiated to be able to fetch the corresponding classes. + * + * @param coderConfigurations list of topic -> decoders -> filters mapping + */ + protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations, + boolean decoder) { + + if (logger.isInfoEnabled()) { + logger.info("INIT-CODERS: " + this); + } + + if (coderConfigurations == null) { + return; + } + + + for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) { + String topic = coderConfig.getTopic(); + + CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder(); + if (coderConfig.getCustomGsonCoder() != null + && coderConfig.getCustomGsonCoder().getClassContainer() != null + && !coderConfig.getCustomGsonCoder().getClassContainer().isEmpty()) { + + String customGsonCoderClass = coderConfig.getCustomGsonCoder().getClassContainer(); + if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), + customGsonCoderClass)) { + throw makeRetrieveEx(customGsonCoderClass); + } else { + if (logger.isInfoEnabled()) { + logClassFetched(customGsonCoderClass); + } + } + } + + CustomJacksonCoder customJacksonCoder = coderConfig.getCustomJacksonCoder(); + if (coderConfig.getCustomJacksonCoder() != null + && coderConfig.getCustomJacksonCoder().getClassContainer() != null + && !coderConfig.getCustomJacksonCoder().getClassContainer().isEmpty()) { + + String customJacksonCoderClass = coderConfig.getCustomJacksonCoder().getClassContainer(); + if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), + customJacksonCoderClass)) { + throw makeRetrieveEx(customJacksonCoderClass); + } else { + if (logger.isInfoEnabled()) { + logClassFetched(customJacksonCoderClass); + } + } + } + + List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters(); + if (coderFilters == null || coderFilters.isEmpty()) { + continue; + } + + for (PotentialCoderFilter coderFilter : coderFilters) { + String potentialCodedClass = coderFilter.getCodedClass(); + JsonProtocolFilter protocolFilter = coderFilter.getFilter(); + + if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), + potentialCodedClass)) { + throw makeRetrieveEx(potentialCodedClass); + } else { + if (logger.isInfoEnabled()) { + logClassFetched(potentialCodedClass); + } + } + + if (decoder) { + EventProtocolCoder.manager.addDecoder(this.getGroupId(), this.getArtifactId(), + topic, potentialCodedClass, protocolFilter, + customGsonCoder, + customJacksonCoder, + this.policyContainer.getClassLoader().hashCode()); + } else { + EventProtocolCoder.manager.addEncoder(this.getGroupId(), this.getArtifactId(), + topic, potentialCodedClass, protocolFilter, + customGsonCoder, + customJacksonCoder, + this.policyContainer.getClassLoader().hashCode()); + } + } + } + } + + /** + * Logs an error and makes an exception for an item that cannot be retrieved. + * @param itemName the item to retrieve + * @return a new exception + */ + private IllegalArgumentException makeRetrieveEx(String itemName) { + logger.error("{} cannot be retrieved", itemName); + return new IllegalArgumentException(itemName + " cannot be retrieved"); + } + + /** + * Logs the name of the class that was fetched. + * @param className class name fetched + */ + private void logClassFetched(String className) { + logger.info("CLASS FETCHED {}", className); + } + + + /** + * remove decoders. + */ + protected void removeDecoders() { + if (logger.isInfoEnabled()) { + logger.info("REMOVE-DECODERS: {}", this); + } + + if (this.decoderConfigurations == null) { + return; + } + + + for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) { + String topic = coderConfig.getTopic(); + EventProtocolCoder.manager.removeDecoders(this.getGroupId(), this.getArtifactId(), topic); + } + } + + /** + * remove decoders. + */ + protected void removeEncoders() { + + if (logger.isInfoEnabled()) { + logger.info("REMOVE-ENCODERS: {}", this); + } + + if (this.encoderConfigurations == null) { + return; + } + + for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) { + String topic = coderConfig.getTopic(); + EventProtocolCoder.manager.removeEncoders(this.getGroupId(), this.getArtifactId(), topic); + } + } + + + @Override + public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) { + if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), coderClass.getCanonicalName())) { + logger.error("{}{} cannot be retrieved. ", this, coderClass.getCanonicalName()); + return false; + } + + if (modelHash == this.modelClassLoaderHash) { + if (logger.isInfoEnabled()) { + logger.info(coderClass.getCanonicalName() + + this + " class loader matches original drools controller rules classloader " + + coderClass.getClassLoader()); + } + return true; + } else { + if (logger.isWarnEnabled()) { + logger.warn(this + coderClass.getCanonicalName() + " class loaders don't match " + + coderClass.getClassLoader() + " vs " + + this.policyContainer.getClassLoader()); + } + return false; + } + } + + @Override + public boolean start() { + + if (logger.isInfoEnabled()) { + logger.info("START: {}", this); + } + + synchronized (this) { + if (this.alive) { + return true; + } + this.alive = true; + } + + return this.policyContainer.start(); + } + + @Override + public boolean stop() { + + logger.info("STOP: {}", this); + + synchronized (this) { + if (!this.alive) { + return true; + } + this.alive = false; + } + + return this.policyContainer.stop(); + } + + @Override + public void shutdown() { + logger.info("{}: SHUTDOWN", this); + + try { + this.stop(); + this.removeCoders(); + } catch (Exception e) { + logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e); + } finally { + this.policyContainer.shutdown(); + } + + } + + @Override + public void halt() { + logger.info("{}: HALT", this); + + try { + this.stop(); + this.removeCoders(); + } catch (Exception e) { + logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e); + } finally { + this.policyContainer.destroy(); + } + } + + /** + * removes this drools controllers and encoders and decoders from operation. + */ + protected void removeCoders() { + logger.info("{}: REMOVE-CODERS", this); + + try { + this.removeDecoders(); + } catch (IllegalArgumentException e) { + logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e); + } + + try { + this.removeEncoders(); + } catch (IllegalArgumentException e) { + logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e); + } + } + + @Override + public boolean isAlive() { + return this.alive; + } + + @Override + public boolean offer(String topic, String event) { + logger.debug("{}: OFFER: {} <- {}", this, topic, event); + + if (this.locked) { + return true; + } + if (!this.alive) { + return true; + } + + // 0. Check if the policy container has any sessions + + if (this.policyContainer.getPolicySessions().isEmpty()) { + // no sessions + return true; + } + + // 1. Now, check if this topic has a decoder: + + if (!EventProtocolCoder.manager.isDecodingSupported(this.getGroupId(), + this.getArtifactId(), + topic)) { + + logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this, + topic, this.getGroupId(), this.getArtifactId()); + return true; + } + + // 2. Decode + + Object anEvent; + try { + anEvent = EventProtocolCoder.manager.decode(this.getGroupId(), + this.getArtifactId(), + topic, + event); + } catch (UnsupportedOperationException uoe) { + logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic, + event, uoe.getMessage(), uoe); + return true; + } catch (Exception e) { + logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic, + event, e.getMessage(), e); + return true; + } + + synchronized (this.recentSourceEvents) { + this.recentSourceEvents.add(anEvent); + } + + // increment event count for Nagios monitoring + PdpJmx.getInstance().updateOccured(); + + // Broadcast + + if (logger.isInfoEnabled()) { + logger.info("{} BROADCAST-INJECT of {} FROM {} INTO {}", + this, event, topic, this.policyContainer.getName()); + } + + for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { + try { + if (feature.beforeInsert(this, anEvent)) { + return true; + } + } catch (Exception e) { + logger.error("{}: feature {} before-insert failure because of {}", + this, feature.getClass().getName(), e.getMessage(), e); + } + } + + boolean successInject = this.policyContainer.insertAll(anEvent); + if (!successInject) { + logger.warn(this + "Failed to inject into PolicyContainer {}", this.getSessionNames()); + } + + for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { + try { + if (feature.afterInsert(this, anEvent, successInject)) { + return true; + } + } catch (Exception e) { + logger.error("{}: feature {} after-insert failure because of {}", + this, feature.getClass().getName(), e.getMessage(), e); + } + } + + return true; + } + + @Override + public boolean deliver(TopicSink sink, Object event) { + + if (logger.isInfoEnabled()) { + logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink); + } + + if (sink == null) { + throw new IllegalArgumentException(this + " invalid sink"); + } + + if (event == null) { + throw new IllegalArgumentException(this + " invalid event"); + } - List<Object> factObjects = new ArrayList<>(); - - QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams); - for (QueryResultsRow row : queryResults) { - try { - factObjects.add(row.get(queriedEntity)); - if (delete) - kieSession.delete(row.getFactHandle(queriedEntity)); - } catch (Exception e) { - logger.warn("Object cannot be retrieved from row: {}", row, e); - } - } - - return factObjects; - } - - @Override - public Class<?> fetchModelClass(String className) { - return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className); - } - - /** - * @return the recentSourceEvents - */ - @Override - public Object[] getRecentSourceEvents() { - synchronized(this.recentSourceEvents) { - Object[] events = new Object[recentSourceEvents.size()]; - return recentSourceEvents.toArray(events); - } - } - - /** - * @return the recentSinkEvents - */ - @Override - public String[] getRecentSinkEvents() { - synchronized(this.recentSinkEvents) { - String[] events = new String[recentSinkEvents.size()]; - return recentSinkEvents.toArray(events); - } - } - - @Override - public boolean isBrained() { - return true; - } - - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("MavenDroolsController [policyContainer=") - .append((policyContainer != null) ? policyContainer.getName() : "NULL").append(":") - .append(", alive=") - .append(alive).append(", locked=") - .append(", modelClassLoaderHash=").append(modelClassLoaderHash).append("]"); - return builder.toString(); - } + if (this.locked) { + throw new IllegalStateException(this + " is locked"); + } + + if (!this.alive) { + throw new IllegalStateException(this + " is stopped"); + } + + String json = + EventProtocolCoder.manager.encode(sink.getTopic(), event, this); + + synchronized (this.recentSinkEvents) { + this.recentSinkEvents.add(json); + } + + return sink.send(json); + + } + + @Override + public String getVersion() { + return this.policyContainer.getVersion(); + } + + @Override + public String getArtifactId() { + return this.policyContainer.getArtifactId(); + } + + @Override + public String getGroupId() { + return this.policyContainer.getGroupId(); + } + + /** + * Get model class loader hash. + * + * @return the modelClassLoaderHash + */ + public int getModelClassLoaderHash() { + return modelClassLoaderHash; + } + + @Override + public synchronized boolean lock() { + logger.info("LOCK: {}", this); + + this.locked = true; + return true; + } + + @Override + public synchronized boolean unlock() { + logger.info("UNLOCK: {}", this); + + this.locked = false; + return true; + } + + @Override + public boolean isLocked() { + return this.locked; + } + + @JsonIgnore + @Override + public PolicyContainer getContainer() { + return this.policyContainer; + } + + @JsonProperty("sessions") + @Override + public List<String> getSessionNames() { + return getSessionNames(true); + } + + /** + * get session names. + * + * @param abbreviated true for the short form, otherwise the long form + * @return session names + */ + protected List<String> getSessionNames(boolean abbreviated) { + List<String> sessionNames = new ArrayList<>(); + try { + for (PolicySession session: this.policyContainer.getPolicySessions()) { + if (abbreviated) { + sessionNames.add(session.getName()); + } else { + sessionNames.add(session.getFullName()); + } + } + } catch (Exception e) { + logger.warn("Can't retrieve CORE sessions: " + e.getMessage(), e); + sessionNames.add(e.getMessage()); + } + return sessionNames; + } + + @JsonProperty("sessionCoordinates") + @Override + public List<String> getCanonicalSessionNames() { + return getSessionNames(false); + } + + /** + * provides the underlying core layer container sessions. + * + * @return the attached Policy Container + */ + protected List<PolicySession> getSessions() { + List<PolicySession> sessions = new ArrayList<>(); + sessions.addAll(this.policyContainer.getPolicySessions()); + return sessions; + } + + /** + * provides the underlying core layer container session with name sessionName. + * + * @param sessionName session name + * @return the attached Policy Container + * @throws IllegalArgumentException when an invalid session name is provided + * @throws IllegalStateException when the drools controller is in an invalid state + */ + protected PolicySession getSession(String sessionName) { + if (sessionName == null || sessionName.isEmpty()) { + throw new IllegalArgumentException("A Session Name must be provided"); + } + + List<PolicySession> sessions = this.getSessions(); + for (PolicySession session : sessions) { + if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName())) { + return session; + } + } + + throw invalidSessNameEx(sessionName); + } + + private IllegalArgumentException invalidSessNameEx(String sessionName) { + return new IllegalArgumentException("Invalid Session Name: " + sessionName); + } + + @Override + public Map<String,Integer> factClassNames(String sessionName) { + if (sessionName == null || sessionName.isEmpty()) { + throw invalidSessNameEx(sessionName); + } + + Map<String,Integer> classNames = new HashMap<>(); + + PolicySession session = getSession(sessionName); + KieSession kieSession = session.getKieSession(); + + Collection<FactHandle> facts = session.getKieSession().getFactHandles(); + for (FactHandle fact : facts) { + try { + String className = kieSession.getObject(fact).getClass().getName(); + if (classNames.containsKey(className)) { + classNames.put(className, classNames.get(className) + 1); + } else { + classNames.put(className, 1); + } + } catch (Exception e) { + logger.warn("Object cannot be retrieved from fact {}", fact, e); + } + } + + return classNames; + } + + @Override + public long factCount(String sessionName) { + if (sessionName == null || sessionName.isEmpty()) { + throw invalidSessNameEx(sessionName); + } + + PolicySession session = getSession(sessionName); + return session.getKieSession().getFactCount(); + } + + @Override + public List<Object> facts(String sessionName, String className, boolean delete) { + if (sessionName == null || sessionName.isEmpty()) { + throw invalidSessNameEx(sessionName); + } + + if (className == null || className.isEmpty()) { + throw new IllegalArgumentException("Invalid Class Name: " + className); + } + + Class<?> factClass = + ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className); + if (factClass == null) { + throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className); + } + + PolicySession session = getSession(sessionName); + KieSession kieSession = session.getKieSession(); + + List<Object> factObjects = new ArrayList<>(); + + Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass)); + for (FactHandle factHandle : factHandles) { + try { + factObjects.add(kieSession.getObject(factHandle)); + if (delete) { + kieSession.delete(factHandle); + } + } catch (Exception e) { + logger.warn("Object cannot be retrieved from fact {}", factHandle, e); + } + } + + return factObjects; + } + + @Override + public List<Object> factQuery(String sessionName, String queryName, String queriedEntity, + boolean delete, Object... queryParams) { + if (sessionName == null || sessionName.isEmpty()) { + throw invalidSessNameEx(sessionName); + } + + if (queryName == null || queryName.isEmpty()) { + throw new IllegalArgumentException("Invalid Query Name: " + queryName); + } + + if (queriedEntity == null || queriedEntity.isEmpty()) { + throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity); + } + + PolicySession session = getSession(sessionName); + KieSession kieSession = session.getKieSession(); + + boolean found = false; + for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) { + for (Query q : kiePackage.getQueries()) { + if (q.getName() != null && q.getName().equals(queryName)) { + found = true; + break; + } + } + } + if (!found) { + throw new IllegalArgumentException("Invalid Query Name: " + queryName); + } + + List<Object> factObjects = new ArrayList<>(); + + QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams); + for (QueryResultsRow row : queryResults) { + try { + factObjects.add(row.get(queriedEntity)); + if (delete) { + kieSession.delete(row.getFactHandle(queriedEntity)); + } + } catch (Exception e) { + logger.warn("Object cannot be retrieved from row: {}", row, e); + } + } + + return factObjects; + } + + @Override + public Class<?> fetchModelClass(String className) { + return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className); + } + + /** + * Get recent source events. + * + * @return the recentSourceEvents + */ + @Override + public Object[] getRecentSourceEvents() { + synchronized (this.recentSourceEvents) { + Object[] events = new Object[recentSourceEvents.size()]; + return recentSourceEvents.toArray(events); + } + } + + /** + * Get recent sink events. + * + * @return the recentSinkEvents + */ + @Override + public String[] getRecentSinkEvents() { + synchronized (this.recentSinkEvents) { + String[] events = new String[recentSinkEvents.size()]; + return recentSinkEvents.toArray(events); + } + } + + @Override + public boolean isBrained() { + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder + .append("MavenDroolsController [policyContainer=") + .append((policyContainer != null) ? policyContainer.getName() : "NULL") + .append(":") + .append(", alive=") + .append(alive) + .append(", locked=") + .append(", modelClassLoaderHash=") + .append(modelClassLoaderHash) + .append("]"); + return builder.toString(); + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java index 31ac1008..93cbc65e 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java @@ -31,153 +31,153 @@ import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; /** - * no-op Drools Controller + * no-op Drools Controller. */ public class NullDroolsController implements DroolsController { - @Override - public boolean start() { - return false; - } - - @Override - public boolean stop() { - return false; - } - - @Override - public void shutdown() { - return; - } - - @Override - public void halt() { - return; - } - - @Override - public boolean isAlive() { - return false; - } - - @Override - public boolean lock() { - return false; - } - - @Override - public boolean unlock() { - return false; - } - - @Override - public boolean isLocked() { - return false; - } - - @Override - public String getGroupId() { - return NO_GROUP_ID; - } - - @Override - public String getArtifactId() { - return NO_ARTIFACT_ID; - } - - @Override - public String getVersion() { - return NO_VERSION; - } - - @Override - public List<String> getSessionNames() { - return new ArrayList<>(); - } - - @Override - public List<String> getCanonicalSessionNames() { - return new ArrayList<>(); - } - - @Override - public boolean offer(String topic, String event) { - return false; - } - - @Override - public boolean deliver(TopicSink sink, Object event) { - throw new IllegalStateException(makeInvokeMsg()); - } - - @Override - public Object[] getRecentSourceEvents() { - return new String[0]; - } - - @Override - public PolicyContainer getContainer() { - return null; - } - - @Override - public String[] getRecentSinkEvents() { - return new String[0]; - } - - @Override - public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) { - throw new IllegalStateException(makeInvokeMsg()); - } - - @Override - public Class<?> fetchModelClass(String className) { - throw new IllegalArgumentException(makeInvokeMsg()); - } - - @Override - public boolean isBrained() { - return false; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("NullDroolsController []"); - return builder.toString(); - } - - @Override - public void updateToVersion(String newGroupId, String newArtifactId, String newVersion, - List<TopicCoderFilterConfiguration> decoderConfigurations, - List<TopicCoderFilterConfiguration> encoderConfigurations) - throws LinkageError { - throw new IllegalArgumentException(makeInvokeMsg()); - } - - @Override - public Map<String, Integer> factClassNames(String sessionName) { - return new HashMap<>(); - } - - @Override - public long factCount(String sessionName) { - return 0; - } - - @Override - public List<Object> facts(String sessionName, String className, boolean delete) { - return new ArrayList<>(); - } - - @Override - public List<Object> factQuery(String sessionName, String queryName, - String queriedEntity, - boolean delete, Object... queryParams) { - return new ArrayList<>(); - } - - private String makeInvokeMsg() { - return this.getClass().getCanonicalName() + " invoked"; - } + @Override + public boolean start() { + return false; + } + + @Override + public boolean stop() { + return false; + } + + @Override + public void shutdown() { + return; + } + + @Override + public void halt() { + return; + } + + @Override + public boolean isAlive() { + return false; + } + + @Override + public boolean lock() { + return false; + } + + @Override + public boolean unlock() { + return false; + } + + @Override + public boolean isLocked() { + return false; + } + + @Override + public String getGroupId() { + return NO_GROUP_ID; + } + + @Override + public String getArtifactId() { + return NO_ARTIFACT_ID; + } + + @Override + public String getVersion() { + return NO_VERSION; + } + + @Override + public List<String> getSessionNames() { + return new ArrayList<>(); + } + + @Override + public List<String> getCanonicalSessionNames() { + return new ArrayList<>(); + } + + @Override + public boolean offer(String topic, String event) { + return false; + } + + @Override + public boolean deliver(TopicSink sink, Object event) { + throw new IllegalStateException(makeInvokeMsg()); + } + + @Override + public Object[] getRecentSourceEvents() { + return new String[0]; + } + + @Override + public PolicyContainer getContainer() { + return null; + } + + @Override + public String[] getRecentSinkEvents() { + return new String[0]; + } + + @Override + public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) { + throw new IllegalStateException(makeInvokeMsg()); + } + + @Override + public Class<?> fetchModelClass(String className) { + throw new IllegalArgumentException(makeInvokeMsg()); + } + + @Override + public boolean isBrained() { + return false; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("NullDroolsController []"); + return builder.toString(); + } + + @Override + public void updateToVersion(String newGroupId, String newArtifactId, String newVersion, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) + throws LinkageError { + throw new IllegalArgumentException(makeInvokeMsg()); + } + + @Override + public Map<String, Integer> factClassNames(String sessionName) { + return new HashMap<>(); + } + + @Override + public long factCount(String sessionName) { + return 0; + } + + @Override + public List<Object> facts(String sessionName, String className, boolean delete) { + return new ArrayList<>(); + } + + @Override + public List<Object> factQuery(String sessionName, String queryName, + String queriedEntity, + boolean delete, Object... queryParams) { + return new ArrayList<>(); + } + + private String makeInvokeMsg() { + return this.getClass().getCanonicalName() + " invoked"; + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureAPI.java b/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureAPI.java index f4158804..135e1c58 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureAPI.java +++ b/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureAPI.java @@ -29,28 +29,32 @@ import org.onap.policy.drools.utils.OrderedServiceImpl; */ public interface DroolsControllerFeatureAPI extends OrderedService { - /** - * intercepts before the Drools Controller gives the Policy Container a fact to - * insert into its Policy Sessions - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - default boolean beforeInsert(DroolsController controller, Object fact) {return false;} + /** + * intercepts before the Drools Controller gives the Policy Container a fact to + * insert into its Policy Sessions. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + default boolean beforeInsert(DroolsController controller, Object fact) { + return false; + } - /** - * called after a fact is injected into the Policy Container - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - default boolean afterInsert(DroolsController controller, Object fact, boolean successInsert) {return false;} + /** + * called after a fact is injected into the Policy Container. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + default boolean afterInsert(DroolsController controller, Object fact, boolean successInsert) { + return false; + } - /** - * Feature providers implementing this interface - */ - public static final OrderedServiceImpl<DroolsControllerFeatureAPI> providers = - new OrderedServiceImpl<>(DroolsControllerFeatureAPI.class); + /** + * Feature providers implementing this interface. + */ + public static final OrderedServiceImpl<DroolsControllerFeatureAPI> providers = + new OrderedServiceImpl<>(DroolsControllerFeatureAPI.class); } diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureAPI.java b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureAPI.java index 94d1b15e..286948ed 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureAPI.java +++ b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureAPI.java @@ -28,194 +28,230 @@ import org.onap.policy.drools.utils.OrderedService; import org.onap.policy.drools.utils.OrderedServiceImpl; public interface PolicyControllerFeatureAPI extends OrderedService { - - /** - * Feature providers implementing this interface - */ - public static final OrderedServiceImpl<PolicyControllerFeatureAPI> providers = - new OrderedServiceImpl<PolicyControllerFeatureAPI>(PolicyControllerFeatureAPI.class); - - /** - * called before creating a controller with name 'name' and - * properties 'properties' - * - * @param name name of the the controller - * @param properties configuration properties - * - * @return a policy controller. A take over of the creation operation - * is performed by returning a non-null policy controller. - * 'null' indicates that no take over has taken place, and processing should - * continue to the next feature provider. - */ - public default PolicyController beforeCreate(String name, Properties properties) {return null;} - - /** - * called after creating a controller with name 'name' - * - * @param controller controller - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterCreate(PolicyController controller) {return false;} - - /** - * intercept before the Policy Controller is started. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeStart(PolicyController controller) {return false;} - - /** - * intercept after the Policy Controller is started. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterStart(PolicyController controller) {return false;} - - /** - * intercept before the Policy Controller is stopped. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean beforeStop(PolicyController controller) {return false;} - - /** - * intercept after the Policy Controller is stopped - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.d. - */ - public default boolean afterStop(PolicyController controller) {return false;} - - /** - * intercept before the Policy Controller is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeLock(PolicyController controller) {return false;} - - /** - * intercept after the Policy Controller is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean afterLock(PolicyController controller) {return false;} - - /** - * intercept before the Policy Controller is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeUnlock(PolicyController controller) {return false;} - - /** - * intercept after the Policy Controller is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterUnlock(PolicyController controller) {return false;} - - /** - * intercept before the Policy Controller is shut down - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean beforeShutdown(PolicyController controller) {return false;} - - /** - * called after the Policy Controller is shut down - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterShutdown(PolicyController controller) {return false;} - - /** - * intercept before the Policy Controller is halted - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean beforeHalt(PolicyController controller) {return false;} - - /** - * called after the Policy Controller is halted - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterHalt(PolicyController controller) {return false;} - - - /** - * intercept before the Policy Controller is offered an event - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeOffer(PolicyController controller, - CommInfrastructure protocol, - String topic, - String event) {return false;} - - /** - * called after the Policy Controller processes an event offer - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterOffer(PolicyController controller, - CommInfrastructure protocol, - String topic, - String event, - boolean success) {return false;} - - /** - * intercept before the Policy Controller delivers (posts) an event - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeDeliver(PolicyController controller, - CommInfrastructure protocol, - String topic, - Object event) {return false;} - - /** - * called after the Policy Controller delivers (posts) an event - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterDeliver(PolicyController controller, - CommInfrastructure protocol, - String topic, - Object event, - boolean success) {return false;} + + /** + * Feature providers implementing this interface. + */ + public static final OrderedServiceImpl<PolicyControllerFeatureAPI> providers = + new OrderedServiceImpl<>(PolicyControllerFeatureAPI.class); + + /** + * called before creating a controller with name 'name' and + * properties 'properties'. + * + * @param name name of the the controller + * @param properties configuration properties + * + * @return a policy controller. A take over of the creation operation + * is performed by returning a non-null policy controller. + * 'null' indicates that no take over has taken place, and processing should + * continue to the next feature provider. + */ + public default PolicyController beforeCreate(String name, Properties properties) { + return null; + } + + /** + * called after creating a controller with name 'name'. + * + * @param controller controller + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterCreate(PolicyController controller) { + return false; + } + + /** + * intercept before the Policy Controller is started. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeStart(PolicyController controller) { + return false; + } + + /** + * intercept after the Policy Controller is started. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterStart(PolicyController controller) { + return false; + } + + /** + * intercept before the Policy Controller is stopped. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean beforeStop(PolicyController controller) { + return false; + } + + /** + * intercept after the Policy Controller is stopped. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.d. + */ + public default boolean afterStop(PolicyController controller) { + return false; + } + + /** + * intercept before the Policy Controller is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeLock(PolicyController controller) { + return false; + } + + /** + * intercept after the Policy Controller is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean afterLock(PolicyController controller) { + return false; + } + + /** + * intercept before the Policy Controller is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeUnlock(PolicyController controller) { + return false; + } + + /** + * intercept after the Policy Controller is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterUnlock(PolicyController controller) { + return false; + } + + /** + * intercept before the Policy Controller is shut down. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean beforeShutdown(PolicyController controller) { + return false; + } + + /** + * called after the Policy Controller is shut down. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterShutdown(PolicyController controller) { + return false; + } + + /** + * intercept before the Policy Controller is halted. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean beforeHalt(PolicyController controller) { + return false; + } + + /** + * called after the Policy Controller is halted. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterHalt(PolicyController controller) { + return false; + } + + + /** + * intercept before the Policy Controller is offered an event. + * + * @return true if this feature intercepts and takes ownership. + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeOffer(PolicyController controller, + CommInfrastructure protocol, + String topic, + String event) { + return false; + } + + /** + * called after the Policy Controller processes an event offer. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterOffer(PolicyController controller, + CommInfrastructure protocol, + String topic, + String event, + boolean success) { + return false; + } + + /** + * intercept before the Policy Controller delivers (posts) an event. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeDeliver(PolicyController controller, + CommInfrastructure protocol, + String topic, + Object event) { + return false; + } + + /** + * called after the Policy Controller delivers (posts) an event. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterDeliver(PolicyController controller, + CommInfrastructure protocol, + String topic, + Object event, + boolean success) { + return false; + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureAPI.java b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureAPI.java index 956401ad..66b206b6 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureAPI.java +++ b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureAPI.java @@ -30,172 +30,208 @@ import org.onap.policy.drools.utils.OrderedServiceImpl; * Policy Engine Feature API. * Provides Interception Points during the Policy Engine lifecycle. */ -public interface PolicyEngineFeatureAPI extends OrderedService { - /** - * Feature providers implementing this interface - */ - public static final OrderedServiceImpl<PolicyEngineFeatureAPI> providers = - new OrderedServiceImpl<>(PolicyEngineFeatureAPI.class); - - /** - * intercept before the Policy Engine is commanded to boot. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeBoot(PolicyEngine engine, String[] cliArgs) {return false;}; - - /** - * intercept after the Policy Engine is booted. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterBoot(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine is configured. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeConfigure(PolicyEngine engine, Properties properties) {return false;}; - - /** - * intercept after the Policy Engine is configured. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterConfigure(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine goes active. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeActivate(PolicyEngine engine) {return false;}; - - /** - * intercept after the Policy Engine goes active. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterActivate(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine goes standby. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeDeactivate(PolicyEngine engine) {return false;}; - - /** - * intercept after the Policy Engine goes standby. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterDeactivate(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine is started. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeStart(PolicyEngine engine) {return false;}; - - /** - * intercept after the Policy Engine is started. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterStart(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine is stopped. - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean beforeStop(PolicyEngine engine) {return false;}; - - /** - * intercept after the Policy Engine is stopped - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.d. - */ - public default boolean afterStop(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeLock(PolicyEngine engine) {return false;}; - - /** - * intercept after the Policy Engine is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean afterLock(PolicyEngine engine) {return false;}; - - /** - * intercept before the Policy Engine is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean beforeUnlock(PolicyEngine engine) {return false;}; - - /** - * intercept after the Policy Engine is locked - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterUnlock(PolicyEngine engine) {return false;}; - - /** - * intercept the Policy Engine is shut down - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise.. - */ - public default boolean beforeShutdown(PolicyEngine engine){return false;}; - - /** - * called after the Policy Engine is shut down - * - * @return true if this feature intercepts and takes ownership - * of the operation preventing the invocation of - * lower priority features. False, otherwise. - */ - public default boolean afterShutdown(PolicyEngine engine) {return false;}; +public interface PolicyEngineFeatureAPI extends OrderedService { + /** + * Feature providers implementing this interface. + */ + public static final OrderedServiceImpl<PolicyEngineFeatureAPI> providers = + new OrderedServiceImpl<>(PolicyEngineFeatureAPI.class); + + /** + * intercept before the Policy Engine is commanded to boot. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeBoot(PolicyEngine engine, String[] cliArgs) { + return false; + } + + /** + * intercept after the Policy Engine is booted. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterBoot(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine is configured. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeConfigure(PolicyEngine engine, Properties properties) { + return false; + } + + /** + * intercept after the Policy Engine is configured. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterConfigure(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine goes active. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeActivate(PolicyEngine engine) { + return false; + } + + /** + * intercept after the Policy Engine goes active. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterActivate(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine goes standby. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeDeactivate(PolicyEngine engine) { + return false; + } + + /** + * intercept after the Policy Engine goes standby. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterDeactivate(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine is started. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeStart(PolicyEngine engine) { + return false; + } + + /** + * intercept after the Policy Engine is started. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterStart(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine is stopped. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean beforeStop(PolicyEngine engine) { + return false; + } + + /** + * intercept after the Policy Engine is stopped. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.d. + */ + public default boolean afterStop(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeLock(PolicyEngine engine) { + return false; + } + + /** + * intercept after the Policy Engine is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean afterLock(PolicyEngine engine) { + return false; + } + + /** + * intercept before the Policy Engine is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean beforeUnlock(PolicyEngine engine) { + return false; + } + + /** + * intercept after the Policy Engine is locked. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterUnlock(PolicyEngine engine) { + return false; + } + + /** + * intercept the Policy Engine is shut down. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise.. + */ + public default boolean beforeShutdown(PolicyEngine engine){ + return false; + } + + /** + * called after the Policy Engine is shut down. + * + * @return true if this feature intercepts and takes ownership + * of the operation preventing the invocation of + * lower priority features. False, otherwise. + */ + public default boolean afterShutdown(PolicyEngine engine) { + return false; + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/persistence/FileSystemPersistence.java b/policy-management/src/main/java/org/onap/policy/drools/persistence/FileSystemPersistence.java index b1e49ec2..6864a346 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/persistence/FileSystemPersistence.java +++ b/policy-management/src/main/java/org/onap/policy/drools/persistence/FileSystemPersistence.java @@ -17,6 +17,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.policy.drools.persistence; import java.io.File; @@ -37,276 +38,282 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Properties based Persistence + * Properties based Persistence. */ public class FileSystemPersistence implements SystemPersistence { - /** - * policy controllers suffix - */ - public static final String CONTROLLER_SUFFIX_IDENTIFIER = "-controller"; - - /** - * policy controller properties file suffix - */ - public static final String PROPERTIES_FILE_CONTROLLER_SUFFIX = - CONTROLLER_SUFFIX_IDENTIFIER + ".properties"; - - /** - * policy controller properties file suffix - */ - public static final String PROPERTIES_FILE_CONTROLLER_BACKUP_SUFFIX = - CONTROLLER_SUFFIX_IDENTIFIER + ".properties.bak"; - - /** - * policy engine properties file name - */ - public static final String PROPERTIES_FILE_ENGINE = "policy-engine.properties"; - - /** - * Installation environment suffix for files - */ - public static final String ENV_SUFFIX = ".environment"; - - /** - * configuration directory - */ - protected Path configurationDirectory = Paths.get(DEFAULT_CONFIGURATION_DIR); - - /** - * logger - */ - private static final Logger logger = LoggerFactory.getLogger(FileSystemPersistence.class); - - - @Override - public void setConfigurationDir(String configDir) { - String tempConfigDir = configDir; - if (tempConfigDir == null) { - tempConfigDir = DEFAULT_CONFIGURATION_DIR; - this.configurationDirectory = Paths.get(DEFAULT_CONFIGURATION_DIR); + /** + * policy controllers suffix. + */ + public static final String CONTROLLER_SUFFIX_IDENTIFIER = "-controller"; + + /** + * policy controller properties file suffix. + */ + public static final String PROPERTIES_FILE_CONTROLLER_SUFFIX = + CONTROLLER_SUFFIX_IDENTIFIER + ".properties"; + + /** + * policy controller properties file suffix. + */ + public static final String PROPERTIES_FILE_CONTROLLER_BACKUP_SUFFIX = + CONTROLLER_SUFFIX_IDENTIFIER + ".properties.bak"; + + /** + * policy engine properties file name. + */ + public static final String PROPERTIES_FILE_ENGINE = "policy-engine.properties"; + + /** + * Installation environment suffix for files. + */ + public static final String ENV_SUFFIX = ".environment"; + + /** + * configuration directory. + */ + protected Path configurationDirectory = Paths.get(DEFAULT_CONFIGURATION_DIR); + + /** + * logger. + */ + private static final Logger logger = LoggerFactory.getLogger(FileSystemPersistence.class); + + + @Override + public void setConfigurationDir(String configDir) { + String tempConfigDir = configDir; + + if (tempConfigDir == null) { + tempConfigDir = DEFAULT_CONFIGURATION_DIR; + this.configurationDirectory = Paths.get(DEFAULT_CONFIGURATION_DIR); + } + + if (!tempConfigDir.equals(DEFAULT_CONFIGURATION_DIR)) { + this.configurationDirectory = Paths.get(tempConfigDir); + } + + if (Files.notExists(this.configurationDirectory)) { + try { + Files.createDirectories(this.configurationDirectory); + } catch (final IOException e) { + throw new IllegalStateException("cannot create " + this.configurationDirectory, e); + } + } + + if (!Files.isDirectory(this.configurationDirectory)) { + throw new IllegalStateException( + "config directory: " + this.configurationDirectory + " is not a directory"); + } + } + + @Override + public Path getConfigurationPath() { + return this.configurationDirectory; } - if (!tempConfigDir.equals(DEFAULT_CONFIGURATION_DIR)) - this.configurationDirectory = Paths.get(tempConfigDir); + @Override + public Properties getEngineProperties() { + final Path policyEnginePath = + Paths.get(this.configurationDirectory.toString(), PROPERTIES_FILE_ENGINE); + try { + if (Files.exists(policyEnginePath)) { + return PropertyUtil.getProperties(policyEnginePath.toFile()); + } + } catch (final Exception e) { + logger.warn("{}: could not find {}", this, policyEnginePath, e); + } - if (Files.notExists(this.configurationDirectory)) { - try { - Files.createDirectories(this.configurationDirectory); - } catch (final IOException e) { - throw new IllegalStateException("cannot create " + this.configurationDirectory, e); - } + return null; } - if (!Files.isDirectory(this.configurationDirectory)) - throw new IllegalStateException( - "config directory: " + this.configurationDirectory + " is not a directory"); - } - - @Override - public Path getConfigurationPath() { - return this.configurationDirectory; - } - - @Override - public Properties getEngineProperties() { - final Path policyEnginePath = - Paths.get(this.configurationDirectory.toString(), PROPERTIES_FILE_ENGINE); - try { - if (Files.exists(policyEnginePath)) - return PropertyUtil.getProperties(policyEnginePath.toFile()); - } catch (final Exception e) { - logger.warn("{}: could not find {}", this, policyEnginePath, e); + @Override + public boolean backupController(String controllerName) { + final Path controllerPropertiesPath = Paths.get(this.configurationDirectory.toString(), + controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX); + + if (Files.exists(controllerPropertiesPath)) { + try { + logger.info("{}: there is an existing configuration file @ {} ", this, + controllerPropertiesPath); + final Path controllerPropertiesBakPath = Paths.get(this.configurationDirectory.toString(), + controllerName + PROPERTIES_FILE_CONTROLLER_BACKUP_SUFFIX); + Files.copy(controllerPropertiesPath, controllerPropertiesBakPath, + StandardCopyOption.REPLACE_EXISTING); + } catch (final Exception e) { + logger.warn("{}: {} cannot be backed up", this, controllerName, e); + return false; + } + } + + return true; } - return null; - } - - @Override - public boolean backupController(String controllerName) { - final Path controllerPropertiesPath = Paths.get(this.configurationDirectory.toString(), - controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX); - - if (Files.exists(controllerPropertiesPath)) { - try { - logger.info("{}: there is an existing configuration file @ {} ", this, - controllerPropertiesPath); - final Path controllerPropertiesBakPath = Paths.get(this.configurationDirectory.toString(), - controllerName + PROPERTIES_FILE_CONTROLLER_BACKUP_SUFFIX); - Files.copy(controllerPropertiesPath, controllerPropertiesBakPath, - StandardCopyOption.REPLACE_EXISTING); - } catch (final Exception e) { - logger.warn("{}: {} cannot be backed up", this, controllerName, e); - return false; - } + @Override + public boolean storeController(String controllerName, Object configuration) { + if (!(configuration instanceof Properties)) { + throw new IllegalArgumentException( + "configuration must be of type properties to be handled by this manager"); + } + + final Properties properties = (Properties) configuration; + + final Path controllerPropertiesPath = Paths.get(this.configurationDirectory.toString(), + controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX); + if (Files.exists(controllerPropertiesPath)) { + try { + final Properties oldProperties = + PropertyUtil.getProperties(controllerPropertiesPath.toFile()); + if (oldProperties.equals(properties)) { + logger.info( + "{}: noop: a properties file with the same contents exists for controller {}.", this, + controllerName); + return true; + } else { + this.backupController(controllerName); + } + } catch (final Exception e) { + logger.info("{}: no existing {} properties {}", this, controllerName, e); + // continue + } + } + + final File controllerPropertiesFile = controllerPropertiesPath.toFile(); + try (FileWriter writer = new FileWriter(controllerPropertiesFile)) { + properties.store(writer, "Machine created Policy Controller Configuration"); + } catch (final Exception e) { + logger.warn("{}: {} cannot be saved", this, controllerName, e); + return false; + } + + return true; } - return true; - } - - @Override - public boolean storeController(String controllerName, Object configuration) { - if (!(configuration instanceof Properties)) - throw new IllegalArgumentException( - "configuration must be of type properties to be handled by this manager"); - - final Properties properties = (Properties) configuration; - - final Path controllerPropertiesPath = Paths.get(this.configurationDirectory.toString(), - controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX); - if (Files.exists(controllerPropertiesPath)) { - try { - final Properties oldProperties = - PropertyUtil.getProperties(controllerPropertiesPath.toFile()); - if (oldProperties.equals(properties)) { - logger.info( - "{}: noop: a properties file with the same contents exists for controller {}.", this, - controllerName); - return true; - } else { - this.backupController(controllerName); + @Override + public boolean deleteController(String controllerName) { + final Path controllerPropertiesPath = Paths.get(this.configurationDirectory.toString(), + controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX); + + if (Files.exists(controllerPropertiesPath)) { + try { + final Path controllerPropertiesBakPath = Paths.get(this.configurationDirectory.toString(), + controllerName + PROPERTIES_FILE_CONTROLLER_BACKUP_SUFFIX); + Files.move(controllerPropertiesPath, controllerPropertiesBakPath, + StandardCopyOption.REPLACE_EXISTING); + } catch (final Exception e) { + logger.warn("{}: {} cannot be deleted", this, controllerName, e); + return false; + } } - } catch (final Exception e) { - logger.info("{}: no existing {} properties {}", this, controllerName, e); - // continue - } + + return true; } - final File controllerPropertiesFile = controllerPropertiesPath.toFile(); - try (FileWriter writer = new FileWriter(controllerPropertiesFile)) { - properties.store(writer, "Machine created Policy Controller Configuration"); - } catch (final Exception e) { - logger.warn("{}: {} cannot be saved", this, controllerName, e); - return false; + @Override + public Properties getControllerProperties(String controllerName) { + return this.getProperties(controllerName + CONTROLLER_SUFFIX_IDENTIFIER); } - return true; - } - - @Override - public boolean deleteController(String controllerName) { - final Path controllerPropertiesPath = Paths.get(this.configurationDirectory.toString(), - controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX); - - if (Files.exists(controllerPropertiesPath)) { - try { - final Path controllerPropertiesBakPath = Paths.get(this.configurationDirectory.toString(), - controllerName + PROPERTIES_FILE_CONTROLLER_BACKUP_SUFFIX); - Files.move(controllerPropertiesPath, controllerPropertiesBakPath, - StandardCopyOption.REPLACE_EXISTING); - } catch (final Exception e) { - logger.warn("{}: {} cannot be deleted", this, controllerName, e); - return false; - } + @Override + public List<Properties> getControllerProperties() { + final List<Properties> controllers = new ArrayList<>(); + final File[] controllerFiles = this.sortedListFiles(); + for (final File controllerFile : controllerFiles) { + if (controllerFile.getName().endsWith(PROPERTIES_FILE_CONTROLLER_SUFFIX)) { + final int idxSuffix = controllerFile.getName().indexOf(PROPERTIES_FILE_CONTROLLER_SUFFIX); + final int lastIdxSuffix = + controllerFile.getName().lastIndexOf(PROPERTIES_FILE_CONTROLLER_SUFFIX); + if (idxSuffix != lastIdxSuffix) { + throw new IllegalArgumentException( + "Improper naming of controller properties file: " + "Expected <controller-name>" + + FileSystemPersistence.PROPERTIES_FILE_CONTROLLER_SUFFIX); + } + + final String name = controllerFile.getName().substring(0, lastIdxSuffix); + try { + final Properties controllerProperties = this.getControllerProperties(name); + final String controllerName = + controllerProperties.getProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME); + if (controllerName == null) { + controllerProperties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, name); + } else if (!controllerName.equals(name)) { + logger.error("{}: mismatch controller named {} with file name {}", this, controllerName, + controllerFile.getName()); + continue; + } + controllers.add(this.getControllerProperties(name)); + } catch (final Exception e) { + logger.error("{}: cannot obtain properties for controller {} because of {}", name, + e.getMessage(), e); + } + } + } + return controllers; } - return true; - } - - @Override - public Properties getControllerProperties(String controllerName) { - return this.getProperties(controllerName + CONTROLLER_SUFFIX_IDENTIFIER); - } - - @Override - public List<Properties> getControllerProperties() { - final List<Properties> controllers = new ArrayList<>(); - final File[] controllerFiles = this.sortedListFiles(); - for (final File controllerFile : controllerFiles) { - if (controllerFile.getName().endsWith(PROPERTIES_FILE_CONTROLLER_SUFFIX)) { - final int idxSuffix = controllerFile.getName().indexOf(PROPERTIES_FILE_CONTROLLER_SUFFIX); - final int lastIdxSuffix = - controllerFile.getName().lastIndexOf(PROPERTIES_FILE_CONTROLLER_SUFFIX); - if (idxSuffix != lastIdxSuffix) - throw new IllegalArgumentException( - "Improper naming of controller properties file: " + "Expected <controller-name>" - + FileSystemPersistence.PROPERTIES_FILE_CONTROLLER_SUFFIX); - - final String name = controllerFile.getName().substring(0, lastIdxSuffix); + @Override + public Properties getProperties(String name) { + final Path propertiesPath = + Paths.get(this.configurationDirectory.toString(), name + ".properties"); + + if (!Files.exists(propertiesPath)) { + throw new IllegalArgumentException("properties for " + name + " are not persisted."); + } + try { - final Properties controllerProperties = this.getControllerProperties(name); - final String controllerName = - controllerProperties.getProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME); - if (controllerName == null) { - controllerProperties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, name); - } else if (!controllerName.equals(name)) { - logger.error("{}: mismatch controller named {} with file name {}", this, controllerName, - controllerFile.getName()); - continue; - } - controllers.add(this.getControllerProperties(name)); + return PropertyUtil.getProperties(propertiesPath.toFile()); } catch (final Exception e) { - logger.error("{}: cannot obtain properties for controller {} because of {}", name, - e.getMessage(), e); + logger.warn("{}: can't read properties @ {}", name, propertiesPath); + throw new IllegalArgumentException( + "can't read properties for " + name + " @ " + propertiesPath, e); } - } } - return controllers; - } - @Override - public Properties getProperties(String name) { - final Path propertiesPath = - Paths.get(this.configurationDirectory.toString(), name + ".properties"); - - if (!Files.exists(propertiesPath)) { - throw new IllegalArgumentException("properties for " + name + " are not persisted."); + @Override + public List<Properties> getEnvironmentProperties() { + final List<Properties> envs = new ArrayList<>(); + final File[] envFiles = this.sortedListFiles(); + for (final File envFile : envFiles) { + if (envFile.getName().endsWith(ENV_SUFFIX)) { + final String name = envFile.getName().substring(0, envFile.getName().indexOf(ENV_SUFFIX)); + try { + envs.add(this.getEnvironmentProperties(name)); + } catch (final Exception e) { + logger.error("{}: cannot get environment {} because of {}", name, e.getMessage(), e); + } + } + } + return envs; } - try { - return PropertyUtil.getProperties(propertiesPath.toFile()); - } catch (final Exception e) { - logger.warn("{}: can't read properties @ {}", name, propertiesPath); - throw new IllegalArgumentException( - "can't read properties for " + name + " @ " + propertiesPath, e); - } - } - - @Override - public List<Properties> getEnvironmentProperties() { - final List<Properties> envs = new ArrayList<>(); - final File[] envFiles = this.sortedListFiles(); - for (final File envFile : envFiles) { - if (envFile.getName().endsWith(ENV_SUFFIX)) { - final String name = envFile.getName().substring(0, envFile.getName().indexOf(ENV_SUFFIX)); + @Override + public Properties getEnvironmentProperties(String name) { + final Path envPath = Paths.get(this.configurationDirectory.toString(), name + ENV_SUFFIX); + if (!Files.exists(envPath)) { + throw new IllegalArgumentException("{} environment" + name + " cannot be retrieved"); + } + try { - envs.add(this.getEnvironmentProperties(name)); + return PropertyUtil.getProperties(envPath.toFile()); } catch (final Exception e) { - logger.error("{}: cannot get environment {} because of {}", name, e.getMessage(), e); + throw new IllegalArgumentException("cannot read environment " + name, e); } - } - } - return envs; - } - - @Override - public Properties getEnvironmentProperties(String name) { - final Path envPath = Paths.get(this.configurationDirectory.toString(), name + ENV_SUFFIX); - if (!Files.exists(envPath)) { - throw new IllegalArgumentException("{} environment" + name + " cannot be retrieved"); } - try { - return PropertyUtil.getProperties(envPath.toFile()); - } catch (final Exception e) { - throw new IllegalArgumentException("cannot read environment " + name, e); + /** + * provides a list of files sorted by name in ascending order in the configuration directory. + */ + protected File[] sortedListFiles() { + final File[] dirFiles = this.configurationDirectory.toFile().listFiles(); + Arrays.sort(dirFiles, (a, b) -> a.getName().compareTo(b.getName())); + return dirFiles; } - } - - /** - * provides a list of files sorted by name in ascending order in the configuration directory - */ - protected File[] sortedListFiles() { - final File[] dirFiles = this.configurationDirectory.toFile().listFiles(); - Arrays.sort(dirFiles, (a, b) -> a.getName().compareTo(b.getName())); - return dirFiles; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("FileSystemPersistence [configurationDirectory=") + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("FileSystemPersistence [configurationDirectory=") .append(this.configurationDirectory).append("]"); - return builder.toString(); - } + return builder.toString(); + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/persistence/SystemPersistence.java b/policy-management/src/main/java/org/onap/policy/drools/persistence/SystemPersistence.java index 0d0a33ce..34b27c23 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/persistence/SystemPersistence.java +++ b/policy-management/src/main/java/org/onap/policy/drools/persistence/SystemPersistence.java @@ -25,108 +25,107 @@ import java.util.List; import java.util.Properties; /** - * System Configuration + * System Configuration. */ public interface SystemPersistence { - /** - * configuration directory - */ - public static final String DEFAULT_CONFIGURATION_DIR = "config"; + /** + * configuration directory. + */ + public static final String DEFAULT_CONFIGURATION_DIR = "config"; - /** - * Persistence Manager. For now it is a file-based properties management, In the future, it will - * probably be DB based, so manager implementation will change. - */ - public static final SystemPersistence manager = new FileSystemPersistence(); + /** + * Persistence Manager. For now it is a file-based properties management, In the future, it will + * probably be DB based, so manager implementation will change. + */ + public static final SystemPersistence manager = new FileSystemPersistence(); - /** - * sets a configuration directory and ensures it exists - * - * @param configDir configuration directory or null to use the default one - */ - public void setConfigurationDir(String configDir); + /** + * sets a configuration directory and ensures it exists. + * + * @param configDir configuration directory or null to use the default one + */ + public void setConfigurationDir(String configDir); - /** - * get configuration directory path - * - * @return configuration directory path - */ - public Path getConfigurationPath(); + /** + * get configuration directory path. + * + * @return configuration directory path + */ + public Path getConfigurationPath(); - /** - * backs up a controller configuration. - * - * @param controllerName the controller name - * @return true if the configuration is backed up - */ - public boolean backupController(String controllerName); + /** + * backs up a controller configuration. + * + * @param controllerName the controller name + * @return true if the configuration is backed up + */ + public boolean backupController(String controllerName); - /** - * persists controller configuration - * - * @param controllerName the controller name - * @param configuration object containing the configuration - * - * @return true if storage is succesful, false otherwise - * @throws IllegalArgumentException if the configuration cannot be handled by the persistence - * manager - */ - public boolean storeController(String controllerName, Object configuration); + /** + * persists controller configuration. + * + * @param controllerName the controller name + * @param configuration object containing the configuration + * + * @return true if storage is succesful, false otherwise + * @throws IllegalArgumentException if the configuration cannot be handled by the persistence + * manager + */ + public boolean storeController(String controllerName, Object configuration); - /** - * delete controller configuration - * - * @param controllerName the controller name - * @return true if storage is succesful, false otherwise - */ - public boolean deleteController(String controllerName); + /** + * delete controller configuration. + * + * @param controllerName the controller name + * @return true if storage is succesful, false otherwise + */ + public boolean deleteController(String controllerName); - /** - * get controller properties - * - * @param controllerName controller name - * @return properties for this controller - * - * @throws IllegalArgumentException if the controller name does not lead to a properties - * configuration - */ - public Properties getControllerProperties(String controllerName); + /** + * get controller properties. + * + * @param controllerName controller name + * @return properties for this controller + * + * @throws IllegalArgumentException if the controller name does not lead to a properties + * configuration + */ + public Properties getControllerProperties(String controllerName); - /** - * get controllers configuration - * - * @return list of controllers properties - */ - public List<Properties> getControllerProperties(); + /** + * get controllers configuration. + * + * @return list of controllers properties + */ + public List<Properties> getControllerProperties(); - /** - * get environments - * - * @param environment name - */ - public List<Properties> getEnvironmentProperties(); + /** + * get environments. + * + */ + public List<Properties> getEnvironmentProperties(); - /** - * get environment properties - * - * @param environment name - */ - public Properties getEnvironmentProperties(String environmentName); + /** + * get environment properties. + * + * @param environmentName name + */ + public Properties getEnvironmentProperties(String environmentName); - /** - * get the engine properties - * - * @return the engine properties - */ - public Properties getEngineProperties(); + /** + * get the engine properties. + * + * @return the engine properties + */ + public Properties getEngineProperties(); - /** - * get properties by name - * - * @param name - * @return properties - * - * @throws IllegalArgumentException if the name does not lead to a properties configuration - */ - public Properties getProperties(String name); + /** + * get properties by name. + * + * @param name name + * @return properties + * + * @throws IllegalArgumentException if the name does not lead to a properties configuration + */ + public Properties getProperties(String name); } diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java index d0008f08..3e4e7ecd 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java @@ -38,344 +38,355 @@ import org.slf4j.LoggerFactory; * Coder (Encoder/Decoder) of Events. */ public interface EventProtocolCoder { - - /** - * singleton reference to the global event protocol coder - */ - public static EventProtocolCoder manager = new MultiplexorEventProtocolCoder(); - - public static class CoderFilters { - - /** - * coder class - */ - protected String factClass; - - /** - * filters to apply to the selection of the decodedClass; - */ - protected JsonProtocolFilter filter; - - /** - * classloader hash - */ - protected int modelClassLoaderHash; - - - /** - * constructor - * - * @param codedClass coder class - * @param filter filters to apply - */ - public CoderFilters(String codedClass, JsonProtocolFilter filter, int modelClassLoaderHash) { - this.factClass = codedClass; - this.filter = filter; - this.modelClassLoaderHash = modelClassLoaderHash; - } - - /** - * @return the codedClass - */ - public String getCodedClass() { - return factClass; - } - - /** - * @param codedClass the decodedClass to set - */ - public void setCodedClass(String codedClass) { - this.factClass = codedClass; - } - - /** - * @return the filter - */ - public JsonProtocolFilter getFilter() { - return filter; - } - - /** - * @param filter the filter to set - */ - public void setFilter(JsonProtocolFilter filter) { - this.filter = filter; - } - - public int getModelClassLoaderHash() { - return modelClassLoaderHash; - } - - public void setFromClassLoaderHash(int fromClassLoaderHash) { - this.modelClassLoaderHash = fromClassLoaderHash; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("CoderFilters [factClass=").append(factClass).append(", filter=").append(filter) - .append(", modelClassLoaderHash=").append(modelClassLoaderHash).append("]"); - return builder.toString(); - } - - } - - /** - * Adds a Decoder class to decode the protocol over this topic - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * @param eventClass the event class - * @param protocolFilter filters to selectively choose a particular decoder - * when there are multiples - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public void addDecoder(String groupId, String artifactId, - String topic, - String eventClass, - JsonProtocolFilter protocolFilter, - CustomGsonCoder customGsonCoder, - CustomJacksonCoder customJacksonCoder, - int modelClassLoaderHash); - - /** - * removes all decoders associated with the controller id - * @param groupId of the controller - * @param artifactId of the controller - * @param topic of the controller - * - * @throws IllegalArgumentException if invalid arguments have been provided - */ - void removeEncoders(String groupId, String artifactId, String topic); - - /** - * removes decoders associated with the controller id and topic - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * - * @throws IllegalArgumentException if invalid arguments have been provided - */ - public void removeDecoders(String groupId, String artifactId, String topic); - - /** - * Given a controller id and a topic, it gives back its filters - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * - * return list of decoders - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic); - - - /** - * Given a controller id and a topic, it gives back the decoding configuration - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * - * return decoding toolset - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic); - - /** - * Given a controller id and a topic, it gives back all the decoding configurations - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * - * return decoding toolset - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId); - - - /** - * gets all decoders associated with the group and artifact ids - * @param groupId of the controller - * @param artifactId of the controller - * - * @throws IllegalArgumentException if invalid arguments have been provided - */ - public List<CoderFilters> getDecoderFilters(String groupId, String artifactId); - - - /** - * Given a controller id and a topic, it gives back the classes that implements the encoding - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * - * return list of decoders - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic); - - /** - * gets all encoders associated with the group and artifact ids - * @param groupId of the controller - * @param artifactId of the controller - * - * @throws IllegalArgumentException if invalid arguments have been provided - */ - public List<CoderFilters> getEncoderFilters(String groupId, String artifactId); - - /** - * Given a controller id, a topic, and a classname, it gives back the classes that implements the decoding - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * @param classname classname - * - * return list of decoders - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public CoderFilters getDecoderFilters(String groupId, String artifactId, String topic, String classname); - - /** - * is there a decoder supported for the controller id and topic - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic protocol - * @return true if supported - */ - public boolean isDecodingSupported(String groupId, String artifactId, String topic); - - /** - * Adds a Encoder class to encode the protocol over this topic - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * @param eventClass the event class - * @param protocolFilter filters to selectively choose a particular decoder - * when there are multiples - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public void addEncoder(String groupId, String artifactId, String topic, - String eventClass, - JsonProtocolFilter protocolFilter, - CustomGsonCoder customGsonCoder, - CustomJacksonCoder customJacksonCoder, - int modelClassLoaderHash); - - /** - * is there an encoder supported for the controller id and topic - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic protocol - * @return true if supported - */ - public boolean isEncodingSupported(String groupId, String artifactId, String topic); - - /** - * get encoder based on coordinates and classname - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic protocol - * @param json event string - * @return - * @throws IllegalArgumentException invalid arguments passed in - */ - public CoderFilters getEncoderFilters(String groupId, String artifactId, String topic, String classname); - - /** - * get encoder based on topic and encoded class - * - * @param topic topic - * @param encodedClass encoded class - * @return - * @throws IllegalArgumentException invalid arguments passed in - */ - public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass); - - /** - * gets the identifier of the creator of the encoder - * - * @param topic topic - * @param encodedClass encoded class - * @return a drools controller - * @throws IllegalArgumentException invalid arguments passed in - */ - public DroolsController getDroolsController(String topic, Object encodedClass); - - /** - * gets the identifier of the creator of the encoder - * - * @param topic topic - * @param encodedClass encoded class - * @return list of drools controllers - * @throws IllegalArgumentException invalid arguments passed in - */ - public List<DroolsController> getDroolsControllers(String topic, Object encodedClass); - - /** - * decode topic's stringified event (json) to corresponding Event Object. - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic protocol - * @param json event string - * @return - * @throws IllegalArgumentException invalid arguments passed in - * @throws UnsupportedOperationException if the operation is not supported - * @throws IllegalStateException if the system is in an illegal state - */ - public Object decode(String groupId, String artifactId, String topic, String json); - - /** - * encodes topic's stringified event (json) to corresponding Event Object. - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic protocol - * @param event Object - * - * @throws IllegalArgumentException invalid arguments passed in - */ - public String encode(String groupId, String artifactId, String topic, Object event); - - /** - * encodes topic's stringified event (json) to corresponding Event Object. - * - * @param topic topic - * @param event event object - * - * @throws IllegalArgumentException invalid arguments passed in - * @throws UnsupportedOperationException operation cannot be performed - */ - public String encode(String topic, Object event); - - /** - * encodes topic's stringified event (json) to corresponding Event Object. - * - * @param topic topic - * @param event event object - * @param droolsController - * - * @throws IllegalArgumentException invalid arguments passed in - * @throws UnsupportedOperationException operation cannot be performed - */ - public String encode(String topic, Object event, DroolsController droolsController); + + /** + * singleton reference to the global event protocol coder. + */ + public static EventProtocolCoder manager = new MultiplexorEventProtocolCoder(); + + public static class CoderFilters { + + /** + * coder class. + */ + protected String factClass; + + /** + * filters to apply to the selection of the decodedClass. + */ + protected JsonProtocolFilter filter; + + /** + * classloader hash. + */ + protected int modelClassLoaderHash; + + + /** + * constructor. + * + * @param codedClass coder class + * @param filter filters to apply + */ + public CoderFilters(String codedClass, JsonProtocolFilter filter, int modelClassLoaderHash) { + this.factClass = codedClass; + this.filter = filter; + this.modelClassLoaderHash = modelClassLoaderHash; + } + + /** + * Get coded class. + * + * @return the codedClass + */ + public String getCodedClass() { + return factClass; + } + + /** + * Set coded class. + * + * @param codedClass the decodedClass to set + */ + public void setCodedClass(String codedClass) { + this.factClass = codedClass; + } + + /** + * Get filter. + * + * @return the filter + */ + public JsonProtocolFilter getFilter() { + return filter; + } + + /** + * Set filter. + * + * @param filter the filter to set + */ + public void setFilter(JsonProtocolFilter filter) { + this.filter = filter; + } + + public int getModelClassLoaderHash() { + return modelClassLoaderHash; + } + + public void setFromClassLoaderHash(int fromClassLoaderHash) { + this.modelClassLoaderHash = fromClassLoaderHash; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("CoderFilters [factClass=").append(factClass).append(", filter=").append(filter) + .append(", modelClassLoaderHash=").append(modelClassLoaderHash).append("]"); + return builder.toString(); + } + + } + + /** + * Adds a Decoder class to decode the protocol over this topic. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * @param eventClass the event class + * @param protocolFilter filters to selectively choose a particular decoder + * when there are multiples + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public void addDecoder(String groupId, String artifactId, + String topic, + String eventClass, + JsonProtocolFilter protocolFilter, + CustomGsonCoder customGsonCoder, + CustomJacksonCoder customJacksonCoder, + int modelClassLoaderHash); + + /** + * removes all decoders associated with the controller id. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic of the controller + * + * @throws IllegalArgumentException if invalid arguments have been provided + */ + void removeEncoders(String groupId, String artifactId, String topic); + + /** + * removes decoders associated with the controller id and topic. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * + * @throws IllegalArgumentException if invalid arguments have been provided + */ + public void removeDecoders(String groupId, String artifactId, String topic); + + /** + * Given a controller id and a topic, it gives back its filters. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * + * @return list of decoders + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic); + + + /** + * Given a controller id and a topic, it gives back the decoding configuration. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * + * @return decoding toolset + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic); + + /** + * Given a controller id and a topic, it gives back all the decoding configurations. + * + * @param groupId of the controller + * @param artifactId of the controller + * + * @return decoding toolset + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId); + + /** + * gets all decoders associated with the group and artifact ids. + * + * @param groupId of the controller + * @param artifactId of the controller + * + * @throws IllegalArgumentException if invalid arguments have been provided + */ + public List<CoderFilters> getDecoderFilters(String groupId, String artifactId); + + + /** + * Given a controller id, a topic, and a classname, it gives back the classes that implements the decoding. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * @param classname classname + * + * @return list of decoders + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public CoderFilters getDecoderFilters(String groupId, String artifactId, String topic, String classname); + + /** + * Given a controller id and a topic, it gives back the classes that implements the encoding. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * + * @return list of decoders + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic); + + /** + * gets all encoders associated with the group and artifact ids. + * + * @param groupId of the controller + * @param artifactId of the controller + * + * @throws IllegalArgumentException if invalid arguments have been provided + */ + public List<CoderFilters> getEncoderFilters(String groupId, String artifactId); + + /** + * get encoder based on coordinates and classname. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic protocol + * @param classname name of the class + * @return + * + * @throws IllegalArgumentException invalid arguments passed in + */ + public CoderFilters getEncoderFilters(String groupId, String artifactId, String topic, String classname); + + /** + * is there a decoder supported for the controller id and topic. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic protocol + * @return true if supported + */ + public boolean isDecodingSupported(String groupId, String artifactId, String topic); + + /** + * Adds a Encoder class to encode the protocol over this topic. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * @param eventClass the event class + * @param protocolFilter filters to selectively choose a particular decoder + * when there are multiples + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public void addEncoder(String groupId, String artifactId, String topic, + String eventClass, + JsonProtocolFilter protocolFilter, + CustomGsonCoder customGsonCoder, + CustomJacksonCoder customJacksonCoder, + int modelClassLoaderHash); + + /** + * is there an encoder supported for the controller id and topic. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic protocol + * @return true if supported + */ + public boolean isEncodingSupported(String groupId, String artifactId, String topic); + + /** + * get encoder based on topic and encoded class. + * + * @param topic topic + * @param encodedClass encoded class + * @return list of filters + * @throws IllegalArgumentException invalid arguments passed in + */ + public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass); + + /** + * gets the identifier of the creator of the encoder. + * + * @param topic topic + * @param encodedClass encoded class + * @return a drools controller + * @throws IllegalArgumentException invalid arguments passed in + */ + public DroolsController getDroolsController(String topic, Object encodedClass); + + /** + * gets the identifier of the creator of the encoder. + * + * @param topic topic + * @param encodedClass encoded class + * @return list of drools controllers + * @throws IllegalArgumentException invalid arguments passed in + */ + public List<DroolsController> getDroolsControllers(String topic, Object encodedClass); + + /** + * decode topic's stringified event (json) to corresponding Event Object. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic protocol + * @param json event string + * @return object + * @throws IllegalArgumentException invalid arguments passed in + * @throws UnsupportedOperationException if the operation is not supported + * @throws IllegalStateException if the system is in an illegal state + */ + public Object decode(String groupId, String artifactId, String topic, String json); + + /** + * encodes topic's stringified event (json) to corresponding Event Object. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic protocol + * @param event Object + * + * @throws IllegalArgumentException invalid arguments passed in + */ + public String encode(String groupId, String artifactId, String topic, Object event); + + /** + * encodes topic's stringified event (json) to corresponding Event Object. + * + * @param topic topic + * @param event event object + * + * @throws IllegalArgumentException invalid arguments passed in + * @throws UnsupportedOperationException operation cannot be performed + */ + public String encode(String topic, Object event); + + /** + * encodes topic's stringified event (json) to corresponding Event Object. + * + * @param topic topic + * @param event event object + * @param droolsController + * + * @throws IllegalArgumentException invalid arguments passed in + * @throws UnsupportedOperationException operation cannot be performed + */ + public String encode(String topic, Object event, DroolsController droolsController); } /** @@ -383,247 +394,251 @@ public interface EventProtocolCoder { * class and best fitted json parsing tools. */ class MultiplexorEventProtocolCoder implements EventProtocolCoder { - /** - * Logger - */ - private static Logger logger = LoggerFactory.getLogger(MultiplexorEventProtocolCoder.class); - - /** - * Decoders - */ - protected EventProtocolDecoder decoders = new EventProtocolDecoder(); - - /** - * Encoders - */ - protected EventProtocolEncoder encoders = new EventProtocolEncoder(); - - - /** - * {@inheritDoc} - */ - @Override - public void addDecoder(String groupId, String artifactId, String topic, - String eventClass, - JsonProtocolFilter protocolFilter, - CustomGsonCoder customGsonCoder, - CustomJacksonCoder customJacksonCoder, - int modelClassLoaderHash) { - logger.info("{}: add-decoder {}:{}:{}:{}:{}:{}:{}:{}", this, - groupId, artifactId, topic, eventClass, - protocolFilter, customGsonCoder, customJacksonCoder, - modelClassLoaderHash); - this.decoders.add(groupId, artifactId, topic, eventClass, protocolFilter, - customGsonCoder, customJacksonCoder, modelClassLoaderHash); - } - - /** - * {@inheritDoc} - */ - @Override - public void addEncoder(String groupId, String artifactId, String topic, - String eventClass, - JsonProtocolFilter protocolFilter, - CustomGsonCoder customGsonCoder, - CustomJacksonCoder customJacksonCoder, - int modelClassLoaderHash) { - logger.info("{}: add-decoder {}:{}:{}:{}:{}:{}:{}:{}", this, - groupId, artifactId, topic, eventClass, - protocolFilter, customGsonCoder, customJacksonCoder, - modelClassLoaderHash); - this.encoders.add(groupId, artifactId, topic, eventClass, protocolFilter, - customGsonCoder, customJacksonCoder, modelClassLoaderHash); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeDecoders(String groupId, String artifactId, String topic) { - logger.info("{}: remove-decoder {}:{}:{}", this, groupId, artifactId, topic); - this.decoders.remove(groupId, artifactId, topic); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeEncoders(String groupId, String artifactId, String topic) { - logger.info("{}: remove-encoder {}:{}:{}", this, groupId, artifactId, topic); - this.encoders.remove(groupId, artifactId, topic); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isDecodingSupported(String groupId, String artifactId, String topic) { - return this.decoders.isCodingSupported(groupId, artifactId, topic); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEncodingSupported(String groupId, String artifactId, String topic) { - return this.encoders.isCodingSupported(groupId, artifactId, topic); - } - - /** - * {@inheritDoc} - */ - @Override - public Object decode(String groupId, String artifactId, String topic, String json) { - logger.debug("{}: decode {}:{}:{}:{}", this, groupId, artifactId, topic, json); - return this.decoders.decode(groupId, artifactId, topic, json); - } - - /** - * {@inheritDoc} - */ - @Override - public String encode(String groupId, String artifactId, String topic, Object event) { - logger.debug("{}: encode {}:{}:{}:{}", this, groupId, artifactId, topic, event); - return this.encoders.encode(groupId, artifactId, topic, event); - } - - /** - * {@inheritDoc} - */ - @Override - public String encode(String topic, Object event) { - logger.debug("{}: encode {}:{}", this, topic, event); - return this.encoders.encode(topic, event); - } - - /** - * {@inheritDoc} - */ - @Override - public String encode(String topic, Object event, DroolsController droolsController) { - logger.debug("{}: encode {}:{}:{}", this, topic, event, droolsController); - return this.encoders.encode(topic, event, droolsController); - } - - /** - * {@inheritDoc} - */ - @Override - public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic) { - return this.decoders.getFilters(groupId, artifactId, topic); - } - - /** - * {@inheritDoc} - */ - @Override - public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic) { - Pair<ProtocolCoderToolset,ProtocolCoderToolset> decoderToolsets = this.decoders.getCoders(groupId, artifactId, topic); - if (decoderToolsets == null) - throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId + ":" + topic); - - return decoderToolsets.first(); - } - - /** - * {@inheritDoc} - */ - @Override - public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic) { - return this.encoders.getFilters(groupId, artifactId, topic); - } - - /** - * {@inheritDoc} - */ - @Override - public CoderFilters getDecoderFilters(String groupId, String artifactId, String topic, String classname) { - return this.decoders.getFilters(groupId, artifactId, topic, classname); - } - - /** - * {@inheritDoc} - */ - @Override - public CoderFilters getEncoderFilters(String groupId, String artifactId, String topic, String classname) { - return this.encoders.getFilters(groupId, artifactId, topic, classname); - } - - /** - * {@inheritDoc} - */ - @Override - public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass) { - return this.encoders.getReverseFilters(topic, encodedClass); - } - - /** - * get all deocders by maven coordinates and topic - * - * @param groupId group id - * @param artifactId artifact id - * - * @return list of decoders - * @throws IllegalArgumentException if invalid input - */ - @Override - public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId) { - - List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> decoderToolsets = this.decoders.getCoders(groupId, artifactId); - if (decoderToolsets == null) - throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId); - - List<ProtocolCoderToolset> parser1CoderToolset = new ArrayList<>(); - for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderToolsetPair : decoderToolsets) { - parser1CoderToolset.add(coderToolsetPair.first()); - } - - return parser1CoderToolset; - } - - /** - * {@inheritDoc} - */ - @Override - public List<CoderFilters> getDecoderFilters(String groupId, String artifactId) { - return this.decoders.getFilters(groupId, artifactId); - - } - - /** - * {@inheritDoc} - */ - @Override - public List<CoderFilters> getEncoderFilters(String groupId, String artifactId) { - return this.encoders.getFilters(groupId, artifactId); - } - - /** - * {@inheritDoc} - */ - @Override - public DroolsController getDroolsController(String topic, Object encodedClass) { - return this.encoders.getDroolsController(topic, encodedClass); - } - - /** - * {@inheritDoc} - */ - @Override - public List<DroolsController> getDroolsControllers(String topic, Object encodedClass) { - return this.encoders.getDroolsControllers(topic, encodedClass); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("MultiplexorEventProtocolCoder [decoders=").append(decoders).append(", encoders=") - .append(encoders).append("]"); - return builder.toString(); - } + /** + * Logger. + */ + private static Logger logger = LoggerFactory.getLogger(MultiplexorEventProtocolCoder.class); + + /** + * Decoders. + */ + protected EventProtocolDecoder decoders = new EventProtocolDecoder(); + + /** + * Encoders. + */ + protected EventProtocolEncoder encoders = new EventProtocolEncoder(); + + + /** + * {@inheritDoc} + */ + @Override + public void addDecoder(String groupId, String artifactId, String topic, + String eventClass, + JsonProtocolFilter protocolFilter, + CustomGsonCoder customGsonCoder, + CustomJacksonCoder customJacksonCoder, + int modelClassLoaderHash) { + logger.info("{}: add-decoder {}:{}:{}:{}:{}:{}:{}:{}", this, + groupId, artifactId, topic, eventClass, + protocolFilter, customGsonCoder, customJacksonCoder, + modelClassLoaderHash); + this.decoders.add(groupId, artifactId, topic, eventClass, protocolFilter, + customGsonCoder, customJacksonCoder, modelClassLoaderHash); + } + + /** + * {@inheritDoc} + */ + @Override + public void addEncoder(String groupId, String artifactId, String topic, + String eventClass, + JsonProtocolFilter protocolFilter, + CustomGsonCoder customGsonCoder, + CustomJacksonCoder customJacksonCoder, + int modelClassLoaderHash) { + logger.info("{}: add-decoder {}:{}:{}:{}:{}:{}:{}:{}", this, + groupId, artifactId, topic, eventClass, + protocolFilter, customGsonCoder, customJacksonCoder, + modelClassLoaderHash); + this.encoders.add(groupId, artifactId, topic, eventClass, protocolFilter, + customGsonCoder, customJacksonCoder, modelClassLoaderHash); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeDecoders(String groupId, String artifactId, String topic) { + logger.info("{}: remove-decoder {}:{}:{}", this, groupId, artifactId, topic); + this.decoders.remove(groupId, artifactId, topic); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeEncoders(String groupId, String artifactId, String topic) { + logger.info("{}: remove-encoder {}:{}:{}", this, groupId, artifactId, topic); + this.encoders.remove(groupId, artifactId, topic); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDecodingSupported(String groupId, String artifactId, String topic) { + return this.decoders.isCodingSupported(groupId, artifactId, topic); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEncodingSupported(String groupId, String artifactId, String topic) { + return this.encoders.isCodingSupported(groupId, artifactId, topic); + } + + /** + * {@inheritDoc} + */ + @Override + public Object decode(String groupId, String artifactId, String topic, String json) { + logger.debug("{}: decode {}:{}:{}:{}", this, groupId, artifactId, topic, json); + return this.decoders.decode(groupId, artifactId, topic, json); + } + + /** + * {@inheritDoc} + */ + @Override + public String encode(String groupId, String artifactId, String topic, Object event) { + logger.debug("{}: encode {}:{}:{}:{}", this, groupId, artifactId, topic, event); + return this.encoders.encode(groupId, artifactId, topic, event); + } + + /** + * {@inheritDoc} + */ + @Override + public String encode(String topic, Object event) { + logger.debug("{}: encode {}:{}", this, topic, event); + return this.encoders.encode(topic, event); + } + + /** + * {@inheritDoc} + */ + @Override + public String encode(String topic, Object event, DroolsController droolsController) { + logger.debug("{}: encode {}:{}:{}", this, topic, event, droolsController); + return this.encoders.encode(topic, event, droolsController); + } + + /** + * {@inheritDoc} + */ + @Override + public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic) { + return this.decoders.getFilters(groupId, artifactId, topic); + } + + /** + * {@inheritDoc} + */ + @Override + public CoderFilters getDecoderFilters(String groupId, String artifactId, String topic, String classname) { + return this.decoders.getFilters(groupId, artifactId, topic, classname); + } + + /** + * {@inheritDoc} + */ + @Override + public List<CoderFilters> getDecoderFilters(String groupId, String artifactId) { + return this.decoders.getFilters(groupId, artifactId); + + } + + /** + * {@inheritDoc} + */ + @Override + public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic) { + Pair<ProtocolCoderToolset,ProtocolCoderToolset> decoderToolsets = + this.decoders.getCoders(groupId, artifactId, topic); + if (decoderToolsets == null) { + throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId + ":" + topic); + } + + return decoderToolsets.first(); + } + + /** + * get all deocders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * + * @return list of decoders + * @throws IllegalArgumentException if invalid input + */ + @Override + public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId) { + + List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> decoderToolsets = + this.decoders.getCoders(groupId, artifactId); + if (decoderToolsets == null) { + throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId); + } + + List<ProtocolCoderToolset> parser1CoderToolset = new ArrayList<>(); + for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderToolsetPair : decoderToolsets) { + parser1CoderToolset.add(coderToolsetPair.first()); + } + + return parser1CoderToolset; + } + + /** + * {@inheritDoc} + */ + @Override + public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic) { + return this.encoders.getFilters(groupId, artifactId, topic); + } + + /** + * {@inheritDoc} + */ + @Override + public CoderFilters getEncoderFilters(String groupId, String artifactId, String topic, String classname) { + return this.encoders.getFilters(groupId, artifactId, topic, classname); + } + + /** + * {@inheritDoc} + */ + @Override + public List<CoderFilters> getEncoderFilters(String groupId, String artifactId) { + return this.encoders.getFilters(groupId, artifactId); + } + + /** + * {@inheritDoc} + */ + @Override + public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass) { + return this.encoders.getReverseFilters(topic, encodedClass); + } + + /** + * {@inheritDoc} + */ + @Override + public DroolsController getDroolsController(String topic, Object encodedClass) { + return this.encoders.getDroolsController(topic, encodedClass); + } + + /** + * {@inheritDoc} + */ + @Override + public List<DroolsController> getDroolsControllers(String topic, Object encodedClass) { + return this.encoders.getDroolsControllers(topic, encodedClass); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("MultiplexorEventProtocolCoder [decoders=").append(decoders).append(", encoders=") + .append(encoders).append("]"); + return builder.toString(); + } } /** @@ -631,723 +646,758 @@ class MultiplexorEventProtocolCoder implements EventProtocolCoder { * class and best fitted json parsing tools. */ abstract class GenericEventProtocolCoder { - private static final String INVALID_ARTIFACT_ID_MSG = "Invalid artifact id"; - - private static final String INVALID_GROUP_ID_MSG = "Invalid group id"; - - private static final String INVALID_TOPIC_MSG = "Invalid Topic"; - - private static final String UNSUPPORTED_MSG = "Unsupported"; - - private static final String MISSING_CLASS = "class must be provided"; - - private static Logger logger = LoggerFactory.getLogger(GenericEventProtocolCoder.class); - - /** - * Mapping topic:controller-id -> <protocol-decoder-toolset-pair> - * where protocol-coder-toolset-pair contains both a jackson-protocol-coder-toolset - * and a gson-protocol-coder-toolset. The first value of the pair will the - * protocol coder toolset most likely to be successful with the encoding or decoding, - * and consequently the second value will be the less likely. - */ - protected final HashMap<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> coders = - new HashMap<>(); - - /** - * Mapping topic + classname -> Protocol Set - */ - protected final HashMap<String, List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>> reverseCoders = - new HashMap<>(); - - protected boolean multipleToolsetRetries = false; - - GenericEventProtocolCoder(boolean multipleToolsetRetries) { - this.multipleToolsetRetries = multipleToolsetRetries; - } - - /** - * Index a new coder - * - * @param groupId of the controller - * @param artifactId of the controller - * @param topic the topic - * @param eventClass the event class - * @param protocolFilter filters to selectively choose a particular decoder - * when there are multiples - * - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public void add(String groupId, String artifactId, - String topic, - String eventClass, - JsonProtocolFilter protocolFilter, - CustomGsonCoder customGsonCoder, - CustomJacksonCoder customJacksonCoder, - int modelClassLoaderHash) { - if (groupId == null || groupId.isEmpty()) { - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - } - - if (artifactId == null || artifactId.isEmpty()) - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - - if (eventClass == null) { - throw new IllegalArgumentException("Invalid Event Class"); - } - - String key = this.codersKey(groupId, artifactId, topic); - String reverseKey = this.reverseCodersKey(topic, eventClass); - - synchronized(this) { - if (coders.containsKey(key)) { - Pair<ProtocolCoderToolset, ProtocolCoderToolset> toolsets = coders.get(key); - - logger.info("{}: adding coders for existing {}: ", this, key, toolsets.first()); - - toolsets.first().addCoder(eventClass, protocolFilter, modelClassLoaderHash); - toolsets.second().addCoder(eventClass, protocolFilter, modelClassLoaderHash); - - if (!reverseCoders.containsKey(reverseKey)) { - logger.info("{}: adding new reverse coders (multiple classes case) for {}:{}: {}", - this, reverseKey, key, toolsets.first()); - - List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> reverseMappings = - new ArrayList<>(); - reverseMappings.add(toolsets); - reverseCoders.put(reverseKey, reverseMappings); - } - return; - } - - GsonProtocolCoderToolset gsonCoderTools = - new GsonProtocolCoderToolset - (topic, key, - groupId, artifactId, - eventClass, protocolFilter, - customGsonCoder, - modelClassLoaderHash); - - JacksonProtocolCoderToolset jacksonCoderTools = - new JacksonProtocolCoderToolset - (topic, key, - groupId, artifactId, - eventClass, protocolFilter, - customJacksonCoder, - modelClassLoaderHash); - - // Use Gson as the first priority encoding/decoding toolset, and Jackson - // as second. This is because it has been observed that they can diverge - // somewhat in the encoding/decoding data types, which can produce json - // that may result incompatible with what some network elements are - // expecting. As decoding takes place, this element will reconfigure - // itself to set the jackson one as the favoured one first, if errors - // are detected in the gson encoding - - Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = - new Pair<>(gsonCoderTools, - jacksonCoderTools); - - logger.info("{}: adding coders for new {}: {}", this, key, coderTools.first()); - - coders.put(key, coderTools); - - if (reverseCoders.containsKey(reverseKey)) { - // There is another controller (different group id/artifact id/topic) - // that shares the class and the topic. - - List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = - reverseCoders.get(reverseKey); - boolean present = false; - for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> parserSet: toolsets) { - // just doublecheck - present = parserSet.first().getControllerId().equals(key); - if (present) { - /* anomaly */ - logger.error("{}: unexpected toolset reverse mapping found for {}:{}: {}", - this, reverseKey, key, parserSet.first()); - } - } - - if (present) { - return; - } else { - logger.info("{}: adding coder set for {}: {} ", this, - reverseKey, coderTools.getFirst()); - toolsets.add(coderTools); - } - } else { - List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = - new ArrayList<>(); - toolsets.add(coderTools); - - logger.info("{}: adding toolset for reverse key {}: {}", this, reverseKey, toolsets); - reverseCoders.put(reverseKey, toolsets); - } - - } - } - - /** - * produces key for indexing toolset entries - * - * @param group group id - * @param artifactId artifact id - * @param topic topic - * @return index key - */ - protected String codersKey(String groupId, String artifactId, String topic) { - return groupId + ":" + artifactId + ":" + topic; - } - - /** - * produces a key for the reverse index - * - * @param topic topic - * @param eventClass coded class - * @return reverse index key - */ - protected String reverseCodersKey(String topic, String eventClass) { - return topic + ":" + eventClass; - } - - /** - * remove coder - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @throws IllegalArgumentException if invalid input - */ - public void remove(String groupId, String artifactId, String topic) { - - if (groupId == null || groupId.isEmpty()) - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - - if (artifactId == null || artifactId.isEmpty()) - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - - String key = this.codersKey(groupId, artifactId, topic); - - synchronized(this) { - if (coders.containsKey(key)) { - Pair<ProtocolCoderToolset, ProtocolCoderToolset> p = coders.remove(key); - - logger.info("{}: removed toolset for {}: {}", this, key, p.getFirst()); - - for (CoderFilters codeFilter : p.first().getCoders()) { - String className = codeFilter.getCodedClass(); - String reverseKey = this.reverseCodersKey(topic, className); - if (this.reverseCoders.containsKey(reverseKey) ) { - List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsets = - this.reverseCoders.get(reverseKey); - Iterator<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsetsIter = - toolsets.iterator(); - while (toolsetsIter.hasNext()) { - Pair<ProtocolCoderToolset, ProtocolCoderToolset> toolset = toolsetsIter.next(); - if (toolset.first().getControllerId().equals(key)) { - logger.info("{}: removed coder from toolset for {} from reverse mapping {}: ", - this, reverseKey); - toolsetsIter.remove(); - } - } - - if (this.reverseCoders.get(reverseKey).isEmpty()) { - logger.info("{}: removing reverse mapping for {}: ", this, reverseKey); - this.reverseCoders.remove(reverseKey); - } - } - } - } - } - } - - /** - * does it support coding? - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return true if its is codable - */ - public boolean isCodingSupported(String groupId, String artifactId, String topic) { - - if (groupId == null || groupId.isEmpty()) - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - - if (artifactId == null || artifactId.isEmpty()) - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - - String key = this.codersKey(groupId, artifactId, topic); - synchronized(this) { - return coders.containsKey(key); - } - } - - /** - * decode a json string into an Object - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @param json json string to convert to object - * @return the decoded object - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public Object decode(String groupId, String artifactId, String topic, String json) { - - if (!isCodingSupported(groupId, artifactId, topic)) - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic) + " for encoding"); - - String key = this.codersKey(groupId, artifactId, topic); - Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); - try { - Object event = coderTools.first().decode(json); - if (event != null) - return event; - } catch (Exception e) { - logger.debug("{}, cannot decode {}", this, json, e); - } - - if (multipleToolsetRetries) { - // try the less favored toolset - try { - Object event = coderTools.second().decode(json); - if (event != null) { - // change the priority of the toolset - synchronized(this) { - ProtocolCoderToolset first = coderTools.first(); - ProtocolCoderToolset second = coderTools.second(); - coderTools.first(second); - coderTools.second(first); - } - - return event; - } - } catch (Exception e) { - throw new UnsupportedOperationException(e); - } - } - - throw new UnsupportedOperationException("Cannot decode neither with gson or jackson"); - } - - /** - * encode an object into a json string - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @param event object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public String encode(String groupId, String artifactId, String topic, Object event) { - - if (!isCodingSupported(groupId, artifactId, topic)) - throw new IllegalArgumentException - ("Unsupported:" + codersKey(groupId, artifactId, topic)); - - if (event == null) - throw new IllegalArgumentException("Unsupported topic:" + topic); - - // reuse the decoder set, since there must be affinity in the model - String key = this.codersKey(groupId, artifactId, topic); - return this.encodeInternal(key, event); - } - - /** - * encode an object into a json string - * - * @param key identifier - * @param event object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - protected String encodeInternal(String key, Object event) { - - logger.debug("{}: encode for {}: {}", this, key, event); - - Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); - try { - String json = coderTools.first().encode(event); - if (json != null && !json.isEmpty()) - return json; - } catch (Exception e) { - logger.warn("{}: cannot encode (first) for {}: {}", this, key, event, e); - } - - if (multipleToolsetRetries) { - // try the less favored toolset - try { - String json = coderTools.second().encode(event); - if (json != null) { - // change the priority of the toolset - synchronized(this) { - ProtocolCoderToolset first = coderTools.first(); - ProtocolCoderToolset second = coderTools.second(); - coderTools.first(second); - coderTools.second(first); - } - - return json; - } - } catch (Exception e) { - logger.error("{}: cannot encode (second) for {}: {}", this, key, event, e); - throw new UnsupportedOperationException(e); - } - } - - throw new UnsupportedOperationException("Cannot decode neither with gson or jackson"); - } - - /** - * encode an object into a json string - * - * @param topic topic - * @param event object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public String encode(String topic, Object event) { - - if (event == null) - throw new IllegalArgumentException("Invalid encoded class"); - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException("Invalid topic"); - + private static final String INVALID_ARTIFACT_ID_MSG = "Invalid artifact id"; + + private static final String INVALID_GROUP_ID_MSG = "Invalid group id"; + + private static final String INVALID_TOPIC_MSG = "Invalid Topic"; + + private static final String UNSUPPORTED_MSG = "Unsupported"; + + private static final String MISSING_CLASS = "class must be provided"; + + private static Logger logger = LoggerFactory.getLogger(GenericEventProtocolCoder.class); + + /** + * Mapping topic:controller-id -> /<protocol-decoder-toolset-pair/> + * where protocol-coder-toolset-pair contains both a jackson-protocol-coder-toolset + * and a gson-protocol-coder-toolset. The first value of the pair will the + * protocol coder toolset most likely to be successful with the encoding or decoding, + * and consequently the second value will be the less likely. + */ + protected final HashMap<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> coders = + new HashMap<>(); + + /** + * Mapping topic + classname -> Protocol Set. + */ + protected final HashMap<String, List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>> reverseCoders = + new HashMap<>(); + + protected boolean multipleToolsetRetries = false; + + GenericEventProtocolCoder(boolean multipleToolsetRetries) { + this.multipleToolsetRetries = multipleToolsetRetries; + } + + /** + * Index a new coder. + * + * @param groupId of the controller + * @param artifactId of the controller + * @param topic the topic + * @param eventClass the event class + * @param protocolFilter filters to selectively choose a particular decoder + * when there are multiples + * + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public void add(String groupId, String artifactId, + String topic, + String eventClass, + JsonProtocolFilter protocolFilter, + CustomGsonCoder customGsonCoder, + CustomJacksonCoder customJacksonCoder, + int modelClassLoaderHash) { + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + if (eventClass == null) { + throw new IllegalArgumentException("Invalid Event Class"); + } + + String key = this.codersKey(groupId, artifactId, topic); + String reverseKey = this.reverseCodersKey(topic, eventClass); + + synchronized (this) { + if (coders.containsKey(key)) { + Pair<ProtocolCoderToolset, ProtocolCoderToolset> toolsets = coders.get(key); + + logger.info("{}: adding coders for existing {}: ", this, key, toolsets.first()); + + toolsets.first().addCoder(eventClass, protocolFilter, modelClassLoaderHash); + toolsets.second().addCoder(eventClass, protocolFilter, modelClassLoaderHash); + + if (!reverseCoders.containsKey(reverseKey)) { + logger.info("{}: adding new reverse coders (multiple classes case) for {}:{}: {}", + this, reverseKey, key, toolsets.first()); + + List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> reverseMappings = + new ArrayList<>(); + reverseMappings.add(toolsets); + reverseCoders.put(reverseKey, reverseMappings); + } + return; + } + + GsonProtocolCoderToolset gsonCoderTools = + new GsonProtocolCoderToolset(topic, key, + groupId, artifactId, + eventClass, protocolFilter, + customGsonCoder, + modelClassLoaderHash); + + JacksonProtocolCoderToolset jacksonCoderTools = + new JacksonProtocolCoderToolset(topic, key, + groupId, artifactId, + eventClass, protocolFilter, + customJacksonCoder, + modelClassLoaderHash); + + // Use Gson as the first priority encoding/decoding toolset, and Jackson + // as second. This is because it has been observed that they can diverge + // somewhat in the encoding/decoding data types, which can produce json + // that may result incompatible with what some network elements are + // expecting. As decoding takes place, this element will reconfigure + // itself to set the jackson one as the favoured one first, if errors + // are detected in the gson encoding + + Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = + new Pair<>(gsonCoderTools, + jacksonCoderTools); + + logger.info("{}: adding coders for new {}: {}", this, key, coderTools.first()); + + coders.put(key, coderTools); + + if (reverseCoders.containsKey(reverseKey)) { + // There is another controller (different group id/artifact id/topic) + // that shares the class and the topic. + + List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = + reverseCoders.get(reverseKey); + boolean present = false; + for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> parserSet: toolsets) { + // just doublecheck + present = parserSet.first().getControllerId().equals(key); + if (present) { + /* anomaly */ + logger.error("{}: unexpected toolset reverse mapping found for {}:{}: {}", + this, reverseKey, key, parserSet.first()); + } + } + + if (present) { + return; + } else { + logger.info("{}: adding coder set for {}: {} ", this, + reverseKey, coderTools.getFirst()); + toolsets.add(coderTools); + } + } else { + List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = + new ArrayList<>(); + toolsets.add(coderTools); + + logger.info("{}: adding toolset for reverse key {}: {}", this, reverseKey, toolsets); + reverseCoders.put(reverseKey, toolsets); + } + + } + } + + /** + * produces key for indexing toolset entries. + * + * @param group group id + * @param artifactId artifact id + * @param topic topic + * @return index key + */ + protected String codersKey(String groupId, String artifactId, String topic) { + return groupId + ":" + artifactId + ":" + topic; + } + + /** + * produces a key for the reverse index. + * + * @param topic topic + * @param eventClass coded class + * @return reverse index key + */ + protected String reverseCodersKey(String topic, String eventClass) { + return topic + ":" + eventClass; + } + + /** + * remove coder. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @throws IllegalArgumentException if invalid input + */ + public void remove(String groupId, String artifactId, String topic) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + String key = this.codersKey(groupId, artifactId, topic); + + synchronized (this) { + if (coders.containsKey(key)) { + Pair<ProtocolCoderToolset, ProtocolCoderToolset> pair = coders.remove(key); + + logger.info("{}: removed toolset for {}: {}", this, key, pair.getFirst()); + + for (CoderFilters codeFilter : pair.first().getCoders()) { + String className = codeFilter.getCodedClass(); + String reverseKey = this.reverseCodersKey(topic, className); + if (this.reverseCoders.containsKey(reverseKey) ) { + List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsets = + this.reverseCoders.get(reverseKey); + Iterator<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsetsIter = + toolsets.iterator(); + while (toolsetsIter.hasNext()) { + Pair<ProtocolCoderToolset, ProtocolCoderToolset> toolset = toolsetsIter.next(); + if (toolset.first().getControllerId().equals(key)) { + logger.info("{}: removed coder from toolset for {} from reverse mapping {}: ", + this, reverseKey); + toolsetsIter.remove(); + } + } + + if (this.reverseCoders.get(reverseKey).isEmpty()) { + logger.info("{}: removing reverse mapping for {}: ", this, reverseKey); + this.reverseCoders.remove(reverseKey); + } + } + } + } + } + } + + /** + * does it support coding. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return true if its is codable + */ + public boolean isCodingSupported(String groupId, String artifactId, String topic) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + String key = this.codersKey(groupId, artifactId, topic); + synchronized (this) { + return coders.containsKey(key); + } + } + + /** + * decode a json string into an Object. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @param json json string to convert to object + * @return the decoded object + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public Object decode(String groupId, String artifactId, String topic, String json) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic) + + " for encoding"); + } + + String key = this.codersKey(groupId, artifactId, topic); + Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); + try { + Object event = coderTools.first().decode(json); + if (event != null) { + return event; + } + } catch (Exception e) { + logger.debug("{}, cannot decode {}", this, json, e); + } + + if (multipleToolsetRetries) { + // try the less favored toolset + try { + Object event = coderTools.second().decode(json); + if (event != null) { + // change the priority of the toolset + synchronized (this) { + ProtocolCoderToolset first = coderTools.first(); + ProtocolCoderToolset second = coderTools.second(); + coderTools.first(second); + coderTools.second(first); + } + + return event; + } + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + } + + throw new UnsupportedOperationException("Cannot decode neither with gson or jackson"); + } + + /** + * encode an object into a json string. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @param event object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public String encode(String groupId, String artifactId, String topic, Object event) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + if (event == null) { + throw new IllegalArgumentException("Unsupported topic:" + topic); + } + + // reuse the decoder set, since there must be affinity in the model + String key = this.codersKey(groupId, artifactId, topic); + return this.encodeInternal(key, event); + } + + /** + * encode an object into a json string. + * + * @param topic topic + * @param event object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public String encode(String topic, Object event) { + + if (event == null) { + throw new IllegalArgumentException("Invalid encoded class"); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException("Invalid topic"); + } + String reverseKey = this.reverseCodersKey(topic, event.getClass().getCanonicalName()); - if (!this.reverseCoders.containsKey(reverseKey)) + if (!this.reverseCoders.containsKey(reverseKey)) { throw new IllegalArgumentException("no reverse coder has been found"); - - List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> - toolsets = this.reverseCoders.get(reverseKey); - + } + + List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsets = this.reverseCoders.get(reverseKey); + String key = codersKey(toolsets.get(0).first().getGroupId(), toolsets.get(0).first().getArtifactId(), topic); return this.encodeInternal(key, event); - } - - /** - * encode an object into a json string - * - * @param topic topic - * @param encodedClass object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public String encode(String topic, Object encodedClass, DroolsController droolsController) { - - if (encodedClass == null) - throw new IllegalArgumentException("Invalid encoded class"); - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException("Invalid topic"); - - String key = codersKey(droolsController.getGroupId(), droolsController.getArtifactId(), topic); - return this.encodeInternal(key, encodedClass); - } - - /** - * @param topic - * @param encodedClass - * @param reverseKey - * @return - * @throws IllegalStateException - * @throws IllegalArgumentException - */ - protected List<DroolsController> droolsCreators(String topic, Object encodedClass) { - - List<DroolsController> droolsControllers = new ArrayList<>(); - - String reverseKey = this.reverseCodersKey(topic, encodedClass.getClass().getCanonicalName()); - if (!this.reverseCoders.containsKey(reverseKey)) { - logger.warn("{}: no reverse mapping for {}", this, reverseKey); - return droolsControllers; - } - - List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> - toolsets = this.reverseCoders.get(reverseKey); - - // There must be multiple toolset pairs associated with <topic,classname> reverseKey - // case 2 different controllers use the same models and register the same encoder for - // the same topic. This is assumed not to occur often but for the purpose of encoding - // but there should be no side-effects. Ownership is crosscheck against classname and - // classloader reference. - - if (toolsets == null || toolsets.isEmpty()) - throw new IllegalStateException("No Encoders toolsets available for topic "+ topic + - " encoder " + encodedClass.getClass().getCanonicalName()); - - for (Pair<ProtocolCoderToolset, ProtocolCoderToolset> encoderSet : toolsets) { - // figure out the right toolset - String groupId = encoderSet.first().getGroupId(); - String artifactId = encoderSet.first().getArtifactId(); - List<CoderFilters> coderFilters = encoderSet.first().getCoders(); - for (CoderFilters coder : coderFilters) { - if (coder.getCodedClass().equals(encodedClass.getClass().getCanonicalName())) { - DroolsController droolsController = - DroolsController.factory.get(groupId, artifactId, ""); - if (droolsController.ownsCoder(encodedClass.getClass(), coder.getModelClassLoaderHash())) { - droolsControllers.add(droolsController); - } - } - } - } - - if (droolsControllers.isEmpty()) - throw new IllegalStateException("No Encoders toolsets available for "+ topic + - ":" + encodedClass.getClass().getCanonicalName()); - - return droolsControllers; - } - - - /** - * get all filters by maven coordinates and topic - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public List<CoderFilters> getFilters(String groupId, String artifactId, String topic) { - - if (!isCodingSupported(groupId, artifactId, topic)) - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - - String key = this.codersKey(groupId, artifactId, topic); - Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); - return coderTools.first().getCoders(); - } - - /** - * get all coders by maven coordinates and topic - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public Pair<ProtocolCoderToolset,ProtocolCoderToolset> getCoders(String groupId, String artifactId, String topic) { - - if (!isCodingSupported(groupId, artifactId, topic)) - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - - String key = this.codersKey(groupId, artifactId, topic); - return coders.get(key); - } - - /** - * get all coders by maven coordinates and topic - * - * @param groupId group id - * @param artifactId artifact id - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public List<CoderFilters> getFilters(String groupId, String artifactId) { - - if (groupId == null || groupId.isEmpty()) - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - - if (artifactId == null || artifactId.isEmpty()) - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - - String key = this.codersKey(groupId, artifactId, ""); - - List<CoderFilters> codersFilters = new ArrayList<>(); - for (Map.Entry<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> entry : coders.entrySet()) { - if (entry.getKey().startsWith(key)) { - codersFilters.addAll(entry.getValue().first().getCoders()); - } - } - - return codersFilters; - } - - /** - * get all coders by maven coordinates and topic - * - * @param groupId group id - * @param artifactId artifact id - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> getCoders(String groupId, String artifactId) { - - if (groupId == null || groupId.isEmpty()) - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - - if (artifactId == null || artifactId.isEmpty()) - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - - String key = this.codersKey(groupId, artifactId, ""); - - List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> coderToolset = new ArrayList<>(); - for (Map.Entry<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> entry : coders.entrySet()) { - if (entry.getKey().startsWith(key)) { - coderToolset.add(entry.getValue()); - } - } - - return coderToolset; - } - - - /** - * get all filters by maven coordinates, topic, and classname - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @param classname - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public CoderFilters getFilters(String groupId, String artifactId, String topic, String classname) { - - if (!isCodingSupported(groupId, artifactId, topic)) - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - - if (classname == null || classname.isEmpty()) - throw new IllegalArgumentException("classname must be provided"); - - String key = this.codersKey(groupId, artifactId, topic); - Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); - return coderTools.first().getCoder(classname); - } - - /** - * get coded based on class and topic - * - * @param topic - * @param codedClass - * @return - * @throws IllegalArgumentException - */ - public List<CoderFilters> getReverseFilters(String topic, String codedClass) { - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException(UNSUPPORTED_MSG); - - if (codedClass == null) - throw new IllegalArgumentException(MISSING_CLASS); - - String key = this.reverseCodersKey(topic, codedClass); - List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = this.reverseCoders.get(key); - if (toolsets == null) - throw new IllegalArgumentException("No Coder found for " + key); - - - List<CoderFilters> coderFilters = new ArrayList<>(); - for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> toolset: toolsets) { - coderFilters.addAll(toolset.first().getCoders()); - } - - return coderFilters; - } - - /** - * returns group and artifact id of the creator of the encoder - * - * @param topic - * @param fact - * @return - * @throws IllegalArgumentException - */ - DroolsController getDroolsController(String topic, Object fact) { - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException(UNSUPPORTED_MSG); - - if (fact == null) - throw new IllegalArgumentException(MISSING_CLASS); - - List<DroolsController> droolsControllers = droolsCreators(topic, fact); - - if (droolsControllers.isEmpty()) - throw new IllegalArgumentException("Invalid Topic: " + topic); - - if (droolsControllers.size() > 1) { - logger.warn("{}: multiple drools-controller {} for {}:{} ", this, - droolsControllers, topic, fact.getClass().getCanonicalName()); - // continue - } - return droolsControllers.get(0); - } - - /** - * returns group and artifact id of the creator of the encoder - * - * @param topic - * @param fact - * @return - * @throws IllegalArgumentException - */ - List<DroolsController> getDroolsControllers(String topic, Object fact) { - - if (topic == null || topic.isEmpty()) - throw new IllegalArgumentException(UNSUPPORTED_MSG); - - if (fact == null) - throw new IllegalArgumentException(MISSING_CLASS); - - List<DroolsController> droolsControllers = droolsCreators(topic, fact); - if (droolsControllers.size() > 1) { - // unexpected - logger.warn("{}: multiple drools-controller {} for {}:{} ", this, - droolsControllers, topic, fact.getClass().getCanonicalName()); - // continue - } - return droolsControllers; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("GenericEventProtocolCoder [coders=").append(coders.keySet()).append(", reverseCoders=") - .append(reverseCoders.keySet()).append("]"); - return builder.toString(); - } + } + + /** + * encode an object into a json string. + * + * @param key identifier + * @param event object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + protected String encodeInternal(String key, Object event) { + + logger.debug("{}: encode for {}: {}", this, key, event); + + Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); + try { + String json = coderTools.first().encode(event); + if (json != null && !json.isEmpty()) { + return json; + } + } catch (Exception e) { + logger.warn("{}: cannot encode (first) for {}: {}", this, key, event, e); + } + + if (multipleToolsetRetries) { + // try the less favored toolset + try { + String json = coderTools.second().encode(event); + if (json != null) { + // change the priority of the toolset + synchronized (this) { + ProtocolCoderToolset first = coderTools.first(); + ProtocolCoderToolset second = coderTools.second(); + coderTools.first(second); + coderTools.second(first); + } + + return json; + } + } catch (Exception e) { + logger.error("{}: cannot encode (second) for {}: {}", this, key, event, e); + throw new UnsupportedOperationException(e); + } + } + + throw new UnsupportedOperationException("Cannot decode neither with gson or jackson"); + } + + /** + * encode an object into a json string. + * + * @param topic topic + * @param encodedClass object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public String encode(String topic, Object encodedClass, DroolsController droolsController) { + + if (encodedClass == null) { + throw new IllegalArgumentException("Invalid encoded class"); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException("Invalid topic"); + } + + String key = codersKey(droolsController.getGroupId(), droolsController.getArtifactId(), topic); + return this.encodeInternal(key, encodedClass); + } + + /** + * Drools creators. + * + * @param topic topic + * @param encodedClass encoded class + * @return list of controllers + * + * @throws IllegalStateException illegal state + * @throws IllegalArgumentException argument + */ + protected List<DroolsController> droolsCreators(String topic, Object encodedClass) { + + List<DroolsController> droolsControllers = new ArrayList<>(); + + String reverseKey = this.reverseCodersKey(topic, encodedClass.getClass().getCanonicalName()); + if (!this.reverseCoders.containsKey(reverseKey)) { + logger.warn("{}: no reverse mapping for {}", this, reverseKey); + return droolsControllers; + } + + List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsets = this.reverseCoders.get(reverseKey); + + // There must be multiple toolset pairs associated with <topic,classname> reverseKey + // case 2 different controllers use the same models and register the same encoder for + // the same topic. This is assumed not to occur often but for the purpose of encoding + // but there should be no side-effects. Ownership is crosscheck against classname and + // classloader reference. + + if (toolsets == null || toolsets.isEmpty()) { + throw new IllegalStateException("No Encoders toolsets available for topic " + + topic + + " encoder " + encodedClass.getClass().getCanonicalName()); + } + + for (Pair<ProtocolCoderToolset, ProtocolCoderToolset> encoderSet : toolsets) { + // figure out the right toolset + String groupId = encoderSet.first().getGroupId(); + String artifactId = encoderSet.first().getArtifactId(); + List<CoderFilters> coderFilters = encoderSet.first().getCoders(); + for (CoderFilters coder : coderFilters) { + if (coder.getCodedClass().equals(encodedClass.getClass().getCanonicalName())) { + DroolsController droolsController = + DroolsController.factory.get(groupId, artifactId, ""); + if (droolsController.ownsCoder(encodedClass.getClass(), coder.getModelClassLoaderHash())) { + droolsControllers.add(droolsController); + } + } + } + } + + if (droolsControllers.isEmpty()) { + throw new IllegalStateException("No Encoders toolsets available for " + + topic + + ":" + encodedClass.getClass().getCanonicalName()); + } + + return droolsControllers; + } + + + /** + * get all filters by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public List<CoderFilters> getFilters(String groupId, String artifactId, String topic) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + String key = this.codersKey(groupId, artifactId, topic); + Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); + return coderTools.first().getCoders(); + } + + /** + * get all coders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public List<CoderFilters> getFilters(String groupId, String artifactId) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + String key = this.codersKey(groupId, artifactId, ""); + + List<CoderFilters> codersFilters = new ArrayList<>(); + for (Map.Entry<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> entry : coders.entrySet()) { + if (entry.getKey().startsWith(key)) { + codersFilters.addAll(entry.getValue().first().getCoders()); + } + } + + return codersFilters; + } + + /** + * get all coders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public Pair<ProtocolCoderToolset,ProtocolCoderToolset> getCoders(String groupId, String artifactId, String topic) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + String key = this.codersKey(groupId, artifactId, topic); + return coders.get(key); + } + + /** + * get all coders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> getCoders(String groupId, String artifactId) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + String key = this.codersKey(groupId, artifactId, ""); + + List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> coderToolset = new ArrayList<>(); + for (Map.Entry<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> entry : coders.entrySet()) { + if (entry.getKey().startsWith(key)) { + coderToolset.add(entry.getValue()); + } + } + + return coderToolset; + } + + /** + * get all filters by maven coordinates, topic, and classname. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @param classname classname + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public CoderFilters getFilters(String groupId, String artifactId, String topic, String classname) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + if (classname == null || classname.isEmpty()) { + throw new IllegalArgumentException("classname must be provided"); + } + + String key = this.codersKey(groupId, artifactId, topic); + Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key); + return coderTools.first().getCoder(classname); + } + + /** + * get coded based on class and topic. + * + * @param topic topic + * @param codedClass class + * @return list of reverse filters + */ + public List<CoderFilters> getReverseFilters(String topic, String codedClass) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(UNSUPPORTED_MSG); + } + + if (codedClass == null) { + throw new IllegalArgumentException(MISSING_CLASS); + } + + String key = this.reverseCodersKey(topic, codedClass); + List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = this.reverseCoders.get(key); + if (toolsets == null) { + throw new IllegalArgumentException("No Coder found for " + key); + } + + List<CoderFilters> coderFilters = new ArrayList<>(); + for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> toolset: toolsets) { + coderFilters.addAll(toolset.first().getCoders()); + } + + return coderFilters; + } + + /** + * returns group and artifact id of the creator of the encoder. + * + * @param topic topic + * @param fact fact + * @return the drools controller + */ + DroolsController getDroolsController(String topic, Object fact) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(UNSUPPORTED_MSG); + } + + if (fact == null) { + throw new IllegalArgumentException(MISSING_CLASS); + } + + List<DroolsController> droolsControllers = droolsCreators(topic, fact); + + if (droolsControllers.isEmpty()) { + throw new IllegalArgumentException("Invalid Topic: " + topic); + } + + if (droolsControllers.size() > 1) { + logger.warn("{}: multiple drools-controller {} for {}:{} ", this, + droolsControllers, topic, fact.getClass().getCanonicalName()); + // continue + } + return droolsControllers.get(0); + } + + /** + * returns group and artifact id of the creator of the encoder. + * + * @param topic topic + * @param fact fact + * @return + */ + List<DroolsController> getDroolsControllers(String topic, Object fact) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(UNSUPPORTED_MSG); + } + + if (fact == null) { + throw new IllegalArgumentException(MISSING_CLASS); + } + + List<DroolsController> droolsControllers = droolsCreators(topic, fact); + if (droolsControllers.size() > 1) { + // unexpected + logger.warn("{}: multiple drools-controller {} for {}:{} ", this, + droolsControllers, topic, fact.getClass().getCanonicalName()); + // continue + } + return droolsControllers; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("GenericEventProtocolCoder [coders=").append(coders.keySet()).append(", reverseCoders=") + .append(reverseCoders.keySet()).append("]"); + return builder.toString(); + } } class EventProtocolDecoder extends GenericEventProtocolCoder { - public EventProtocolDecoder(){super(false);} - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("EventProtocolDecoder [toString()=").append(super.toString()).append("]"); - return builder.toString(); - } - + public EventProtocolDecoder() { + super(false); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("EventProtocolDecoder [toString()=").append(super.toString()).append("]"); + return builder.toString(); + } + } - + class EventProtocolEncoder extends GenericEventProtocolCoder { - - public EventProtocolEncoder(){super(false);} - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("EventProtocolEncoder [toString()=").append(super.toString()).append("]"); - return builder.toString(); - } + + public EventProtocolEncoder() { + super(false); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("EventProtocolEncoder [toString()=").append(super.toString()).append("]"); + return builder.toString(); + } } diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java index a5902d66..4fd2f522 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java @@ -20,339 +20,378 @@ package org.onap.policy.drools.protocol.coders; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import java.util.ArrayList; import java.util.List; - import java.util.concurrent.CopyOnWriteArrayList; import org.onap.policy.drools.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - /** * JSON Protocol Filter. */ public class JsonProtocolFilter { - - private static final String MISSING_RULE_NAME = "no rule name provided"; - /** - * Logger - */ - private static final Logger logger = LoggerFactory.getLogger(JsonProtocolFilter.class); - - /** - * Helper class to collect Filter information - */ - public static class FilterRule { - /** - * Field name - */ - private String name; - - /** - * Field Value regex - */ - private String regex; - - /** - * Filter Constructor - * - * @param name field name - * @param regex field regex value - */ - public FilterRule(String name, String regex) { - this.setName(name); - this.setRegex(regex); - } - - /** - * Default constructor (for serialization only) - */ - public FilterRule() { - super(); - } - - /** - * gets name - * - * @return - */ - public String getName() { - return name; - } - - /** - * gets regex - * - * @return - */ - public String getRegex() { - return regex; - } - - /** - * sets field name - * @param name field name - */ - public void setName(String name) { - if (name == null || name.isEmpty()) - throw new IllegalArgumentException("filter field name must be provided"); - - this.name = name; - } - - /** - * sets regex name - * @param regex - */ - public void setRegex(String regex) { - if (regex == null || regex.isEmpty()) - this.regex = ".*"; - - this.regex = regex; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Filter [name=").append(name).append(", regex=").append(regex).append("]"); - return builder.toString(); - } - } - - /** - * all the filters to be applied - */ - protected List<FilterRule> rules = new CopyOnWriteArrayList<>(); - - /** - * Create a Protocol Filter - * - * @throws IllegalArgumentException an invalid input has been provided - */ - public JsonProtocolFilter() {} - - /** - * - * @param filters filter list - * - * @throws IllegalArgumentException an invalid input has been provided - */ - public JsonProtocolFilter(List<FilterRule> filters) { - List<FilterRule> temp = new ArrayList<>(); - for (FilterRule rule : filters) { - if (rule.getName() == null || rule.getName().isEmpty()) { - continue; - } - - if (rule.getRegex() == null || rule.getRegex().isEmpty()) { - rule.setRegex(".*"); - } - - temp.add(rule); - } - - this.rules.addAll(temp); - } - - /** - * - * @param rawFilters raw filter initialization - * - * @throws IllegalArgumentException an invalid input has been provided - */ - public static JsonProtocolFilter fromRawFilters(List<Pair<String, String>> rawFilters) { - - if (rawFilters == null) { - throw new IllegalArgumentException("No raw filters provided"); - } - - List<FilterRule> filters = new ArrayList<>(); - for (Pair<String, String> filterPair: rawFilters) { - if (filterPair.first() == null || filterPair.first().isEmpty()) { - continue; - } - - filters.add(new FilterRule(filterPair.first(), filterPair.second())); - } - return new JsonProtocolFilter(filters); - } - - /** - * are there any filters? - * - * @return true if there are filters, false otherwise - */ - public boolean isRules() { - return !this.rules.isEmpty(); - } - - /** - * accept a JSON string as conformant it if passes all filters - * - * @param json json is a JSON object - * @return true if json string is conformant - * - * @throws IllegalArgumentException an invalid input has been provided - */ - public boolean accept(JsonElement json) { - if (json == null) { - throw new IllegalArgumentException("no JSON provided"); - } - - if (!json.isJsonObject()) { - return false; - } - - if (rules.isEmpty()) { - return true; - } - - try { - JsonObject event = json.getAsJsonObject(); - for (FilterRule filter: rules) { - if (filter.getRegex() == null || - filter.getRegex().isEmpty() || - ".*".equals(filter.getRegex())) { - - // Only check for presence - if (!event.has(filter.getName())) { - return false; - } - } else { - JsonElement field = event.get(filter.getName()); - if (field == null) { - return false; - } - - String fieldValue = field.getAsString(); - if (!fieldValue.matches(filter.getRegex())) { - return false; - } - } - } - return true; - } catch (Exception e) { - throw new IllegalArgumentException(e); - } - } - - /** - * accept a JSON string as conformant it if passes all filters - * - * @param json json string - * @return true if json string is conformant - * - * @throws IllegalArgumentException an invalid input has been provided - */ - public boolean accept(String json) { - if (json == null || json.isEmpty()) { - throw new IllegalArgumentException("no JSON provided"); - } - - if (rules.isEmpty()) { - return true; - } - - try { - JsonElement element = new JsonParser().parse(json); - if (element == null || !element.isJsonObject()) { - return false; - } - - return this.accept(element.getAsJsonObject()); - } catch (IllegalArgumentException ile) { - throw ile; - } catch (Exception e) { - logger.info("{}: cannot accept {} because of {}", - this, json, e.getMessage(), e); - throw new IllegalArgumentException(e); - } - } - - public List<FilterRule> getRules() { - return new ArrayList<>(this.rules); - } - - public List<FilterRule> getRules(String name) { - if (name == null || name.isEmpty()) - throw new IllegalArgumentException(MISSING_RULE_NAME); - - ArrayList<FilterRule> temp = new ArrayList<>(); - for (FilterRule rule : this.rules) { - if (rule.getName().equals(name)) { - temp.add(rule); - } - } - return temp; - } - - public void setRules(List<FilterRule> rulesFilters) { - if (rulesFilters == null) - throw new IllegalArgumentException("no rules provided"); - - this.rules.clear(); - this.rules.addAll(rulesFilters); - } - - public void deleteRules(String name) { - if (name == null || name.isEmpty()) - throw new IllegalArgumentException(MISSING_RULE_NAME); - - List<FilterRule> temp = new ArrayList<>(); - for (FilterRule rule : this.rules) { - if (rule.name.equals(name)) { - temp.add(rule); - } - } - this.rules.removeAll(temp); - } - - public void deleteRule(String name, String regex) { - if (name == null || name.isEmpty()) - throw new IllegalArgumentException(MISSING_RULE_NAME); - - String nonNullRegex = regex; - if (regex == null || regex.isEmpty()) { - nonNullRegex = ".*"; - } - - List<FilterRule> temp = new ArrayList<>(); - for (FilterRule rule : this.rules) { - if (rule.name.equals(name) && rule.getRegex().equals(nonNullRegex)) { - temp.add(rule); - } - } - - this.rules.removeAll(temp); - } - - public void addRule(String name, String regex) { - if (name == null || name.isEmpty()) - throw new IllegalArgumentException(MISSING_RULE_NAME); - - String nonNullRegex = regex; - if (regex == null || regex.isEmpty()) { - nonNullRegex = ".*"; - } - - for (FilterRule rule : this.rules) { - if (rule.getName().equals(name) && rule.getRegex().equals(regex)) { - return; - } - } - - this.rules.add(new FilterRule(name, nonNullRegex)); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("JsonProtocolFilter [rules=").append(rules).append("]"); - return builder.toString(); - } + + private static final String MISSING_RULE_NAME = "no rule name provided"; + /** + * Logger. + */ + private static final Logger logger = LoggerFactory.getLogger(JsonProtocolFilter.class); + + /** + * Helper class to collect Filter information. + */ + public static class FilterRule { + /** + * Field name. + */ + private String name; + + /** + * Field Value regex. + */ + private String regex; + + /** + * Filter Constructor. + * + * @param name field name + * @param regex field regex value + */ + public FilterRule(String name, String regex) { + this.setName(name); + this.setRegex(regex); + } + + /** + * Default constructor (for serialization only). + */ + public FilterRule() { + super(); + } + + /** + * gets name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * gets regex. + * + * @return regular expression string + */ + public String getRegex() { + return regex; + } + + /** + * Sets field name. + * + * @param name field name + */ + public void setName(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("filter field name must be provided"); + } + + this.name = name; + } + + /** + * sets regex name. + * + * @param regex expression + */ + public void setRegex(String regex) { + if (regex == null || regex.isEmpty()) { + this.regex = ".*"; + } + + this.regex = regex; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Filter [name=").append(name).append(", regex=").append(regex).append("]"); + return builder.toString(); + } + } + + /** + * all the filters to be applied. + */ + protected List<FilterRule> rules = new CopyOnWriteArrayList<>(); + + /** + * Create a Protocol Filter. + * + * @throws IllegalArgumentException an invalid input has been provided + */ + public JsonProtocolFilter() { + super(); + } + + /** + * Constructor. + * + * @param filters filter list + * + * @throws IllegalArgumentException an invalid input has been provided + */ + public JsonProtocolFilter(List<FilterRule> filters) { + List<FilterRule> temp = new ArrayList<>(); + for (FilterRule rule : filters) { + if (rule.getName() == null || rule.getName().isEmpty()) { + continue; + } + + if (rule.getRegex() == null || rule.getRegex().isEmpty()) { + rule.setRegex(".*"); + } + + temp.add(rule); + } + + this.rules.addAll(temp); + } + + /** + * From raw filters. + * + * @param rawFilters raw filter initialization + * + * @throws IllegalArgumentException an invalid input has been provided + */ + public static JsonProtocolFilter fromRawFilters(List<Pair<String, String>> rawFilters) { + + if (rawFilters == null) { + throw new IllegalArgumentException("No raw filters provided"); + } + + List<FilterRule> filters = new ArrayList<>(); + for (Pair<String, String> filterPair: rawFilters) { + if (filterPair.first() == null || filterPair.first().isEmpty()) { + continue; + } + + filters.add(new FilterRule(filterPair.first(), filterPair.second())); + } + return new JsonProtocolFilter(filters); + } + + /** + * are there any filters. + * + * @return true if there are filters, false otherwise + */ + public boolean isRules() { + return !this.rules.isEmpty(); + } + + /** + * accept a JSON string as conformant it if passes all filters. + * + * @param json json is a JSON object + * @return true if json string is conformant + * + * @throws IllegalArgumentException an invalid input has been provided + */ + public boolean accept(JsonElement json) { + if (json == null) { + throw new IllegalArgumentException("no JSON provided"); + } + + if (!json.isJsonObject()) { + return false; + } + + if (rules.isEmpty()) { + return true; + } + + try { + JsonObject event = json.getAsJsonObject(); + for (FilterRule filter: rules) { + if (filter.getRegex() == null + || filter.getRegex().isEmpty() + || ".*".equals(filter.getRegex())) { + + // Only check for presence + if (!event.has(filter.getName())) { + return false; + } + } else { + JsonElement field = event.get(filter.getName()); + if (field == null) { + return false; + } + + String fieldValue = field.getAsString(); + if (!fieldValue.matches(filter.getRegex())) { + return false; + } + } + } + return true; + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Accept a JSON string as conformant it if passes all filters. + * + * @param json json string + * @return true if json string is conformant + * + * @throws IllegalArgumentException an invalid input has been provided + */ + public boolean accept(String json) { + if (json == null || json.isEmpty()) { + throw new IllegalArgumentException("no JSON provided"); + } + + if (rules.isEmpty()) { + return true; + } + + try { + JsonElement element = new JsonParser().parse(json); + if (element == null || !element.isJsonObject()) { + return false; + } + + return this.accept(element.getAsJsonObject()); + } catch (IllegalArgumentException ile) { + throw ile; + } catch (Exception e) { + logger.info("{}: cannot accept {} because of {}", + this, json, e.getMessage(), e); + throw new IllegalArgumentException(e); + } + } + + public List<FilterRule> getRules() { + return new ArrayList<>(this.rules); + } + + /** + * Get rules. + * + * @param name name + * @return + */ + public List<FilterRule> getRules(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException(MISSING_RULE_NAME); + } + + ArrayList<FilterRule> temp = new ArrayList<>(); + for (FilterRule rule : this.rules) { + if (rule.getName().equals(name)) { + temp.add(rule); + } + } + return temp; + } + + /** + * Set Rules. + * + * @param rulesFilters filters + */ + public void setRules(List<FilterRule> rulesFilters) { + if (rulesFilters == null) { + throw new IllegalArgumentException("no rules provided"); + } + + this.rules.clear(); + this.rules.addAll(rulesFilters); + } + + /** + * Delete rules. + * + * @param name name + */ + public void deleteRules(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException(MISSING_RULE_NAME); + } + + List<FilterRule> temp = new ArrayList<>(); + for (FilterRule rule : this.rules) { + if (rule.name.equals(name)) { + temp.add(rule); + } + } + this.rules.removeAll(temp); + } + + /** + * Delete rule. + * + * @param name name + * @param regex regex + */ + public void deleteRule(String name, String regex) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException(MISSING_RULE_NAME); + } + + String nonNullRegex = regex; + if (regex == null || regex.isEmpty()) { + nonNullRegex = ".*"; + } + + List<FilterRule> temp = new ArrayList<>(); + for (FilterRule rule : this.rules) { + if (rule.name.equals(name) && rule.getRegex().equals(nonNullRegex)) { + temp.add(rule); + } + } + + this.rules.removeAll(temp); + } + + /** + * Add rule. + * + * @param name name + * @param regex regex + */ + public void addRule(String name, String regex) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException(MISSING_RULE_NAME); + } + + String nonNullRegex = regex; + if (regex == null || regex.isEmpty()) { + nonNullRegex = ".*"; + } + + for (FilterRule rule : this.rules) { + if (rule.getName().equals(name) && rule.getRegex().equals(regex)) { + return; + } + } + + this.rules.add(new FilterRule(name, nonNullRegex)); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("JsonProtocolFilter [rules=").append(rules).append("]"); + return builder.toString(); + } } |