001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors.
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.repository.rules;
023    
024    import java.io.ByteArrayInputStream;
025    import java.io.InputStreamReader;
026    import java.io.Reader;
027    import java.util.Collections;
028    import java.util.HashMap;
029    import java.util.Map;
030    import javax.rules.RuleServiceProvider;
031    import javax.rules.admin.RuleExecutionSet;
032    import javax.rules.admin.RuleExecutionSetProvider;
033    import net.jcip.annotations.Immutable;
034    import org.jboss.dna.common.component.ClassLoaderFactory;
035    import org.jboss.dna.common.component.ComponentConfig;
036    import org.jboss.dna.common.util.CheckArg;
037    
038    /**
039     * A description of a set of rules compatible with a JSR-94 rule engine.
040     * @author Randall Hauch
041     */
042    @Immutable
043    public class RuleSet extends ComponentConfig implements Cloneable {
044    
045        private final String providerUri;
046        private final String ruleSetUri;
047        private final String rules;
048        private final Map<String, Object> properties;
049    
050        /**
051         * Create a JSR-94 rule set definition.
052         * @param name the name of the rule set, which is considered the unique identifier
053         * @param description the description
054         * @param classname the name of the Java class used for the component
055         * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
056         * @param providerUri the URI of the JSR-94 {@link RuleServiceProvider} implementation to use
057         * @param ruleSetUri the URI of the JSR-94 {@link RuleExecutionSet} represented by this object; if null, the name is used
058         * @param rules the string containing the rules in a provider-specific language
059         * @param properties the provider-specific properties, whose values should be strings or byte arrays (the latter if the
060         * provider expects an {@link Reader} with the value)
061         * @throws IllegalArgumentException if any of the name, classname, provider URI, or rules parameters are null, empty or blank,
062         * or if the classname is not a valid Java classname
063         */
064        public RuleSet( String name, String description, String classname, String[] classpath, String providerUri, String ruleSetUri, String rules, Map<String, Object> properties ) {
065            super(name, description, System.currentTimeMillis(), classname, classpath);
066            if (ruleSetUri == null) ruleSetUri = name.trim();
067            CheckArg.isNotEmpty(ruleSetUri, "rule set URI");
068            CheckArg.isNotEmpty(providerUri, "provider URI");
069            CheckArg.isNotEmpty(rules, "rules");
070            this.providerUri = providerUri;
071            this.ruleSetUri = ruleSetUri;
072            this.rules = rules;
073            if (properties == null) properties = Collections.emptyMap();
074            this.properties = Collections.unmodifiableMap(properties);
075        }
076    
077        /**
078         * Get the URI of the JSR-94 {@link RuleServiceProvider} implementation that should be used.
079         * @return the URI of the JSR-94 implementation; never null, empty or blank
080         */
081        public String getProviderUri() {
082            return this.providerUri;
083        }
084    
085        /**
086         * Get the URI of this rule set. The value must be valid as defined by JSR-94 {@link RuleExecutionSet}.
087         * @return the rule set's URI; never null, empty or blank
088         */
089        public String getRuleSetUri() {
090            return this.ruleSetUri;
091        }
092    
093        /**
094         * Get the rules defined in terms of the language reqired by the {@link #getProviderUri() provider}.
095         * @return the rules for this rule set
096         */
097        public String getRules() {
098            return this.rules;
099        }
100    
101        /**
102         * Get this rule set's properties as an unmodifiable map. Note that the values of these properties are either strings if the
103         * value is to be {@link #getExecutionSetProperties() passed} literally, or a byte array if the value is to be
104         * {@link #getExecutionSetProperties() passed} as an InputStream.
105         * @return the unmodifiable properties; never null but possible empty
106         */
107        public Map<String, Object> getProperties() {
108            return this.properties;
109        }
110    
111        /**
112         * Get the properties for this rule set that can be passed to an {@link RuleExecutionSetProvider}'s
113         * {@link RuleExecutionSetProvider#createRuleExecutionSet(String, Map) createRuleExecutionSet} method.
114         * <p>
115         * This method converts any byte array value in the {@link #getProperties() properties} into an {@link Reader}. Since
116         * {@link ByteArrayInputStream} is used, there is no need to close these stream.
117         * </p>
118         * @return the properties; never null but possible empty
119         */
120        public Map<Object, Object> getExecutionSetProperties() {
121            Map<Object, Object> props = new HashMap<Object, Object>();
122            for (Map.Entry<String, Object> entry : this.properties.entrySet()) {
123                String key = entry.getKey();
124                Object value = entry.getValue();
125                if (value instanceof byte[]) {
126                    value = new InputStreamReader(new ByteArrayInputStream((byte[])value));
127                }
128                props.put(key, value);
129            }
130            return props;
131        }
132    
133        /**
134         * {@inheritDoc}
135         */
136        @Override
137        public boolean hasChanged( ComponentConfig obj ) {
138            if (super.hasChanged(obj)) return true;
139            RuleSet that = (RuleSet)obj;
140            if (!this.providerUri.equals(that.providerUri)) return true;
141            if (!this.ruleSetUri.equals(that.ruleSetUri)) return true;
142            return false;
143        }
144    
145        /**
146         * {@inheritDoc}
147         */
148        @Override
149        public RuleSet clone() {
150            return new RuleSet(this.getName(), this.getDescription(), this.getComponentClassname(), this.getComponentClasspathArray(), this.providerUri, this.ruleSetUri, this.rules, this.properties);
151        }
152    }