1 22 package org.jboss.services.deployment; 23 24 import java.io.BufferedWriter ; 25 import java.io.File ; 26 import java.io.FileFilter ; 27 import java.io.FileInputStream ; 28 import java.io.FileOutputStream ; 29 import java.io.FileWriter ; 30 import java.io.IOException ; 31 import java.io.InputStream ; 32 import java.io.OutputStream ; 33 import java.io.PrintWriter ; 34 import java.io.StringBufferInputStream ; 35 import java.io.StringWriter ; 36 import java.net.URL ; 37 import java.util.ArrayList ; 38 import java.util.Collection ; 39 import java.util.Collections ; 40 import java.util.HashMap ; 41 import java.util.Iterator ; 42 import java.util.List ; 43 import java.util.Map ; 44 import java.util.Set ; 45 import java.util.TreeMap ; 46 import java.util.TreeSet ; 47 48 import javax.management.ObjectName ; 49 50 import org.apache.velocity.VelocityContext; 51 import org.apache.velocity.app.VelocityEngine; 52 import org.jboss.deployment.DeploymentInfo; 53 import org.jboss.logging.Logger; 54 import org.jboss.mx.util.MBeanServerLocator; 55 import org.jboss.services.deployment.metadata.ConfigInfo; 56 import org.jboss.services.deployment.metadata.ConfigInfoBinding; 57 import org.jboss.services.deployment.metadata.PropertyInfo; 58 import org.jboss.services.deployment.metadata.TemplateInfo; 59 import org.jboss.system.server.ServerConfig; 60 import org.jboss.system.server.ServerConfigLocator; 61 import org.jboss.util.file.Files; 62 import org.jboss.varia.deployment.convertor.XslTransformer; 63 import org.jboss.xb.binding.ObjectModelFactory; 64 import org.jboss.xb.binding.Unmarshaller; 65 import org.jboss.xb.binding.UnmarshallerFactory; 66 67 76 public class DeploymentManager 77 { 78 80 81 public static final String TEMPLATE_CONFIG_FILE = "template-config.xml"; 82 83 84 public static final String TEMPLATE_ERROR_PARAM = "template-error"; 85 86 87 public static final String CONTEXT_HELPER = "helper"; 88 89 90 private static final String MAIN_DEPLOYER_OBJECT_NAME = "jboss.system:service=MainDeployer"; 91 92 93 private static final String MAIN_DEPLOYER_LIST_OPERATION_NAME = "listDeployed"; 94 95 97 98 private Logger log; 99 100 101 private File templateDir; 102 103 104 private File undeployDir; 105 106 107 private File deployDir; 108 109 110 private Map configMap; 111 112 113 VelocityEngine ve; 114 115 119 public DeploymentManager(String templateDir, String undeployDir, String deployDir, Logger log) 120 throws Exception 121 { 122 this.log = log; 123 124 initialize(templateDir, undeployDir, deployDir); 126 } 127 128 130 133 public Set listModuleTemplates() 134 { 135 Set keys = configMap.keySet(); 136 137 synchronized(configMap) 138 { 139 return new TreeSet (keys); 141 } 142 } 143 144 151 public List getTemplatePropertyInfo(String template) 152 throws Exception 153 { 154 ConfigInfo ci = (ConfigInfo)configMap.get(template); 155 156 if (ci == null) 157 { 158 throw new Exception ("template does not exist: " + template); 159 } 160 else 161 { List propertyList = ci.getPropertyInfoList(); 163 List newList = new ArrayList (propertyList.size()); 164 165 for (Iterator i = propertyList.iterator(); i.hasNext();) 166 { 167 newList.add(new PropertyInfo((PropertyInfo)i.next())); 168 } 169 return newList; 170 } 171 } 172 173 public String createModule(String module, String template, HashMap properties) 174 throws Exception 175 { 176 if (module == null || template == null || properties == null) 177 throw new Exception ("Null argument: module=" + module + 178 ", template=" + template + ", properties=" + properties); 179 180 if (!module.equals(Files.encodeFileName(module))) 182 throw new Exception ("not a filesystem friendly module name: " + module); 183 184 log.info("createModule(module=" + module + 185 ", template=" + template + ", properties=" + properties + ")"); 186 187 ConfigInfo ci = (ConfigInfo)configMap.get(template); 188 189 if (ci == null) 190 throw new Exception ("template does not exist: " + template); 191 192 File outputModule; 195 196 String extension = ci.getExtension(); 197 if (extension == null || module.endsWith(extension)) 198 outputModule = new File (this.undeployDir, module); 199 else 200 outputModule = new File (this.undeployDir, module + extension); 201 202 if (outputModule.exists()) 204 throw new Exception ("module already exist: " + outputModule); 205 206 String vmTemplate = ci.getTemplate(); 207 208 try 210 { 211 if (vmTemplate != null ) 213 { 214 VelocityContext ctx = createTemplateContext(ci, properties); 215 216 BufferedWriter out = new BufferedWriter (new FileWriter (outputModule)); 217 218 try { 219 boolean success = ve.mergeTemplate(template + '/' + vmTemplate, ctx, out); 220 221 if (success == true) 222 { 223 String errorMsg = (String )ctx.get(TEMPLATE_ERROR_PARAM); 224 225 if (errorMsg.length() > 0) 226 throw new Exception ("Template error: " + errorMsg); 227 else 228 log.debug("created module '" + outputModule.getName() + "' based on template '" + template + "'"); 229 } 230 else 231 throw new Exception ("Failed to create module '" + outputModule.getName()); 232 } 233 finally 234 { 235 out.close(); 236 } 237 } 238 else 239 { 240 VelocityContext ctx = createTemplateContext(ci, properties); 243 244 String copydir = ci.getCopydir(); 246 247 File sourceDir = new File (this.templateDir, template + '/' + copydir); 248 249 deepCopy(sourceDir, outputModule); 250 251 List templateList = ci.getTemplateInfoList(); 253 254 for (Iterator i = templateList.iterator(); i.hasNext(); ) 255 { 256 TemplateInfo ti = (TemplateInfo)i.next(); 257 258 File outputFile = new File (outputModule, ti.getOutput()); 259 File outputPath = outputFile.getParentFile(); 260 261 if (!outputPath.exists()) 262 if (!outputPath.mkdirs()) 263 throw new IOException ("cannot create directory: " + outputPath); 264 265 BufferedWriter out = new BufferedWriter (new FileWriter (outputFile)); 266 267 try { 268 boolean success = ve.mergeTemplate(template + '/' + ti.getInput(), ctx, out); 269 270 if (success == true) 271 { 272 String errorMsg = (String )ctx.get(TEMPLATE_ERROR_PARAM); 273 274 if (errorMsg.length() > 0) 275 throw new Exception ("Template error: " + errorMsg); 276 else 277 log.debug("created module '" + outputModule.getName() + "' based on template '" + template + "'"); 278 } 279 280 else 281 throw new Exception ("Failed to create package '" + outputModule.getName()); 282 } 283 finally 284 { 285 out.close(); 286 } 287 } 288 } 289 } 290 catch (Exception e) 291 { 292 if (outputModule.exists()) 293 { 294 boolean deleted = Files.delete(outputModule); 295 if (!deleted) 296 log.warn("Failed to clean-up erroneous module: " + outputModule); 297 } 298 throw e; 299 } 300 return outputModule.getName(); 301 } 302 303 309 public boolean removeModule(String module) 310 { 311 File target = new File (this.undeployDir, module); 312 return Files.delete(target); 313 } 314 315 318 public boolean updateMBean(MBeanData data) throws Exception 319 { 320 if (data == null) 322 throw new Exception ("Null argument: data=" + data); 323 if (data.getName() == null || data.getTemplateName() == null 324 || data.getName().length() == 0 || data.getTemplateName().length() == 0 325 ) 326 throw new Exception ("Null required data: name=" + data.getName() + ", templateName=" + data.getTemplateName()); 327 boolean result = true; 328 329 if (log.isDebugEnabled()) 331 { 332 log.debug("updateMBean(" + data + ")"); 333 log.debug(" template=" + data.getTemplateName()); 334 log.debug(" depends=" + data.getDepends()); 335 log.debug(" attributes=" + data.getAttributes()); 336 log.debug(" xpath=" + data.getXpath()); 337 } 338 339 String template = data.getTemplateName(); 341 ConfigInfo ci = (ConfigInfo)configMap.get(template); 342 if (ci == null) 343 throw new Exception ("template does not exist: " + template); 344 345 String configPath = configPathFor(data.getName()); 348 log.debug("configPath=" + configPath); 349 if (configPath == null) 350 throw new Exception ("No configuration file found for mbean " + data); 351 352 HashMap map = new HashMap (); 353 map.put("mbean", data); 354 355 updateConfigFile(data.getName(), template, map, ci, configPath); 356 357 return result; 358 } 359 360 363 public String updateDataSource(String module, String template, HashMap properties) throws Exception 364 { 365 if (module == null || template == null || properties == null) 366 throw new Exception ("Null argument: module=" + module + ", template=" + template + ", properties=" 367 + properties); 368 369 log.info("updateDataSource(module=" + module + ", template=" + template + ", properties=" + properties + ")"); 370 371 if (!template.endsWith("-update")) 373 template += "-update"; 374 375 module = processDataSourceChanges(module, template, properties); 376 377 return module; 378 } 379 380 383 public String removeDataSource(String module, String template, HashMap properties) throws Exception 384 { 385 if (module == null || template == null || properties == null) 386 throw new Exception ("Null argument: module=" + module + ", template=" + template + ", properties=" 387 + properties); 388 389 log.info("removeDataSource(module=" + module + ", template=" + template + ", properties=" + properties + ")"); 390 391 if (!template.endsWith("-remove")) 393 template += "-remove"; 394 395 module = processDataSourceChanges(module, template, properties); 396 397 return module; 398 } 399 400 401 408 public String configPathFor(String name) throws Exception 409 { 410 if (log.isDebugEnabled()) log.debug("configPathFor(" + name + ")"); 411 String result = null; 412 413 Collection deploymentColl = (Collection ) MBeanServerLocator.locateJBoss().invoke( 415 new ObjectName (MAIN_DEPLOYER_OBJECT_NAME), MAIN_DEPLOYER_LIST_OPERATION_NAME, null, null); 416 417 ObjectName pattern = new ObjectName (name); 420 outer: for (Iterator iter = deploymentColl.iterator(); iter.hasNext();) 421 { 422 DeploymentInfo deploymentInfo = (DeploymentInfo) iter.next(); 423 List associatedMBeanObjectNames = deploymentInfo.mbeans; 424 for (Iterator iterator = associatedMBeanObjectNames.iterator(); iterator.hasNext();) 425 { 426 ObjectName beanName = (ObjectName ) iterator.next(); 427 if (log.isDebugEnabled()) log.debug("beanName=" + beanName); 428 if (pattern.apply(beanName)) { 429 result = deploymentInfo.watch.getFile(); 430 break outer; 431 } 432 } 433 } 434 435 if (log.isDebugEnabled()) log.debug("configPathFor()=" + result); 436 return result; 437 } 438 439 public void moveToDeployDir(String module) 440 throws Exception 441 { 442 File source = new File (this.undeployDir, module); 443 File target = new File (this.deployDir, module); 444 445 if (source.exists()) 446 { 447 boolean moved = source.renameTo(target); 448 if (!moved) 449 throw new Exception ("cannot move module: " + module); 450 } 451 else 452 throw new Exception ("module does not exist: " + module); 453 } 454 455 public void moveToModuleDir(String module) 456 throws Exception 457 { 458 File source = new File (this.deployDir, module); 459 File target = new File (this.undeployDir, module); 460 461 if (source.exists()) 462 { 463 boolean moved = source.renameTo(target); 464 if (!moved) 465 throw new Exception ("cannot move module: " + module); 466 } 467 else 468 throw new Exception ("module does not exist: " + module); 469 } 470 471 public URL getDeployedURL(String module) 472 throws Exception 473 { 474 File target = new File (this.deployDir, module); 475 476 if (!target.exists()) 477 throw new Exception ("module does not exist: " + target); 478 479 return target.toURL(); 480 } 481 482 public URL getUndeployedURL(String module) 483 throws Exception 484 { 485 File target = new File (this.undeployDir, module); 486 487 if (!target.exists()) 488 throw new Exception ("module does not exist: " + target); 489 490 return target.toURL(); 491 } 492 493 495 498 private void initialize(String templateDir, String undeployDir, String deployDir) 499 throws Exception 500 { 501 log.debug("DeploymentManager.initialize()"); 502 this.templateDir = initDir(templateDir, false); 504 log.debug("template dir=" + this.templateDir); 505 506 this.undeployDir = initDir(undeployDir, true); 508 log.debug("undeployDir dir=" + this.undeployDir); 509 510 this.deployDir = initDir(deployDir, false); 511 log.debug("deploy dir=" + this.deployDir); 512 513 List configFiles = findTemplateConfigFiles(this.templateDir); 515 516 log.debug("template config files=" + configFiles); 517 518 Map map = Collections.synchronizedMap(new TreeMap ()); 519 520 for (Iterator i = configFiles.iterator(); i.hasNext(); ) { 522 File file = (File )i.next(); 523 ConfigInfo ci = parseXMLconfig(file); 524 525 ci.setName(file.getParentFile().getName()); 527 528 if (log.isTraceEnabled()) 529 log.trace("file: " + file + " ConfigInfo: " + ci); 530 531 Object existingValue = map.put(ci.getName(), ci); 532 533 if (existingValue != null) 535 throw new Exception ("Duplicate template configuration entry: " + ci.getName()); 536 } 537 538 this.configMap = map; 539 540 this.ve = new VelocityEngine(); 542 543 this.ve.setProperty("runtime.log.logsystem.class", 544 "org.apache.velocity.runtime.log.SimpleLog4JLogSystem"); 545 this.ve.setProperty("runtime.log.logsystem.log4j.category", 546 log.getName() + ".VelocityEngine"); 547 this.ve.setProperty("file.resource.loader.path", this.templateDir.getCanonicalPath()); 548 this.ve.setProperty("stringliterals.interpolate", "false"); 549 550 this.ve.init(); 551 } 552 553 559 private File initDir(String targetDir, boolean create) 560 throws Exception 561 { 562 File dir = null; 563 564 try { 566 URL fileURL = new URL (targetDir); 567 568 File file = new File (fileURL.getFile()); 569 570 if(file.isDirectory() && file.canRead() && file.canWrite()) { 571 dir = file; 572 } 573 } 574 catch(Exception e) { 575 577 File homeDir = ServerConfigLocator.locate().getServerHomeDir(); 578 579 dir = new File (homeDir, targetDir); 580 581 if (create == true) 582 dir.mkdirs(); 583 584 if (!dir.isDirectory()) 585 throw new Exception ("The target directory is not valid: " 586 + dir.getCanonicalPath()); 587 } 588 return dir; 589 } 590 591 601 private List findTemplateConfigFiles(File basedir) 602 { 603 List files = new ArrayList (); 605 606 FileFilter dirFilter = new FileFilter () 608 { 609 public boolean accept(File file) 610 { 611 return file.isDirectory() && !file.getName().startsWith("."); 612 } 613 }; 614 File [] dirs = basedir.listFiles(dirFilter); 616 617 for (int i = 0; i < dirs.length; i++) { 618 File file = new File (dirs[i], TEMPLATE_CONFIG_FILE); 619 620 if (file.isFile() && file.canRead()) 621 files.add(file); 622 } 623 return files; 624 } 625 626 634 private ConfigInfo parseXMLconfig(File file) 635 throws Exception 636 { 637 InputStream is = new FileInputStream (file); 639 640 Unmarshaller unmarshaller = UnmarshallerFactory.newInstance().newUnmarshaller(); 642 643 ObjectModelFactory factory = new ConfigInfoBinding(); 645 646 ConfigInfo ci = (ConfigInfo)unmarshaller.unmarshal(is, factory, null); 648 649 is.close(); 651 652 return ci; 653 } 654 655 666 private VelocityContext createTemplateContext(ConfigInfo ci, HashMap map) 667 throws Exception 668 { 669 VelocityContext vc; 670 671 List propertyList = ci.getPropertyInfoList(); 672 673 if (propertyList.size() > 0) 674 { 675 vc = new VelocityContext(); 676 677 for (Iterator i = propertyList.iterator(); i.hasNext(); ) { 678 PropertyInfo pi = (PropertyInfo)i.next(); 679 680 String name = pi.getName(); 681 String type = pi.getType(); 682 boolean optional = pi.isOptional(); 683 Object defaultValue = pi.getDefaultValue(); 684 685 if (name == null || name.length() == 0 || type == null || type.length() == 0) 686 throw new Exception ("Null or empty name/type property metadata for template: " + ci.getName()); 687 688 Object sentValue = map.get(name); 689 690 if (sentValue != null) 692 { 693 if (!type.equals(sentValue.getClass().getName())) 694 throw new Exception ("Expected type '" + type + "' for property '" + name + 695 "', got '" + sentValue.getClass().getName()); 696 697 vc.put(name, sentValue); 698 } 699 else if (optional == false) { 700 if (defaultValue != null) { 703 vc.put(name, defaultValue); 704 } 705 else { 706 throw new Exception ("Required property missing: '" + name + "' of type '" + type + "'"); 707 } 708 } 709 } 712 } 713 else 714 { 715 vc = new VelocityContext(map); 718 } 719 vc.put(TEMPLATE_ERROR_PARAM, ""); 721 vc.put(CONTEXT_HELPER, new ContextHelper()); 723 724 return vc; 725 } 726 727 734 private void deepCopy(File sourceDir, File targetDir) 735 throws IOException 736 { 737 if (!sourceDir.isDirectory()) 738 throw new IOException ("sourceDir not a directory: " + sourceDir); 739 740 if (!targetDir.mkdir()) 741 throw new IOException ("could not create directory: " + targetDir); 742 743 File [] files = sourceDir.listFiles(); 744 745 for (int i = 0; i < files.length; i++) 746 { 747 File source = files[i]; 748 749 if (!source.canRead()) 750 throw new IOException ("cannot read: " + source); 751 752 if (source.isFile()) 753 Files.copy(source, new File (targetDir, source.getName())); 754 else 755 deepCopy(source, new File (targetDir, source.getName())); 756 } 757 } 758 759 769 private String processDataSourceChanges (String module, String template, HashMap properties) throws Exception 770 { 771 ConfigInfo ci = (ConfigInfo) configMap.get(template); 772 if (ci == null) 773 throw new Exception ("template does not exist: " + template); 774 775 String extension = ci.getExtension(); 777 if (extension != null && !module.endsWith(extension)) 778 module += extension; 779 780 String path = configPathFor("jboss.jca:name=" + (String )properties.get("jndi-name") + ",*"); 783 784 updateConfigFile(module, template, properties, ci, path); 785 786 return module; 787 } 788 789 801 private void updateConfigFile(String name, String template, HashMap map, ConfigInfo ci, String path) 802 throws Exception 803 { 804 String vmTemplate = ci.getTemplate(); 805 806 StringWriter sw = new StringWriter (); 808 PrintWriter xslt = new PrintWriter (sw); 809 VelocityContext ctx = createTemplateContext(ci, map); 810 String tp = template + File.separator + vmTemplate; 811 ve.mergeTemplate(tp, ctx, xslt); 812 StringBuffer buf = sw.getBuffer(); 813 xslt.close(); 814 815 File configFile = new File (path); 818 File outputFile = new File (this.undeployDir, configFile.getName()); 819 InputStream configStream = new FileInputStream (configFile); 820 InputStream xsltStream = new StringBufferInputStream (buf.toString()); 821 OutputStream outputStream = new FileOutputStream (outputFile); 822 XslTransformer.applyTransformation(configStream, outputStream, xsltStream, null); 823 configStream.close(); 824 xsltStream.close(); 825 outputStream.close(); 826 827 if (!configFile.delete()) 829 throw new Exception ("Update failed for '" + name + "', unable to delete old configuration file: " + path); 830 if (!outputFile.renameTo(configFile)) 831 throw new Exception ("Update failed for '" + name + "', unable to move configuration file to deploy directory: " + path); 832 } 833 } 834 | Popular Tags |