1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 package org.opensolaris.opengrok.configuration; 25 26 import java.beans.XMLDecoder; 27 import java.beans.XMLEncoder; 28 import java.io.BufferedInputStream; 29 import java.io.File; 30 import java.io.IOException; 31 import java.net.InetAddress; 32 import java.net.ServerSocket; 33 import java.net.Socket; 34 import java.net.SocketAddress; 35 import java.net.UnknownHostException; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 import org.opensolaris.opengrok.OpenGrokLogger; 41 import org.opensolaris.opengrok.history.Repository; 42 import org.opensolaris.opengrok.index.IgnoredNames; 43 import org.opensolaris.opengrok.util.Executor; 44 45 /** 46 * The RuntimeEnvironment class is used as a placeholder for the current 47 * configuration this execution context (classloader) is using. 48 */ 49 public final class RuntimeEnvironment { 50 private Configuration configuration; 51 private final ThreadLocal<Configuration> threadConfig; 52 53 private static final Logger log = Logger.getLogger(RuntimeEnvironment.class.getName()); 54 55 private static RuntimeEnvironment instance = new RuntimeEnvironment(); 56 57 /** 58 * Get the one and only instance of the RuntimeEnvironment 59 * @return the one and only instance of the RuntimeEnvironment 60 */ 61 public static RuntimeEnvironment getInstance() { 62 return instance; 63 } 64 65 /** 66 * Creates a new instance of RuntimeEnvironment. Private to ensure a 67 * singleton pattern. 68 */ 69 private RuntimeEnvironment() { 70 configuration = new Configuration(); 71 threadConfig = new ThreadLocal<Configuration>() { 72 @Override protected Configuration initialValue() { 73 return configuration; 74 } 75 }; 76 } 77 78 private String getCanonicalPath(String s) { 79 try { 80 File file = new File(s); 81 if (!file.exists()) { 82 return s; 83 } 84 return file.getCanonicalPath(); 85 } catch (IOException ex) { 86 OpenGrokLogger.getLogger().log(Level.SEVERE, "Failed to get canonical path", ex); 87 return s; 88 } 89 } 90 91 /** 92 * Get the path to the where the index database is stored 93 * @return the path to the index database 94 */ 95 public String getDataRootPath() { 96 return threadConfig.get().getDataRoot(); 97 } 98 99 /** 100 * Get a file representing the index database 101 * @return the index database 102 */ 103 public File getDataRootFile() { 104 File ret = null; 105 String file = getDataRootPath(); 106 if (file != null) { 107 ret = new File(file); 108 } 109 110 return ret; 111 } 112 113 /** 114 * Set the path to where the index database is stored 115 * @param dataRoot the index database 116 */ 117 public void setDataRoot(String dataRoot) { 118 final File file = new File(dataRoot); 119 if (!file.exists() && !file.mkdirs()) { 120 OpenGrokLogger.getLogger().log( 121 Level.SEVERE, "Failed to create dataroot: " + dataRoot); 122 } 123 threadConfig.get().setDataRoot(getCanonicalPath(dataRoot)); 124 } 125 126 /** 127 * Get the path to where the sources are located 128 * @return path to where the sources are located 129 */ 130 public String getSourceRootPath() { 131 return threadConfig.get().getSourceRoot(); 132 } 133 134 /** 135 * Get a file representing the directory where the sources are located 136 * @return A file representing the directory where the sources are located 137 */ 138 public File getSourceRootFile() { 139 File ret = null; 140 String file = getSourceRootPath(); 141 if (file != null) { 142 ret = new File(file); 143 } 144 145 return ret; 146 } 147 148 /** 149 * Specify the source root 150 * @param sourceRoot the location of the sources 151 */ 152 public void setSourceRoot(String sourceRoot) { 153 threadConfig.get().setSourceRoot(getCanonicalPath(sourceRoot)); 154 } 155 156 /** 157 * Do we have projects? 158 * @return true if we have projects 159 */ 160 public boolean hasProjects() { 161 List<Project> proj = getProjects(); 162 return (proj != null && !proj.isEmpty()); 163 } 164 165 /** 166 * Get all of the projects 167 * @return a list containing all of the projects (may be null) 168 */ 169 public List<Project> getProjects() { 170 return threadConfig.get().getProjects(); 171 } 172 173 /** 174 * Set the list of the projects 175 * @param projects the list of projects to use 176 */ 177 public void setProjects(List<Project> projects) { 178 threadConfig.get().setProjects(projects); 179 } 180 181 /** 182 * Register this thread in the thread/configuration map (so that all 183 * subsequent calls to the RuntimeEnvironment from this thread will use 184 * the same configuration 185 */ 186 public void register() { 187 threadConfig.set(configuration); 188 } 189 190 /** 191 * Get the context name of the web application 192 * @return the web applications context name 193 */ 194 public String getUrlPrefix() { 195 return threadConfig.get().getUrlPrefix(); 196 } 197 198 /** 199 * Set the web context name 200 * @param urlPrefix the web applications context name 201 */ 202 public void setUrlPrefix(String urlPrefix) { 203 threadConfig.get().setUrlPrefix(urlPrefix); 204 } 205 206 /** 207 * Get the name of the ctags program in use 208 * @return the name of the ctags program in use 209 */ 210 public String getCtags() { 211 return threadConfig.get().getCtags(); 212 } 213 214 /** 215 * Specify the CTags program to use 216 * @param ctags the ctags program to use 217 */ 218 public void setCtags(String ctags) { 219 threadConfig.get().setCtags(ctags); 220 } 221 222 /** 223 * Validate that I have a Exuberant ctags program I may use 224 * @return true if success, false otherwise 225 */ 226 public boolean validateExuberantCtags() { 227 boolean ret = true; 228 Executor executor = new Executor(new String[] {getCtags(), "--version"}); 229 230 executor.exec(false); 231 String output = executor.getOutputString(); 232 if (output == null || output.indexOf("Exuberant Ctags") == -1) { 233 log.severe("Error: No Exuberant Ctags found in PATH!\n" + 234 "(tried running " + getCtags() + ")\n" + 235 "Please use option -c to specify path to a good Exuberant Ctags program"); 236 ret = false; 237 } 238 239 return ret; 240 } 241 242 /** 243 * Get the max time a SMC operation may use to avoid beeing cached 244 * @return the max time 245 */ 246 public int getHistoryReaderTimeLimit() { 247 return threadConfig.get().getHistoryCacheTime(); 248 } 249 250 /** 251 * Specify the maximum time a SCM operation should take before it will 252 * be cached (in ms) 253 * @param historyReaderTimeLimit the max time in ms before it is cached 254 */ 255 public void setHistoryReaderTimeLimit(int historyReaderTimeLimit) { 256 threadConfig.get().setHistoryCacheTime(historyReaderTimeLimit); 257 } 258 259 /** 260 * Is history cache currently enabled? 261 * @return true if history cache is enabled 262 */ 263 public boolean useHistoryCache() { 264 return threadConfig.get().isHistoryCache(); 265 } 266 267 /** 268 * Specify if we should use history cache or not 269 * @param useHistoryCache set false if you do not want to use history cache 270 */ 271 public void setUseHistoryCache(boolean useHistoryCache) { 272 threadConfig.get().setHistoryCache(useHistoryCache); 273 } 274 275 /** 276 * Should we generate HTML or not during the indexing phase 277 * @return true if HTML should be generated during the indexing phase 278 */ 279 public boolean isGenerateHtml() { 280 return threadConfig.get().isGenerateHtml(); 281 } 282 283 /** 284 * Specify if we should generate HTML or not during the indexing phase 285 * @param generateHtml set this to true to pregenerate HTML 286 */ 287 public void setGenerateHtml(boolean generateHtml) { 288 threadConfig.get().setGenerateHtml(generateHtml); 289 } 290 291 /** 292 * Set if we should compress the xref files or not 293 * @param compressXref set to true if the generated html files should be 294 * compressed 295 */ 296 public void setCompressXref(boolean compressXref) { 297 threadConfig.get().setCompressXref(compressXref); 298 } 299 300 /** 301 * Are we using copressed HTML files? 302 * @return true if the html-files should be compressed. false otherwise 303 */ 304 public boolean isCompressXref() { 305 return threadConfig.get().isCompressXref(); 306 } 307 308 public boolean isQuickContextScan() { 309 return threadConfig.get().isQuickContextScan(); 310 } 311 312 public void setQuickContextScan(boolean quickContextScan) { 313 threadConfig.get().setQuickContextScan(quickContextScan); 314 } 315 316 /** 317 * Get the map of external SCM repositories available 318 * @return A map containing all available SCMs 319 */ 320 public Map<String, Repository> getRepositories() { 321 return threadConfig.get().getRepositories(); 322 } 323 324 /** 325 * Set the map of external SCM repositories 326 * @param repositories the repositories to use 327 */ 328 public void setRepositories(Map<String, Repository> repositories) { 329 threadConfig.get().setRepositories(repositories); 330 } 331 332 /** 333 * Set the project that is specified to be the default project to use. The 334 * default project is the project you will search (from the web application) 335 * if the page request didn't contain the cookie.. 336 * @param defaultProject The default project to use 337 */ 338 public void setDefaultProject(Project defaultProject) { 339 threadConfig.get().setDefaultProject(defaultProject); 340 } 341 342 /** 343 * Get the project that is specified to be the default project to use. The 344 * default project is the project you will search (from the web application) 345 * if the page request didn't contain the cookie.. 346 * @return the default project (may be null if not specified) 347 */ 348 public Project getDefaultProject() { 349 return threadConfig.get().getDefaultProject(); 350 } 351 352 /** 353 * Chandan wrote the following answer on the opengrok-discuss list: 354 * "Traditionally search engines (specially spiders) think that large files 355 * are junk. Large files tend to be multimedia files etc., which text 356 * search spiders do not want to chew. So they ignore the contents of 357 * the file after a cutoff length. Lucene does this by number of words, 358 * which is by default is 10,000." 359 * By default OpenGrok will increase this limit to 60000, but it may be 360 * overridden in the configuration file 361 * @return The maximum words to index 362 */ 363 public int getIndexWordLimit() { 364 return threadConfig.get().getIndexWordLimit(); 365 } 366 367 /** 368 * Set the number of words in a file Lucene will index. 369 * See getIndexWordLimit for a better description. 370 * @param indexWordLimit the number of words to index in a single file 371 */ 372 public void setIndexWordLimit(int indexWordLimit) { 373 threadConfig.get().setIndexWordLimit(indexWordLimit); 374 } 375 376 /** 377 * Is the verbosity flag turned on? 378 * @return true if we can print extra information 379 */ 380 public boolean isVerbose() { 381 return threadConfig.get().isVerbose(); 382 } 383 384 /** 385 * Set the verbosity flag (to add extra debug information in output) 386 * @param verbose new value 387 */ 388 public void setVerbose(boolean verbose) { 389 threadConfig.get().setVerbose(verbose); 390 } 391 392 /** 393 * Specify if a search may start with a wildcard. Note that queries 394 * that start with a wildcard will give a significant impact on the 395 * search performace. 396 * @param allowLeadingWildcard set to true to activate (disabled by default) 397 */ 398 public void setAllowLeadingWildcard(boolean allowLeadingWildcard) { 399 threadConfig.get().setAllowLeadingWildcard(allowLeadingWildcard); 400 } 401 402 /** 403 * Is leading wildcards allowed? 404 * @return true if a search may start with a wildcard 405 */ 406 public boolean isAllowLeadingWildcard() { 407 return threadConfig.get().isAllowLeadingWildcard(); 408 } 409 410 public IgnoredNames getIgnoredNames() { 411 return threadConfig.get().getIgnoredNames(); 412 } 413 414 public void setIgnoredNames(IgnoredNames ignoredNames) { 415 threadConfig.get().setIgnoredNames(ignoredNames); 416 } 417 418 /** 419 * Returns the user page for the history listing 420 * @return the URL string fragment preceeding the username 421 */ 422 public String getUserPage() { 423 return threadConfig.get().getUserPage(); 424 } 425 426 /** 427 * Sets the user page for the history listing 428 * @param userPage the URL fragment preceeding the username from history 429 */ 430 public void setUserPage(String userPage) { 431 threadConfig.get().setUserPage(userPage); 432 } 433 434 /** 435 * Returns the bug page for the history listing 436 * @return the URL string fragment preceeding the bug ID 437 */ 438 public String getBugPage() { 439 return threadConfig.get().getBugPage(); 440 } 441 442 /** 443 * Sets the bug page for the history listing 444 * @param bugPage the URL fragment preceeding the bug ID 445 */ 446 public void setBugPage(String bugPage) { 447 threadConfig.get().setBugPage(bugPage); 448 } 449 450 /** 451 * Returns the bug regex for the history listing 452 * @return the regex that is looked for in history comments 453 */ 454 public String getBugPattern() { 455 return threadConfig.get().getBugPattern(); 456 } 457 458 /** 459 * Sets the bug regex for the history listing 460 * @param bugPattern the regex to search history comments 461 */ 462 public void setBugPattern(String bugPattern) { 463 threadConfig.get().setBugPattern(bugPattern); 464 } 465 466 467 /** 468 * Returns the review(ARC) page for the history listing 469 * @return the URL string fragment preceeding the review page ID 470 */ 471 public String getReviewPage() { 472 return threadConfig.get().getReviewPage(); 473 } 474 475 /** 476 * Sets the review(ARC) page for the history listing 477 * @param reviewPage the URL fragment preceeding the review page ID 478 */ 479 public void setReviewPage(String reviewPage) { 480 threadConfig.get().setReviewPage(reviewPage); 481 } 482 483 /** 484 * Returns the review(ARC) regex for the history listing 485 * @return the regex that is looked for in history comments 486 */ 487 public String getReviewPattern() { 488 return threadConfig.get().getReviewPattern(); 489 } 490 491 /** 492 * Sets the review(ARC) regex for the history listing 493 * @param reviewPattern the regex to search history comments 494 */ 495 public void setReviewPattern(String reviewPattern) { 496 threadConfig.get().setReviewPattern(reviewPattern); 497 } 498 499 public String getWebappLAF() { 500 return threadConfig.get().getWebappLAF(); 501 } 502 503 public void setWebappLAF(String laf) { 504 threadConfig.get().setWebappLAF(laf); 505 } 506 507 public boolean isRemoteScmSupported() { 508 return threadConfig.get().isRemoteScmSupported(); 509 } 510 511 public void setRemoteScmSupported(boolean supported) { 512 threadConfig.get().setRemoteScmSupported(supported); 513 } 514 515 public boolean isOptimizeDatabase() { 516 return threadConfig.get().isOptimizeDatabase(); 517 } 518 519 public void setOptimizeDatabase(boolean optimizeDatabase) { 520 threadConfig.get().setOptimizeDatabase(optimizeDatabase); 521 } 522 523 public boolean isUsingLuceneLocking() { 524 return threadConfig.get().isUsingLuceneLocking(); 525 } 526 527 public void setUsingLuceneLocking(boolean useLuceneLocking) { 528 threadConfig.get().setUsingLuceneLocking(useLuceneLocking); 529 } 530 531 public boolean isIndexVersionedFilesOnly() { 532 return threadConfig.get().isIndexVersionedFilesOnly(); 533 } 534 535 public void setIndexVersionedFilesOnly(boolean indexVersionedFilesOnly) { 536 threadConfig.get().setIndexVersionedFilesOnly(indexVersionedFilesOnly); 537 } 538 539 /** 540 * Read an configuration file and set it as the current configuration. 541 * @param file the file to read 542 * @throws IOException if an error occurs 543 */ 544 public void readConfiguration(File file) throws IOException { 545 configuration = Configuration.read(file); 546 register(); 547 } 548 549 /** 550 * Write the current configuration to a file 551 * @param file the file to write the configuration into 552 * @throws IOException if an error occurs 553 */ 554 public void writeConfiguration(File file) throws IOException { 555 threadConfig.get().write(file); 556 } 557 558 /** 559 * Write the current configuration to a socket 560 * @param host the host address to receive the configuration 561 * @param port the port to use on the host 562 * @throws IOException if an error occurs 563 */ 564 public void writeConfiguration(InetAddress host, int port) throws IOException { 565 Socket sock = new Socket(host, port); 566 XMLEncoder e = new XMLEncoder(sock.getOutputStream()); 567 e.writeObject(threadConfig.get()); 568 e.close(); 569 try { 570 sock.close(); 571 } catch (Exception ex) { 572 log.log(Level.INFO, "Couldn't close socket after writing configuration.", ex); 573 } 574 } 575 576 protected void writeConfiguration() throws IOException { 577 writeConfiguration(configServerSocket.getInetAddress(), configServerSocket.getLocalPort()); 578 } 579 580 public void setConfiguration(Configuration configuration) { 581 this.configuration = configuration; 582 register(); 583 } 584 585 private ServerSocket configServerSocket; 586 587 /** 588 * Try to stop the configuration listener thread 589 */ 590 public void stopConfigurationListenerThread() { 591 try { 592 configServerSocket.close(); 593 } catch (Exception e) { log.log(Level.FINE, "Stopping config listener thread: ", e); } 594 } 595 596 /** 597 * Start a thread to listen on a socket to receive new configurations 598 * to use. 599 * @param endpoint The socket address to listen on 600 * @return true if the endpoint was available (and the thread was started) 601 */ 602 public boolean startConfigurationListenerThread(SocketAddress endpoint) { 603 boolean ret = false; 604 605 try { 606 configServerSocket = new ServerSocket(); 607 configServerSocket.bind(endpoint); 608 ret = true; 609 final ServerSocket sock = configServerSocket; 610 Thread t = new Thread(new Runnable() { 611 public void run() { 612 while (!sock.isClosed()) { 613 Socket s = null; 614 try { 615 s = sock.accept(); 616 log.info(" OpenGrok: Got request from " + s.getInetAddress().getHostAddress()); 617 BufferedInputStream in = new BufferedInputStream(s.getInputStream()); 618 619 XMLDecoder d = new XMLDecoder(new BufferedInputStream(in)); 620 Object obj = d.readObject(); 621 d.close(); 622 623 if (obj instanceof Configuration) { 624 configuration = (Configuration)obj; 625 log.info("Configuration updated: " + configuration.getSourceRoot()); 626 } 627 } catch (IOException e) { 628 log.log(Level.WARNING, "Error reading config file: ",e); 629 } finally { 630 if (s != null) { 631 try { 632 s.close(); 633 } catch (IOException ex) { 634 log.log(Level.WARNING, "Interrupt closing config listener reader socket: ", ex); 635 } 636 } 637 } 638 } 639 } 640 }); 641 t.start(); 642 } catch (UnknownHostException ex) { 643 log.log(Level.FINE,"Problem resolving sender: ",ex); 644 } catch (IOException ex) { 645 log.log(Level.FINE,"I/O error when waiting for config: ",ex); 646 } 647 648 if (!ret && configServerSocket != null) { 649 try { 650 configServerSocket.close(); 651 } catch (IOException ex) { 652 log.log(Level.FINE,"I/O problem closing reader config socket: ",ex); 653 } 654 } 655 656 return ret; 657 } 658 } 659