1 16 package org.apache.cocoon.components.language.generator; 17 18 import org.apache.avalon.framework.activity.Disposable; 19 import org.apache.avalon.framework.component.ComponentException; 20 import org.apache.avalon.framework.component.ComponentManager; 21 import org.apache.avalon.framework.component.ComponentSelector; 22 import org.apache.avalon.framework.component.Composable; 23 import org.apache.avalon.framework.component.Recomposable; 24 import org.apache.avalon.framework.context.Context; 25 import org.apache.avalon.framework.context.ContextException; 26 import org.apache.avalon.framework.context.Contextualizable; 27 import org.apache.avalon.framework.logger.AbstractLogEnabled; 28 import org.apache.avalon.framework.parameters.ParameterException; 29 import org.apache.avalon.framework.parameters.Parameterizable; 30 import org.apache.avalon.framework.parameters.Parameters; 31 import org.apache.avalon.framework.thread.ThreadSafe; 32 import org.apache.cocoon.Constants; 33 import org.apache.cocoon.ProcessingException; 34 import org.apache.cocoon.components.classloader.ClassLoaderManager; 35 import org.apache.cocoon.components.language.LanguageException; 36 import org.apache.cocoon.components.language.markup.MarkupLanguage; 37 import org.apache.cocoon.components.language.programming.CodeFormatter; 38 import org.apache.cocoon.components.language.programming.Program; 39 import org.apache.cocoon.components.language.programming.ProgrammingLanguage; 40 import org.apache.cocoon.environment.SourceResolver; 41 import org.apache.cocoon.util.IOUtils; 42 import org.apache.excalibur.source.Source; 43 44 import java.io.File ; 45 import java.net.MalformedURLException ; 46 47 55 public class ProgramGeneratorImpl extends AbstractLogEnabled 56 implements ProgramGenerator, Contextualizable, Composable, Parameterizable, 57 Disposable, ThreadSafe { 58 59 60 protected boolean autoReload = true; 61 62 63 protected boolean preload = false; 64 65 66 protected boolean watchSource = false; 67 68 72 protected GeneratorSelector cache; 73 74 75 protected ComponentManager manager; 76 77 78 protected ComponentSelector markupSelector; 79 80 81 protected ComponentSelector languageSelector; 82 83 84 protected File workDir; 85 86 87 protected ClassLoaderManager classManager; 88 89 90 protected String rootPackage; 91 92 93 protected String contextDir; 94 95 96 97 public void contextualize(Context context) throws ContextException { 98 if (this.workDir == null) { 99 this.workDir = (File ) context.get(Constants.CONTEXT_WORK_DIR); 100 } 101 102 if (this.contextDir == null) { 103 org.apache.cocoon.environment.Context ctx = 104 (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 105 106 try { 110 String rootPath = ctx.getRealPath("/"); 111 if (rootPath != null) { 112 this.contextDir = new File (rootPath).toURL().toExternalForm(); 113 } else { 114 String webInf = ctx.getResource("/WEB-INF").toExternalForm(); 115 this.contextDir = webInf.substring(0, webInf.length() - "WEB-INF".length()); 116 } 117 if (getLogger().isDebugEnabled()) { 118 getLogger().debug("Context directory is " + this.contextDir); 119 } 120 } catch (MalformedURLException e) { 121 getLogger().warn("Could not get context directory", e); 122 this.contextDir = ""; 123 } 124 } 125 } 126 127 133 public void compose(ComponentManager manager) throws ComponentException { 134 if (this.manager == null && manager != null) { 135 this.manager = manager; 136 this.cache = (GeneratorSelector) this.manager.lookup(GeneratorSelector.ROLE + "Selector"); 137 this.markupSelector = (ComponentSelector)this.manager.lookup(MarkupLanguage.ROLE + "Selector"); 138 this.languageSelector = (ComponentSelector)this.manager.lookup(ProgrammingLanguage.ROLE + "Selector"); 139 this.classManager = (ClassLoaderManager)this.manager.lookup(ClassLoaderManager.ROLE); 140 } 141 } 142 143 148 public void parameterize(Parameters params) throws ParameterException { 149 this.autoReload = params.getParameterAsBoolean("auto-reload", autoReload); 150 this.rootPackage = params.getParameter("root-package", "org.apache.cocoon.www"); 151 this.preload = params.getParameterAsBoolean("preload", preload); 152 this.watchSource = params.getParameterAsBoolean("watch-source", watchSource); 153 } 154 155 159 private String getNormalizedName(final String systemId) { 160 StringBuffer contextFilename = new StringBuffer (this.rootPackage.replace('.', File.separatorChar)); 161 contextFilename.append(File.separator); 162 if(systemId.startsWith(this.contextDir)) { 163 contextFilename.append(systemId.substring(this.contextDir.length())); 165 } else { 166 contextFilename.append(systemId); 168 } 169 return IOUtils.normalizedFilename(contextFilename.toString()); 170 } 171 172 182 public CompiledComponent load(ComponentManager newManager, 183 String fileName, 184 String markupLanguageName, 185 String programmingLanguageName, 186 SourceResolver resolver) 187 throws Exception { 188 189 final Source source = resolver.resolveURI(fileName); 190 try { 191 return load(newManager, source, markupLanguageName, programmingLanguageName, resolver); 192 } finally { 193 resolver.release(source); 194 } 195 } 196 197 209 public CompiledComponent load(ComponentManager newManager, 210 Source source, 211 String markupLanguageName, 212 String programmingLanguageName, 213 SourceResolver resolver) 214 throws Exception { 215 216 final String id = source.getURI(); 217 218 ProgrammingLanguage programmingLanguage = null; 219 MarkupLanguage markupLanguage = null; 220 try { 221 final String normalizedName = getNormalizedName(id); 223 224 if (getLogger().isDebugEnabled()) { 225 getLogger().debug("Loading serverpage systemId=[" + id + "]" + 226 " markupLanguageName=[" + markupLanguageName + "]" + 227 " programmingLanguageName=[" + programmingLanguageName + "]" + 228 " -> normalizedName=[" + normalizedName + "]"); 229 } 230 231 markupLanguage = (MarkupLanguage) this.markupSelector.select(markupLanguageName); 232 programmingLanguage = (ProgrammingLanguage) this.languageSelector.select(programmingLanguageName); 233 programmingLanguage.setLanguageName(programmingLanguageName); 234 235 Program program = null; 236 CompiledComponent programInstance = null; 237 238 try { 240 programInstance = (CompiledComponent) this.cache.select(normalizedName); 241 } catch (Exception e) { 242 if (getLogger().isDebugEnabled()) { 243 getLogger().debug("The serverpage [" + id + "] is not in the cache yet"); 244 } 245 } 246 247 if (programInstance == null && this.preload) { 248 try { 250 program = programmingLanguage.preload(normalizedName, 251 this.workDir, 252 markupLanguage.getEncoding()); 253 254 this.cache.addGenerator(newManager, normalizedName, program); 255 programInstance = (CompiledComponent) this.cache.select(normalizedName); 256 257 if (getLogger().isDebugEnabled()) { 258 getLogger().debug("Successfully preloaded serverpage [" + id + "]"); 259 } 260 } catch (Exception e) { 261 if (getLogger().isInfoEnabled()) { 262 getLogger().info("The serverpage [" + id 263 + "] could not be preloaded, will be re-created (" 264 + e + ")"); 265 } 266 } 267 } 268 269 if (programInstance == null) { 270 synchronized (this) { 271 try { 275 programInstance = (CompiledComponent) this.cache.select(normalizedName); 276 if (getLogger().isDebugEnabled()) { 277 getLogger().debug("The serverpage [" + id + "] was now in the cache"); 278 } 279 } catch (Exception e) { 280 if (getLogger().isDebugEnabled()) { 282 getLogger().debug("Creating new serverpage for [" + id + "]"); 283 } 284 generateSourcecode(source, 285 normalizedName, 286 markupLanguage, 287 programmingLanguage); 288 289 programInstance = loadProgram(newManager, 290 normalizedName, 291 markupLanguage, 292 programmingLanguage); 293 } 294 } 295 } else { 296 if (this.autoReload) { 298 long sourceLastModified = source.getLastModified(); 299 if (programInstance.modifiedSince(sourceLastModified)) { 303 if (getLogger().isDebugEnabled()) { 304 getLogger().debug("ReCreating serverpage for [" + id + "]"); 305 } 306 synchronized (this) { 307 if (getLogger().isDebugEnabled()) { 308 getLogger().debug("Releasing old serverpage program [" + id + "]"); 309 } 310 release(programInstance); 311 programmingLanguage.unload(program, normalizedName, this.workDir); 312 this.cache.removeGenerator(normalizedName); 313 programInstance = null; 314 program = null; 315 316 generateSourcecode(source, 317 normalizedName, 318 markupLanguage, 319 programmingLanguage); 320 321 programInstance = loadProgram(newManager, 322 normalizedName, 323 markupLanguage, 324 programmingLanguage); 325 } 326 } else { 327 if (this.watchSource) { 329 if (getLogger().isDebugEnabled()) { 330 getLogger().debug("Checking sourcecode of [" + id + "] for a change"); 331 } 332 File sourcecodeFile = new File (this.workDir, 333 normalizedName + "." + programmingLanguage.getSourceExtension()); 334 if (sourcecodeFile != null && sourcecodeFile.exists()) { 336 long sourcecodeLastModified = sourcecodeFile.lastModified(); 337 if (sourcecodeLastModified > sourceLastModified 338 || sourceLastModified == 0 339 || sourcecodeLastModified == 0) { 340 if (getLogger().isDebugEnabled()) { 341 getLogger().debug("Create new serverpage program for [" + id + "] - repository has changed"); 342 } 343 synchronized (this) { 344 if (getLogger().isDebugEnabled()) { 345 getLogger().debug("Releasing old serverpage program [" + id + "]"); 346 } 347 release(programInstance); 348 this.cache.removeGenerator(normalizedName); 350 programInstance = null; 351 program = null; 352 353 programInstance = loadProgram(newManager, 354 normalizedName, 355 markupLanguage, 356 programmingLanguage); 357 } 358 } else { 359 if (getLogger().isDebugEnabled()) { 360 getLogger().debug("Sourcecode of [" + id + "] has not changed - returning program from cache"); 361 } 362 } 363 } else { 364 if (getLogger().isErrorEnabled()) { 365 getLogger().error("Could not find sourcecode for [" + id + "]"); 366 } 367 } 368 } 369 } 370 } else { 371 if (getLogger().isDebugEnabled()) { 372 getLogger().debug("Not checking for modifications [autoReload=false] - using current version"); 373 } 374 } 375 } 376 377 if (programInstance instanceof Recomposable) { 382 ((Recomposable) programInstance).recompose(newManager); 383 } 384 385 return (programInstance); 386 } finally { 387 this.markupSelector.release(markupLanguage); 388 this.languageSelector.release(programmingLanguage); 389 } 390 } 391 392 private CompiledComponent loadProgram(ComponentManager newManager, 393 String normalizedName, 394 MarkupLanguage markupLanguage, 395 ProgrammingLanguage programmingLanguage) 396 throws Exception { 397 398 CompiledComponent programInstance = null; 399 400 try { 401 return (CompiledComponent) this.cache.select(normalizedName); 402 } catch (Exception e) { 403 } 404 405 try { 406 if (getLogger().isDebugEnabled()) { 407 getLogger().debug("Loading program [" + normalizedName + "]"); 408 } 409 Program program = programmingLanguage.load(normalizedName, this.workDir, markupLanguage.getEncoding()); 410 411 this.cache.addGenerator(newManager, normalizedName, program); 412 if (getLogger().isDebugEnabled()) { 413 getLogger().debug("Successfully loaded program [" + normalizedName + "]"); 414 } 415 } catch (LanguageException le) { 416 if (getLogger().isDebugEnabled()) { 417 getLogger().debug("Got Language Exception", le); 418 } 419 throw new ProcessingException("Language Exception", le); 420 } 421 422 try { 423 programInstance = (CompiledComponent) this.cache.select(normalizedName); 424 } catch (Exception cme) { 425 if (getLogger().isDebugEnabled()) { 426 getLogger().debug("Can't load ServerPage: got exception", cme); 427 } 428 throw new ProcessingException("Can't load ServerPage", cme); 429 } 430 431 return (programInstance); 432 } 433 434 435 private void generateSourcecode(Source source, 436 String normalizedName, 437 MarkupLanguage markupLanguage, 438 ProgrammingLanguage programmingLanguage) 439 throws Exception { 440 441 if (getLogger().isDebugEnabled()) { 442 getLogger().debug("Creating sourcecode for [" + source.getURI() + "]"); 443 } 444 445 String code = markupLanguage.generateCode(source, normalizedName, programmingLanguage); 447 if (code == null || code.length() == 0) { 448 throw new ProcessingException("Failed to generate program code (this may happen " + 452 "if you use Xalan in incremental processing mode). " + 453 "Please check log file and/or console for errors."); 454 } 455 456 String encoding = markupLanguage.getEncoding(); 457 458 CodeFormatter codeFormatter = programmingLanguage.getCodeFormatter(); 460 if (codeFormatter != null) { 461 code = codeFormatter.format(code, encoding); 462 } 463 464 final File sourceFile = new File (this.workDir, normalizedName + "." + programmingLanguage.getSourceExtension()); 466 final File sourceDir = sourceFile.getParentFile(); 467 if (sourceDir != null) { 468 sourceDir.mkdirs(); 469 } 470 IOUtils.serializeString(sourceFile, code); 471 if (getLogger().isDebugEnabled()) { 472 getLogger().debug("Successfully created sourcecode for [" + source.getURI() + "]"); 473 } 474 } 475 476 480 public void release(CompiledComponent component) { 481 this.cache.release(component); 482 } 483 484 489 public void remove(Source source) { 490 final String normalizedName = getNormalizedName(source.getURI()); 491 this.cache.removeGenerator(normalizedName); 492 } 493 494 497 public void dispose() { 498 this.manager.release(this.cache); 499 this.cache = null; 500 this.manager.release(this.markupSelector); 501 this.markupSelector = null; 502 this.manager.release(this.languageSelector); 503 this.languageSelector = null; 504 this.manager.release(this.classManager); 505 this.classManager = null; 506 507 this.manager = null; 508 509 this.workDir = null; 510 this.contextDir = null; 511 } 512 } 513 | Popular Tags |