001    package biz.hammurapi.rules.jsr94;
002    
003    import java.io.File;
004    import java.io.IOException;
005    import java.util.HashMap;
006    import java.util.Map;
007    
008    import javax.rules.ConfigurationException;
009    import javax.rules.RuleRuntime;
010    import javax.rules.RuleServiceProviderManager;
011    import javax.rules.admin.RuleAdministrator;
012    import javax.xml.parsers.FactoryConfigurationError;
013    import javax.xml.parsers.ParserConfigurationException;
014    import javax.xml.transform.TransformerException;
015    import javax.xml.xpath.XPathExpressionException;
016    
017    import org.w3c.dom.Document;
018    import org.w3c.dom.Element;
019    import org.w3c.dom.Node;
020    import org.w3c.dom.NodeList;
021    import org.xml.sax.SAXException;
022    
023    import biz.hammurapi.config.DomConfigFactory;
024    import biz.hammurapi.rules.jsr94.admin.FileRuleAdministrator;
025    import biz.hammurapi.xml.dom.AbstractDomObject;
026    import biz.hammurapi.xml.dom.DOMUtils;
027    import biz.hammurapi.xml.dom.DomSerializable;
028    
029    /**
030     * This rule service provider reads configuration from XML files.
031     * XML definitions are read from rules home directory. By default it is <code>.hammurapi-rules</code> directory
032     * in user home directory. This can be changed by setting <code>biz.hammurapi.rules.jsr94.FileRuleServiceProvider:home</code>
033     * property to custom rules home directory name.
034     * @author Pavel Vlasov
035     * @version ${Revision}
036     */
037    public class FileRuleServiceProvider extends javax.rules.RuleServiceProvider {
038            
039            private static String home;
040            
041            static {
042                    try {
043                            RuleServiceProviderManager.registerRuleServiceProvider("biz.hammurapi.rules.jsr94.FileRuleServiceProvider", biz.hammurapi.rules.jsr94.FileRuleServiceProvider.class);
044                    } catch (ConfigurationException e) {
045                            System.err.println("Could not register biz.hammurapi.rules.jsr94.FileRuleServiceProvider");
046                            e.printStackTrace();
047                    }
048                                    
049                    home=System.getProperty(biz.hammurapi.rules.jsr94.FileRuleServiceProvider.class.getName()+":home");
050            }
051            
052            /**
053             * Registration entry
054             * @author Pavel Vlasov
055             * @version ${Revision}
056             */
057            public static class Registration implements DomSerializable {
058                    private String uri;
059                    
060                    /**
061                     * @return Returns the uri.
062                     */
063                    public String getUri() {
064                            return uri;
065                    }
066                    
067                    /**
068                     * @param uri The uri to set.
069                     */
070                    public void setUri(String uri) {
071                            this.uri = uri;
072                    }
073                    
074                    private String ref;
075                    
076                    /**
077                     * @return Returns the ref.
078                     */
079                    public String getRef() {
080                            return ref;
081                    }
082                            
083                    /**
084                     * @param ref The ref to set.
085                     */
086                    public void setRef(String ref) {
087                            this.ref = ref;
088                    }
089                    
090                    private Node value;
091                    
092                    /**
093                     * @return Returns the value.
094                     */
095                    public Node getValue() {
096                            return value;
097                    }
098                    
099                    /**
100                     * @param value The value to set.
101                     */
102                    public void setValue(Node value) {
103                            this.value = value;
104                    }
105    
106                    /* (non-Javadoc)
107                     * @see biz.hammurapi.xml.dom.DomSerializable#toDom(org.w3c.dom.Element)
108                     */
109                    public void toDom(Element holder) {
110                            holder.setAttribute("uri", uri);
111                            if (ref!=null) {
112                                    holder.setAttribute("ref", ref);                        
113                            } else if (value!=null) {
114                                    Element de=AbstractDomObject.addElement(holder, "definition");
115                                    de.appendChild(holder.getOwnerDocument().importNode(value, true));
116                            }
117                            
118                            if (properties!=null) {
119                                    DOMUtils.toDom(properties, "properties", holder);
120                            }                                               
121                    }
122    
123                    /* (non-Javadoc)
124                     * @see biz.hammurapi.config.DomConfigurable#configure(org.w3c.dom.Node, biz.hammurapi.config.Context)
125                     */
126                    public Registration(Node configNode) throws biz.hammurapi.config.ConfigurationException, XPathExpressionException {
127                            Element configElement = (Element) configNode;
128                            uri=configElement.getAttribute("uri");
129                            if (configElement.hasAttribute("ref")) {
130                                    ref=configElement.getAttribute("ref");
131                            } else {
132                                    value=DOMUtils.selectSingleNode(configNode, "definition/*");
133                            }                       
134                            
135                            Node propertiesNode=DOMUtils.selectSingleNode(configNode, "properties");
136                            if (propertiesNode!=null) {
137                                    properties=(Map) new DomConfigFactory().create(propertiesNode);
138                            }
139                    }       
140                    
141    
142                    private Map properties;
143                    
144                    /**
145                     * @return Returns the properties.
146                     */
147                    public Map getProperties() {
148                            return properties;
149                    }
150    
151                    public Registration(String uri, String ref, Node value, Map properties) {
152                            super();
153                            // TODO Auto-generated constructor stub
154                            this.uri = uri;
155                            this.ref = ref;
156                            this.value = value;
157                            
158                            if (properties!=null) {
159                                    this.properties = new HashMap(properties);
160                            }
161                    }
162            }
163            
164            private Map registrations=new HashMap();
165            private RuleRuntime runtime;
166            private RuleAdministrator administrator;
167            private File regFile;
168            
169            /**
170             * Constructs rule service provider and reads 
171             * rule definitions from XML file <code>registrations.xml</code> in
172             * rules home directory.
173             *
174             */
175            public FileRuleServiceProvider() {
176                    File homeDir;
177                    
178                    if (home==null) {
179                            homeDir=new File(new File(System.getProperty("user.home")), ".hammurapi-rules");
180                    } else {
181                            homeDir=new File(home);
182                    }
183                    
184                    if (homeDir.exists()) {
185                            if (!homeDir.isDirectory()) {
186                                    errorMessage="Invalid home, not a directory - "+homeDir.getAbsolutePath();
187                            }
188                    } else if (!homeDir.mkdirs()) {
189                            errorMessage="Cannot create home directory "+homeDir.getAbsolutePath();
190                    }
191                    
192                    regFile=new File(homeDir, "registrations.xml");         
193                    try {
194                            load();
195                    } catch (Exception e) {
196                            errorMessage="Could not load registrations: "+e;
197                    }                               
198                                    
199                    runtime=new FileRuleRuntime(registrations);
200                    administrator=new FileRuleAdministrator(registrations, regFile);        
201            }
202    
203            /**
204             * Loads configuration from XML file
205             * @param regFile
206             * @throws SAXException
207             * @throws IOException
208             * @throws ParserConfigurationException
209             * @throws FactoryConfigurationError
210             * @throws TransformerException 
211             * @throws biz.hammurapi.config.ConfigurationException 
212             * @throws XPathExpressionException 
213             */
214            private void load() throws SAXException, IOException, ParserConfigurationException, FactoryConfigurationError, biz.hammurapi.config.ConfigurationException, XPathExpressionException {
215                    if (regFile.exists()) {
216                            synchronized (registrations) {
217                                    registrations.clear();
218                                    Document doc=DOMUtils.parse(regFile);
219                                    NodeList nl=DOMUtils.selectNodeList(doc.getDocumentElement(), "/registrations/registration");
220                                    for (int i=0, l=nl.getLength(); i<l; ++i) {                                  
221                                            Registration r=new Registration(nl.item(i));
222                                            registrations.put(r.getUri(), r);
223                                    }
224                            }
225                    }
226            }
227            
228            private String errorMessage;
229    
230            public RuleRuntime getRuleRuntime() throws ConfigurationException {
231                    if (errorMessage!=null) {
232                            throw new ConfigurationException(errorMessage);
233                    }
234                    
235                    return runtime;
236            }
237    
238            public RuleAdministrator getRuleAdministrator() throws ConfigurationException {
239                    if (errorMessage!=null) {
240                            throw new ConfigurationException(errorMessage);
241                    }
242                    
243                    return administrator;
244            }               
245    }