001 package biz.hammurapi.rules.jsr94.admin; 002 003 import java.io.File; 004 import java.io.FileInputStream; 005 import java.io.FileOutputStream; 006 import java.io.IOException; 007 import java.io.OutputStream; 008 import java.rmi.RemoteException; 009 import java.util.HashMap; 010 import java.util.Iterator; 011 import java.util.Map; 012 013 import javax.rules.RuleServiceProvider; 014 import javax.rules.RuleServiceProviderManager; 015 import javax.rules.admin.LocalRuleExecutionSetProvider; 016 import javax.rules.admin.Rule; 017 import javax.rules.admin.RuleAdministrator; 018 import javax.rules.admin.RuleExecutionSet; 019 import javax.rules.admin.RuleExecutionSetDeregistrationException; 020 import javax.rules.admin.RuleExecutionSetProvider; 021 import javax.rules.admin.RuleExecutionSetRegisterException; 022 import javax.xml.parsers.DocumentBuilderFactory; 023 import javax.xml.parsers.FactoryConfigurationError; 024 import javax.xml.parsers.ParserConfigurationException; 025 import javax.xml.transform.TransformerException; 026 import javax.xml.transform.stream.StreamResult; 027 import javax.xml.xpath.XPathExpressionException; 028 029 import org.apache.commons.cli.CommandLine; 030 import org.apache.commons.cli.CommandLineParser; 031 import org.apache.commons.cli.HelpFormatter; 032 import org.apache.commons.cli.Option; 033 import org.apache.commons.cli.OptionBuilder; 034 import org.apache.commons.cli.Options; 035 import org.apache.commons.cli.PosixParser; 036 import org.w3c.dom.Document; 037 import org.w3c.dom.Element; 038 import org.w3c.dom.NodeList; 039 import org.xml.sax.SAXException; 040 041 import biz.hammurapi.config.ConfigurationException; 042 import biz.hammurapi.rules.jsr94.FileRuleServiceProvider; 043 import biz.hammurapi.rules.jsr94.FileRuleServiceProvider.Registration; 044 import biz.hammurapi.util.StreamPumper; 045 import biz.hammurapi.xml.dom.AbstractDomObject; 046 import biz.hammurapi.xml.dom.DOMUtils; 047 import biz.hammurapi.xml.dom.DomSerializable; 048 049 050 /** 051 * This rule administrator stores rulesets in an XML file. 052 * @author Pavel Vlasov 053 * @revision $Revision$ 054 */ 055 public class FileRuleAdministrator implements javax.rules.admin.RuleAdministrator { 056 057 private Map registrations; 058 private File regFile; 059 060 /** 061 * Creates new instance. 062 * @param registrations 063 * @param homeDir 064 */ 065 public FileRuleAdministrator(Map registrations, File regFile) { 066 this.registrations=registrations; 067 this.regFile=regFile; 068 } 069 070 /** 071 * Loads registrations from a file. 072 * @throws SAXException 073 * @throws IOException 074 * @throws ParserConfigurationException 075 * @throws FactoryConfigurationError 076 * @throws TransformerException 077 * @throws ConfigurationException 078 * @throws XPathExpressionException 079 */ 080 private void load() throws SAXException, IOException, ParserConfigurationException, FactoryConfigurationError, ConfigurationException, XPathExpressionException { 081 if (regFile.exists()) { 082 registrations.clear(); 083 Document doc=DOMUtils.parse(regFile); 084 NodeList nl=DOMUtils.selectNodeList(doc.getDocumentElement(), "/registrations/registration"); 085 for (int i=0, l=nl.getLength(); i<l; ++i) { 086 Registration r=new FileRuleServiceProvider.Registration(nl.item(i)); 087 registrations.put(r.getUri(), r); 088 } 089 } 090 } 091 092 /** 093 * Stores registrations to a file. 094 * @throws ParserConfigurationException 095 * @throws FactoryConfigurationError 096 * @throws TransformerException 097 * @throws IOException 098 */ 099 private void store() throws ParserConfigurationException, FactoryConfigurationError, IOException, TransformerException { 100 DOMUtils.serialize(registrations2Dom().getDocumentElement(), regFile); 101 } 102 103 /** 104 * @return 105 * @throws ParserConfigurationException 106 * @throws FactoryConfigurationError 107 */ 108 private Document registrations2Dom() throws ParserConfigurationException, FactoryConfigurationError { 109 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 110 Element root = AbstractDomObject.addElement(doc, "registrations"); 111 Iterator rit=registrations.values().iterator(); 112 while (rit.hasNext()) { 113 ((DomSerializable) rit.next()).toDom(AbstractDomObject.addElement(root, "registration")); 114 } 115 return doc; 116 } 117 118 /** 119 * Creates new RuleExecutionSetProvider. Propeties are passed to the provider. 120 */ 121 public RuleExecutionSetProvider getRuleExecutionSetProvider(Map properties) throws RemoteException { 122 return new biz.hammurapi.rules.jsr94.admin.RuleExecutionSetProvider(properties); 123 } 124 125 /** 126 * Creates new LocalRuleExecutionSetProvider. Properties are passed to the provider. 127 */ 128 public LocalRuleExecutionSetProvider getLocalRuleExecutionSetProvider(Map properties) throws RemoteException { 129 return new biz.hammurapi.rules.jsr94.admin.LocalRuleExecutionSetProvider(properties); 130 } 131 132 /** 133 * Registers rule execution set. 134 * Rule execution set can be registered by reference and by value. 135 * When registered by reference the set url is stored in the file instead of actual XML definition. 136 * To store rule execution set by reference put {"by-reference", Boolean.TRUE} to the properties map. 137 */ 138 public void registerRuleExecutionSet(String uri, RuleExecutionSet res, Map properties) throws RuleExecutionSetRegisterException, RemoteException { 139 if (uri.startsWith("direct:")) { 140 throw new RuleExecutionSetRegisterException("Invalid rule set URI, 'direct:' is a reserved prefix."); 141 } 142 boolean byRef=properties!=null && Boolean.TRUE.equals(properties.get("by-reference")); 143 biz.hammurapi.rules.jsr94.admin.RuleExecutionSet cRes = ((biz.hammurapi.rules.jsr94.admin.RuleExecutionSet) res); 144 if (byRef && cRes.isModified()) { 145 throw new RuleExecutionSetRegisterException("Cannot register modified rule set by reference"); 146 } 147 148 if (byRef && cRes.getUri()==null) { 149 throw new RuleExecutionSetRegisterException("Cannot register rule set by reference - URI is null"); 150 } 151 152 synchronized (registrations) { 153 try { 154 load(); 155 if (byRef) { 156 registrations.put(uri, new FileRuleServiceProvider.Registration(uri, cRes.getUri(), null, cRes.getProperties())); 157 } else { 158 registrations.put(uri, new FileRuleServiceProvider.Registration(uri, null, cRes.getHolder(), cRes.getProperties())); 159 } 160 store(); 161 } catch (Exception e) { 162 throw new RuleExecutionSetRegisterException("Could not deregister rule set with URI: "+uri+", "+e, e); 163 } 164 } 165 } 166 167 /** 168 * Properties are ignored. 169 */ 170 public void deregisterRuleExecutionSet(String uri, Map properties) throws RuleExecutionSetDeregistrationException, RemoteException { 171 synchronized (registrations) { 172 try { 173 load(); 174 registrations.remove(uri); 175 store(); 176 } catch (Exception e) { 177 throw new RuleExecutionSetDeregistrationException("Could not deregister rule set with URI: "+uri+", "+e, e); 178 } 179 } 180 } 181 182 public static void main(String[] args) throws Exception { 183 System.out.println("Hammurapi Rules administrator. Copyright (C) 2006 Hammurapi Group"); 184 String ruleServiceProviderClassName = "biz.hammurapi.rules.jsr94.FileRuleServiceProvider"; 185 Class.forName(ruleServiceProviderClassName); 186 RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider(ruleServiceProviderClassName); 187 RuleAdministrator administrator = serviceProvider.getRuleAdministrator(); 188 189 if (args.length==0) { 190 printHelpAndExit(); 191 } else if ("register".equals(args[0])) { 192 Options options=new Options(); 193 194 Option helpOption=OptionBuilder 195 .withDescription("Print this message") 196 .isRequired(false) 197 .create("h"); 198 199 options.addOption(helpOption); 200 201 Option fileOption=OptionBuilder 202 .withArgName("file") 203 .hasArg() 204 .withDescription("Ruleset file") 205 .isRequired(false) 206 .create("f"); 207 208 options.addOption(fileOption); 209 210 211 Option urlOption=OptionBuilder 212 .withArgName("URL") 213 .hasArg() 214 .withDescription("Ruleset URL") 215 .isRequired(false) 216 .create("u"); 217 218 options.addOption(urlOption); 219 220 Option refOption=OptionBuilder 221 .withArgName("URL") 222 .hasArg() 223 .withDescription("Ruleset URL to be registered by reference") 224 .isRequired(false) 225 .create("r"); 226 227 options.addOption(refOption); 228 229 Option propertyOption=OptionBuilder 230 .withArgName("key=value") 231 .hasArg() 232 .withDescription("Rule set property") 233 .isRequired(false) 234 .create("p"); 235 236 options.addOption(propertyOption); 237 238 Option rulePropertyOption=OptionBuilder 239 .withArgName("rule name>:<key>=<value") 240 .hasArg() 241 .withDescription("Rule property") 242 .isRequired(false) 243 .create("R"); 244 245 options.addOption(rulePropertyOption); 246 247 Option objectFilterOption=OptionBuilder 248 .withArgName("class name") 249 .hasArg() 250 .withDescription("Default object filter") 251 .isRequired(false) 252 .create("o"); 253 254 options.addOption(objectFilterOption); 255 256 CommandLineParser parser=new PosixParser(); 257 CommandLine line=null; 258 259 try { 260 line=parser.parse(options, args); 261 } catch (org.apache.commons.cli.ParseException e) { 262 System.err.println(e.getMessage()); 263 System.err.flush(); 264 printHelpAndExit("register <uri>", options); 265 } 266 267 if (line.hasOption("h")) { 268 printHelpAndExit("register <uri>", options); 269 } 270 271 int no=0; 272 if (line.hasOption('f')) { 273 ++no; 274 } 275 if (line.hasOption('u')) { 276 ++no; 277 } 278 if (line.hasOption('r')) { 279 ++no; 280 } 281 282 if (no==0) { 283 System.out.println("One of -f, -u or -r options must be specified"); 284 printHelpAndExit("register <uri>", options); 285 } 286 287 if (no>1) { 288 System.out.println("Options -f, -u, and -r are mutually exclusive"); 289 printHelpAndExit("register <uri>", options); 290 } 291 292 String[] largs=line.getArgs(); 293 if (largs.length!=2) { 294 System.out.println("Invalid parameters"); 295 printHelpAndExit("register <uri>", options); 296 } 297 298 RuleExecutionSetProvider resp = administrator.getRuleExecutionSetProvider(null); 299 300 Map properties=null; 301 if (line.hasOption('p')) { 302 properties=new HashMap(); 303 String[] props=line.getOptionValues('p'); 304 for (int i=0; i<props.length; i++) { 305 int idx=props[i].indexOf('='); 306 if (idx==-1 || idx==props[i].length()-1 || idx==0) { 307 System.out.println("Invalid property definition: "+props[i]); 308 printHelpAndExit("register <uri>", options); 309 } 310 properties.put(props[i].substring(0, idx), props[i].substring(idx+1)); 311 } 312 } 313 314 if (line.hasOption('f')) { 315 RuleExecutionSet res = resp.createRuleExecutionSet(DOMUtils.parse(new File(line.getOptionValue('f'))).getDocumentElement(), properties); 316 setObjectFilterAndRuleProperties(line, options, res); 317 administrator.registerRuleExecutionSet(largs[1], res, null); 318 } 319 320 if (line.hasOption('u')) { 321 RuleExecutionSet res = resp.createRuleExecutionSet(line.getOptionValue('u'), properties); 322 setObjectFilterAndRuleProperties(line, options, res); 323 administrator.registerRuleExecutionSet(largs[1], res, null); 324 } 325 326 if (line.hasOption('r')) { 327 if (line.hasOption('R')) { 328 System.out.println("Rule properties cannot be set in rule sets registered by reference"); 329 printHelpAndExit("register <uri>", options); 330 } 331 332 if (line.hasOption('o')) { 333 System.out.println("Defalut object filter cannot be set in rule sets registered by reference"); 334 printHelpAndExit("register <uri>", options); 335 } 336 337 RuleExecutionSet res = resp.createRuleExecutionSet(line.getOptionValue('r'), properties); 338 HashMap regProperties = new HashMap(); 339 regProperties.put("by-reference", Boolean.TRUE); 340 administrator.registerRuleExecutionSet(largs[1], res, regProperties); 341 } 342 343 } else if ("deregister".equals(args[0])) { 344 Options options=new Options(); 345 346 Option helpOption=OptionBuilder 347 .withDescription("Print this message") 348 .isRequired(false) 349 .create("h"); 350 351 options.addOption(helpOption); 352 353 CommandLineParser parser=new PosixParser(); 354 CommandLine line=null; 355 356 try { 357 line=parser.parse(options, args); 358 } catch (org.apache.commons.cli.ParseException e) { 359 System.err.println(e.getMessage()); 360 System.err.flush(); 361 printHelpAndExit("deregister <uri>", options); 362 } 363 364 if (line.hasOption("h")) { 365 printHelpAndExit("deregister <uri>", options); 366 } 367 368 String[] largs=line.getArgs(); 369 if (largs.length!=2) { 370 System.out.println("Invalid parameters"); 371 printHelpAndExit("deregister <uri>", options); 372 } 373 374 administrator.deregisterRuleExecutionSet(largs[1], null); 375 } else if ("list".equals(args[0])) { 376 Options options=new Options(); 377 378 Option helpOption=OptionBuilder 379 .withDescription("Print this message") 380 .isRequired(false) 381 .create("h"); 382 383 options.addOption(helpOption); 384 385 Option fileOption=OptionBuilder 386 .withArgName("file") 387 .hasArg() 388 .withDescription("Output file") 389 .isRequired(false) 390 .create("f"); 391 392 options.addOption(fileOption); 393 394 Option xmlOption=OptionBuilder 395 .withDescription("Output in XML format") 396 .isRequired(false) 397 .create("x"); 398 399 options.addOption(xmlOption); 400 401 402 Option htmlOption=OptionBuilder 403 .withDescription("Output in HTML format") 404 .isRequired(false) 405 .create("H"); 406 407 options.addOption(htmlOption); 408 409 CommandLineParser parser=new PosixParser(); 410 CommandLine line=null; 411 412 try { 413 line=parser.parse(options, args); 414 } catch (org.apache.commons.cli.ParseException e) { 415 System.err.println(e.getMessage()); 416 System.err.flush(); 417 printHelpAndExit("list", options); 418 } 419 420 if (line.hasOption("h")) { 421 printHelpAndExit("list", options); 422 } 423 424 OutputStream out; 425 if (line.hasOption('f')) { 426 out=new FileOutputStream(new File(line.getOptionValue('f'))); 427 } else { 428 out=System.out; 429 } 430 431 File regFile=((FileRuleAdministrator) administrator).regFile; 432 433 if (line.hasOption('x')) { 434 FileInputStream in=new FileInputStream(regFile); 435 try { 436 new StreamPumper(in, out, null, false).run(); 437 } finally { 438 in.close(); 439 if (line.hasOption('f')) { 440 out.close(); 441 } 442 } 443 } else if (line.hasOption('H')) { 444 try { 445 DOMUtils.style( 446 DOMUtils.parse(regFile), 447 out, 448 FileRuleAdministrator.class.getResourceAsStream("list_html.xsl"), 449 null); 450 } finally { 451 if (line.hasOption('f')) { 452 out.close(); 453 } 454 } 455 } else { 456 try { 457 DOMUtils.style( 458 DOMUtils.parse(regFile), 459 out, 460 FileRuleAdministrator.class.getResourceAsStream("list_text.xsl"), 461 null); 462 } finally { 463 if (line.hasOption('f')) { 464 out.close(); 465 } 466 } 467 } 468 } else if ("dump".equals(args[0])) { 469 Options options=new Options(); 470 471 Option helpOption=OptionBuilder 472 .withDescription("Print this message") 473 .isRequired(false) 474 .create("h"); 475 476 options.addOption(helpOption); 477 478 Option fileOption=OptionBuilder 479 .withArgName("file") 480 .hasArg() 481 .withDescription("Output file") 482 .isRequired(false) 483 .create("f"); 484 485 options.addOption(fileOption); 486 487 Option xmlOption=OptionBuilder 488 .withDescription("Output in XML format") 489 .isRequired(false) 490 .create("x"); 491 492 options.addOption(xmlOption); 493 494 495 Option htmlOption=OptionBuilder 496 .withDescription("Output in HTML format") 497 .isRequired(false) 498 .create("H"); 499 500 options.addOption(htmlOption); 501 502 CommandLineParser parser=new PosixParser(); 503 CommandLine line=null; 504 505 try { 506 line=parser.parse(options, args); 507 } catch (org.apache.commons.cli.ParseException e) { 508 System.err.println(e.getMessage()); 509 System.err.flush(); 510 printHelpAndExit("dump <uri>", options); 511 } 512 513 if (line.hasOption("h")) { 514 printHelpAndExit("dump <uri>", options); 515 } 516 517 String[] largs=line.getArgs(); 518 if (largs.length!=2) { 519 System.out.println("Invalid parameters"); 520 printHelpAndExit("dump <uri>", options); 521 } 522 523 OutputStream out; 524 if (line.hasOption('f')) { 525 out=new FileOutputStream(new File(line.getOptionValue('f'))); 526 } else { 527 out=System.out; 528 } 529 530 Object registration = ((FileRuleAdministrator) administrator).registrations.get(largs[1]); 531 532 if (line.hasOption('x')) { 533 try { 534 DOMUtils.serialize(registration, "registration", new StreamResult(out)); 535 } finally { 536 if (line.hasOption('f')) { 537 out.close(); 538 } 539 } 540 } else if (line.hasOption('H')) { 541 try { 542 DOMUtils.style( 543 registration, 544 "registration", 545 out, 546 FileRuleAdministrator.class.getResourceAsStream("dump_html.xsl"), 547 null); 548 } finally { 549 if (line.hasOption('f')) { 550 out.close(); 551 } 552 } 553 } else { 554 try { 555 DOMUtils.style( 556 registration, 557 "registration", 558 out, 559 FileRuleAdministrator.class.getResourceAsStream("dump_text.xsl"), 560 null); 561 } finally { 562 if (line.hasOption('f')) { 563 out.close(); 564 } 565 } 566 } 567 } else { 568 System.out.println("Unrecognized command: "+args[0]); 569 printHelpAndExit(); 570 } 571 } 572 573 /** 574 * Reads default object filter and rule properties from command line and sets them in RuleExecutionSet. 575 * @param line 576 * @param res 577 */ 578 private static void setObjectFilterAndRuleProperties(CommandLine line, Options options, RuleExecutionSet res) { 579 if (line.hasOption('o')) { 580 res.setDefaultObjectFilter(line.getOptionValue('o')); 581 } 582 583 if (line.hasOption('R')) { 584 String[] ruleProperties=line.getOptionValues('R'); 585 586 Z: for (int i=0; i<ruleProperties.length; i++) { 587 int idx=ruleProperties[i].indexOf(":"); 588 if (idx<=0 || idx==ruleProperties[i].length()-1) { 589 System.out.println("Rule property format: <Rule name>:<property name>=<property value>"); 590 printHelpAndExit("register <uri>", options); 591 } 592 593 String ruleName=ruleProperties[i].substring(0, idx); 594 595 int idx2=ruleProperties[i].indexOf("=", idx); 596 if (idx2==-1 || idx2==ruleProperties[i].length()-1) { 597 System.out.println("Rule property format: <Rule name>:<property name>=<property value>"); 598 printHelpAndExit("register <uri>", options); 599 } 600 601 String propertyName=ruleProperties[i].substring(idx+1, idx2); 602 String propertyValue=ruleProperties[i].substring(idx2+1); 603 604 Iterator it=res.getRules().iterator(); 605 while (it.hasNext()) { 606 Rule rule=(Rule) it.next(); 607 if (ruleName.equals(rule.getName())) { 608 rule.setProperty(propertyName, propertyValue); 609 continue Z; 610 } 611 } 612 613 System.out.println("Rule not found: "+ruleName); 614 printHelpAndExit("register <uri>", options); 615 } 616 } 617 618 } 619 620 protected static void printHelpAndExit() { 621 System.out.println("Usage: hradmin command [options]"); 622 System.out.println("\tCommands:"); 623 System.out.println("\t\tregister <uri> {-f <file> | -u <url> | -r <url>} [-p <name>=<value>] [-R <rule name>:<property name>=<property value>] [-o <object filter>]- Registers rule set."); 624 System.out.println("\t\tderegister <uri> - Unregisters rule set."); 625 System.out.println("\t\tlist [-f <file>] {-x|-H} - Lists names of registered rule sets to console or file."); 626 System.out.println("\t\tdump <uri> [-f <file>] {-x|-H} - Outputs ruleset definition to console or file."); 627 System.out.println("\n\tExecute 'hradmin command -h' for command-specific options"); 628 629 System.exit(1); 630 } 631 632 protected static void printHelpAndExit(String command, Options options) { 633 HelpFormatter formatter=new HelpFormatter(); 634 formatter.printHelp("hradmin "+command+" [options]", options, false); 635 System.exit(1); 636 } 637 638 639 }