RuntimeEnvironment.java revision 1467
70N/A/*
70N/A * CDDL HEADER START
70N/A *
70N/A * The contents of this file are subject to the terms of the
851N/A * Common Development and Distribution License (the "License").
851N/A * You may not use this file except in compliance with the License.
70N/A *
70N/A * See LICENSE.txt included in this distribution for the specific
70N/A * language governing permissions and limitations under the License.
70N/A *
70N/A * When distributing Covered Code, include this CDDL HEADER in each
70N/A * file and include the License file at LICENSE.txt.
70N/A * If applicable, add the following below this CDDL HEADER, with the
70N/A * fields enclosed by brackets "[]" replaced with your own identifying
70N/A * information: Portions Copyright [yyyy] [name of copyright owner]
70N/A *
70N/A * CDDL HEADER END
70N/A */
70N/A
70N/A/*
70N/A * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
70N/A */
70N/Apackage org.opensolaris.opengrok.configuration;
70N/A
70N/Aimport java.beans.XMLDecoder;
70N/Aimport java.beans.XMLEncoder;
70N/Aimport java.io.BufferedInputStream;
70N/Aimport java.io.ByteArrayInputStream;
70N/Aimport java.io.ByteArrayOutputStream;
70N/Aimport java.io.File;
70N/Aimport java.io.FileNotFoundException;
70N/Aimport java.io.IOException;
851N/Aimport java.net.InetAddress;
70N/Aimport java.net.ServerSocket;
70N/Aimport java.net.Socket;
70N/Aimport java.net.SocketAddress;
493N/Aimport java.net.UnknownHostException;
70N/Aimport java.util.Date;
70N/Aimport java.util.List;
851N/Aimport java.util.Set;
70N/Aimport java.util.logging.Level;
70N/Aimport java.util.logging.Logger;
70N/A
70N/Aimport org.opensolaris.opengrok.history.HistoryGuru;
70N/Aimport org.opensolaris.opengrok.history.RepositoryInfo;
70N/Aimport org.opensolaris.opengrok.index.Filter;
493N/Aimport org.opensolaris.opengrok.index.IgnoredNames;
493N/Aimport org.opensolaris.opengrok.util.Executor;
70N/Aimport org.opensolaris.opengrok.util.IOUtils;
70N/A
/**
* The RuntimeEnvironment class is used as a placeholder for the current
* configuration this execution context (classloader) is using.
*/
public final class RuntimeEnvironment {
private Configuration configuration;
private final ThreadLocal<Configuration> threadConfig;
private static final Logger log = Logger.getLogger(RuntimeEnvironment.class.getName());
private static RuntimeEnvironment instance = new RuntimeEnvironment();
private static Boolean oldVM;
/**
* Get the one and only instance of the RuntimeEnvironment
* @return the one and only instance of the RuntimeEnvironment
*/
public static RuntimeEnvironment getInstance() {
return instance;
}
/**
* Creates a new instance of RuntimeEnvironment. Private to ensure a
* singleton pattern.
*/
private RuntimeEnvironment() {
configuration = new Configuration();
threadConfig = new ThreadLocal<Configuration>() {
@SuppressWarnings("synthetic-access")
@Override protected Configuration initialValue() {
return configuration;
}
};
}
/**
* Makes testing easier by being able to drop the current env and replace it
* with a virgin one.
*/
RuntimeEnvironment resetForTest() {
threadConfig.remove();
instance = new RuntimeEnvironment();
return instance;
}
/**
* Check, whether the running JVM is older than version 1.7 .
* @return {@code true} if older than 1.7
*/
public static final boolean isOldJVM() {
if (oldVM == null) {
String[] s = System.getProperty("java.specification.version", "1.6")
.split("\\.");
int minor = 0;
int major = 0;
try {
major = Integer.parseInt(s[0], 10);
minor = Integer.parseInt(s[1], 10);
} catch (Exception e) {
// ignore
}
oldVM = Boolean.valueOf(major <= 1 && minor < 7);
}
return oldVM.booleanValue();
}
private static String getCanonicalPath(String s) {
try {
File file = new File(s);
if (!file.exists()) {
return s;
}
return file.getCanonicalPath();
} catch (IOException ex) {
log.warning("Failed to get canonical path for '" + s + "': "
+ ex.getMessage());
log.log(Level.FINE, "getCanonicalPath", ex);
return s;
}
}
/**
* @see Configuration#getScanningDepth()
* @return the current scan depth
*/
public int getScanningDepth() {
return threadConfig.get().getScanningDepth();
}
/**
* @see Configuration#setScanningDepth(int)
* @param scanningDepth the scan depth to use.
*/
public void setScanningDepth(int scanningDepth) {
threadConfig.get().setScanningDepth(scanningDepth);
}
/**
* Get the path to the where the index database is stored
* @return the path to the index database
*/
public String getDataRootPath() {
return threadConfig.get().getDataRoot();
}
/**
* Get a file representing the index database
* @return the index database
*/
public File getDataRootFile() {
File ret = null;
String file = getDataRootPath();
if (file != null) {
ret = new File(file);
}
return ret;
}
/**
* Set the path to where the index database is stored
* @param dataRoot the index database
*/
public void setDataRoot(String dataRoot) {
threadConfig.get().setDataRoot(getCanonicalPath(dataRoot));
}
/**
* Get the path to where the sources are located
* @return path to where the sources are located
*/
public String getSourceRootPath() {
return threadConfig.get().getSourceRoot();
}
/**
* Get a file representing the directory where the sources are located
* @return A file representing the directory where the sources are located
*/
public File getSourceRootFile() {
String file = getSourceRootPath();
return (file != null) ? new File(file) : null;
}
/**
* Specify the source root
* @param sourceRoot the location of the sources
*/
public void setSourceRoot(String sourceRoot) {
threadConfig.get().setSourceRoot(getCanonicalPath(sourceRoot));
}
/**
* Returns a path relative to source root. This would just be a simple
* substring operation, except we need to support symlinks outside the
* source root.
* @param file A file to resolve
* @param stripCount Number of characters past source root to strip
* @throws IOException If an IO error occurs
* @throws FileNotFoundException If the file is not relative to source root
* @return Path relative to source root
*/
public String getPathRelativeToSourceRoot(File file, int stripCount) throws IOException {
String canonicalPath = file.getCanonicalPath();
String sourceRoot = getSourceRootPath();
if (canonicalPath.startsWith(sourceRoot)) {
return canonicalPath.substring(sourceRoot.length() + stripCount);
}
for (String allowedSymlink : getAllowedSymlinks()) {
String allowedTarget = new File(allowedSymlink).getCanonicalPath();
if (canonicalPath.startsWith(allowedTarget)) {
return canonicalPath.substring(allowedTarget.length() + stripCount);
}
}
throw new FileNotFoundException("Failed to resolve '" + canonicalPath
+ "' relative to source root '" + sourceRoot + "'");
}
/**
* Do we have projects?
* @return true if we have projects
*/
public boolean hasProjects() {
List<Project> proj = getProjects();
return (proj != null && !proj.isEmpty());
}
/**
* Get all of the projects
* @return a list containing all of the projects (may be null)
*/
public List<Project> getProjects() {
return threadConfig.get().getProjects();
}
/**
* Set the list of the projects
* @param projects the list of projects to use
*/
public void setProjects(List<Project> projects) {
threadConfig.get().setProjects(projects);
}
/**
* Register this thread in the thread/configuration map (so that all
* subsequent calls to the RuntimeEnvironment from this thread will use
* the same configuration
* @return this instance
*/
public RuntimeEnvironment register() {
threadConfig.set(configuration);
return this;
}
/**
* Get the context name of the web application.
* @return the web applications context name.
*/
public String getUrlPrefix() {
return threadConfig.get().getUrlPrefix();
}
/**
* Set the web context name
* @param urlPrefix the web applications context name
*/
public void setUrlPrefix(String urlPrefix) {
threadConfig.get().setUrlPrefix(urlPrefix);
}
/**
* Get the name of the ctags program in use
* @return the name of the ctags program in use
*/
public String getCtags() {
return threadConfig.get().getCtags();
}
/**
* Specify the CTags program to use
* @param ctags the ctags program to use
*/
public void setCtags(String ctags) {
threadConfig.get().setCtags(ctags);
}
/**
* Get the number of search result pages, which should be cached in
* a search result.
* @return the number of result pages to cache.
* @see #getHitsPerPage()
*/
public int getCachePages() {
return threadConfig.get().getCachePages();
}
/**
* Set the number of search result pages, which should be cached in
* a search result.
* @param cachePages the number of result pages to cache.
* @see #getHitsPerPage()
*/
public void setCachePages(int cachePages) {
threadConfig.get().setCachePages(cachePages);
}
/**
* Get the number of hits to display on one search result page.
* @return number of hits per page.
*/
public int getHitsPerPage() {
return threadConfig.get().getHitsPerPage();
}
/**
* Set the number of hits to display on one search result page.
* @param hitsPerPage number of hits per page to set.
*/
public void setHitsPerPage(int hitsPerPage) {
threadConfig.get().setHitsPerPage(hitsPerPage);
}
/**
* Validate that I have a Exuberant ctags program I may use
* @return true if success, false otherwise
*/
public boolean validateExuberantCtags() {
boolean ret = true;
Executor executor = new Executor(new String[] {getCtags(), "--version"});
executor.exec(false);
String output = executor.getOutputString();
if (output == null || output.indexOf("Exuberant Ctags") == -1) {
log.warning("No Exuberant Ctags found in PATH!\n" +
"(tried running '" + getCtags() + "')\n" +
"Please use option -c to specify path to a good Exuberant Ctags program\n"+
"Or set it in java system property "
+ Configuration.CTAGS_CMD_PROPERTY_KEY);
ret = false;
}
return ret;
}
/**
* Get the max time a SMC operation may use to avoid beeing cached
* @return the max time
*/
public int getHistoryReaderTimeLimit() {
return threadConfig.get().getHistoryCacheTime();
}
/**
* Specify the maximum time a SCM operation should take before it will
* be cached (in ms)
* @param historyReaderTimeLimit the max time in ms before it is cached
*/
public void setHistoryReaderTimeLimit(int historyReaderTimeLimit) {
threadConfig.get().setHistoryCacheTime(historyReaderTimeLimit);
}
/**
* Is history cache currently enabled?
* @return true if history cache is enabled
*/
public boolean useHistoryCache() {
return threadConfig.get().isHistoryCache();
}
/**
* Specify if we should use history cache or not
* @param useHistoryCache set false if you do not want to use history cache
*/
public void setUseHistoryCache(boolean useHistoryCache) {
threadConfig.get().setHistoryCache(useHistoryCache);
}
/**
* Should the history cache be stored in a database instead of in XML
* files?
*
* @return {@code true} if the cache should be stored in a database
*/
public boolean storeHistoryCacheInDB() {
return threadConfig.get().isHistoryCacheInDB();
}
/**
* Set whether the history cache should be stored in a database.
* @param store {@code true} if the cache should be stored in a database
*/
public void setStoreHistoryCacheInDB(boolean store) {
threadConfig.get().setHistoryCacheInDB(store);
}
/**
* Should we generate HTML or not during the indexing phase
* @return true if HTML should be generated during the indexing phase
*/
public boolean isGenerateHtml() {
return threadConfig.get().isGenerateHtml();
}
/**
* Specify if we should generate HTML or not during the indexing phase
* @param generateHtml set this to true to pregenerate HTML
*/
public void setGenerateHtml(boolean generateHtml) {
threadConfig.get().setGenerateHtml(generateHtml);
}
/**
* Set if we should compress the xref files or not. Applies to newly
* generated/updated files, only. Re-indexing is not needed, since the
* application automatically detects, whether an xref file is compressed
* and processes it properly.
* @param compressXref set to {@code true} if generated xref html files
* should be compressed
*/
public void setCompressXref(boolean compressXref) {
threadConfig.get().setCompressXref(compressXref);
}
/**
* Set if we should compress generated xref html files?
* @return {@code true} if the xref html files should be compressed.
*/
public boolean isCompressXref() {
return threadConfig.get().isCompressXref();
}
/**
* Check whether a quick context scan should be done.
* @return {@code true} if quick scan should be made.
* @see #setQuickContextScan(boolean)
*/
public boolean isQuickContextScan() {
return threadConfig.get().isQuickContextScan();
}
/**
* If set to {@code true}, at most 1 MiB of a file gets read into a buffer
* at once and than tokenized. Otherwise the whole file gets processed as
* usual, i.e. the file content gets read on demand when tokenizing and the
* whole file gets processed (no matter of its size).
* @param quickContextScan if {@code true} enable quick scanning
*/
public void setQuickContextScan(boolean quickContextScan) {
threadConfig.get().setQuickContextScan(quickContextScan);
}
/**
* Get the map of known repositories.
* @return a possible empty list including {@code null}.
*/
public List<RepositoryInfo> getRepositories() {
return threadConfig.get().getRepositories();
}
/**
* Set the map of external SCM repositories
* @param repositories the repositories to use
*/
public void setRepositories(List<RepositoryInfo> repositories) {
threadConfig.get().setRepositories(repositories);
}
/**
* Set the project that is specified to be the default project to use. The
* default project is the project you will search (from the web application)
* if the page request didn't contain the cookie.
* @param defaultProject The default project to use
*/
public void setDefaultProject(Project defaultProject) {
threadConfig.get().setDefaultProject(defaultProject);
}
/**
* Get the project that is specified to be the default project to use. The
* default project is the project you will search (from the web application)
* if the page request didn't contain the cookie..
* @return the default project (may be null if not specified)
*/
public Project getDefaultProject() {
return threadConfig.get().getDefaultProject();
}
/**
* Chandan wrote the following answer on the opengrok-discuss list:
* "Traditionally search engines (specially spiders) think that large files
* are junk. Large files tend to be multimedia files etc., which text
* search spiders do not want to chew. So they ignore the contents of
* the file after a cutoff length. Lucene does this by number of words,
* which is by default is 10,000."
* By default OpenGrok will increase this limit to 60000, but it may be
* overridden in the configuration file
* @return The maximum words to index
*/
public int getIndexWordLimit() {
return threadConfig.get().getIndexWordLimit();
}
/**
* Set the number of words in a file Lucene will index.
* See getIndexWordLimit for a better description.
* @param indexWordLimit the number of words to index in a single file
*/
public void setIndexWordLimit(int indexWordLimit) {
threadConfig.get().setIndexWordLimit(indexWordLimit);
}
/**
* Is the verbosity flag turned on?
* @return true if we can print extra information
*/
public boolean isVerbose() {
return threadConfig.get().isVerbose();
}
/**
* Set the verbosity flag (to add extra debug information in output)
* @param verbose new value
*/
public void setVerbose(boolean verbose) {
threadConfig.get().setVerbose(verbose);
}
/**
* Is the progress print flag turned on?
* @return {@code true} if we can print per project progress %
*/
public boolean isPrintProgress() {
return threadConfig.get().isPrintProgress();
}
/**
* Set the printing of progress % flag (user convenience).
* @param enable {@code true} to enable progress printing.
*/
public void setPrintProgress(boolean enable) {
threadConfig.get().setPrintProgress(enable);
}
/**
* Specify if a search may start with a wildcard. Note that queries
* that start with a wildcard will give a significant impact on the
* search performace.
* @param allowLeadingWildcard set to true to activate (disabled by default)
*/
public void setAllowLeadingWildcard(boolean allowLeadingWildcard) {
threadConfig.get().setAllowLeadingWildcard(allowLeadingWildcard);
}
/**
* Is leading wildcards allowed?
* @return true if a search may start with a wildcard
*/
public boolean isAllowLeadingWildcard() {
return threadConfig.get().isAllowLeadingWildcard();
}
/**
* Get the list of names/patterns to determine file, which should be
* ignored/excluded from indexing.
* Takes precedence over {@link #getIncludedNames()}.
* @return stuff to ignore, which might be {@code null}.
*/
public IgnoredNames getIgnoredNames() {
return threadConfig.get().getIgnoredNames();
}
/**
* Set the list of names/patterns to determine file, which should be
* ignored/excluded from indexing.
* Takes precedence over {@link #getIncludedNames()}.
* @param ignoredNames stuff to ignore.
*/
public void setIgnoredNames(IgnoredNames ignoredNames) {
threadConfig.get().setIgnoredNames(ignoredNames);
}
/**
* Get the list of names/patterns to determine, which are allowed to
* be indexed.
* @return stuff to index. {@code null} means everything.
* @see #getIgnoredNames()
*/
public Filter getIncludedNames() {
return threadConfig.get().getIncludedNames();
}
/**
* Set the list of names/patterns to determine, which are allowed to
* be indexed.
* @param includedNames stuff to index. {@code null} means everything.
* @see #setIgnoredNames(IgnoredNames)
*/
public void setIncludedNames(Filter includedNames) {
threadConfig.get().setIncludedNames(includedNames);
}
/**
* Returns the user page for the history listing
* @return the URL string fragment preceeding the username
*/
public String getUserPage() {
return threadConfig.get().getUserPage();
}
/**
* Get the client command to use to access the repository for the given
* fully quallified classname.
* @param clazzName name of the targeting class
* @return {@code null} if not yet set, the client command otherwise.
*/
public String getRepoCmd(String clazzName) {
return threadConfig.get().getRepoCmd(clazzName);
}
/**
* Set the client command to use to access the repository for the given
* fully quallified classname.
* @param clazzName name of the targeting class. If {@code null} this method
* does nothing.
* @param cmd the client command to use. If {@code null} the corresponding
* entry for the given clazzName get removed.
* @return the client command previously set, which might be {@code null}.
*/
public String setRepoCmd(String clazzName, String cmd) {
return threadConfig.get().setRepoCmd(clazzName, cmd);
}
/**
* Sets the user page for the history listing
* @param userPage the URL fragment preceeding the username from history
*/
public void setUserPage(String userPage) {
threadConfig.get().setUserPage(userPage);
}
/**
* Returns the user page suffix for the history listing
* @return the URL string fragment following the username
*/
public String getUserPageSuffix() {
return threadConfig.get().getUserPageSuffix();
}
/**
* Sets the user page suffix for the history listing
* @param userPageSuffix the URL fragment following the username from history
*/
public void setUserPageSuffix(String userPageSuffix) {
threadConfig.get().setUserPageSuffix(userPageSuffix);
}
/**
* Returns the bug page for the history listing
* @return the URL string fragment preceeding the bug ID
*/
public String getBugPage() {
return threadConfig.get().getBugPage();
}
/**
* Sets the bug page for the history listing
* @param bugPage the URL fragment preceeding the bug ID
*/
public void setBugPage(String bugPage) {
threadConfig.get().setBugPage(bugPage);
}
/**
* Returns the bug regex for the history listing
* @return the regex that is looked for in history comments
*/
public String getBugPattern() {
return threadConfig.get().getBugPattern();
}
/**
* Sets the bug regex for the history listing
* @param bugPattern the regex to search history comments
*/
public void setBugPattern(String bugPattern) {
threadConfig.get().setBugPattern(bugPattern);
}
/**
* Returns the review(ARC) page for the history listing
* @return the URL string fragment preceeding the review page ID
*/
public String getReviewPage() {
return threadConfig.get().getReviewPage();
}
/**
* Sets the review(ARC) page for the history listing
* @param reviewPage the URL fragment preceeding the review page ID
*/
public void setReviewPage(String reviewPage) {
threadConfig.get().setReviewPage(reviewPage);
}
/**
* Returns the review(ARC) regex for the history listing
* @return the regex that is looked for in history comments
*/
public String getReviewPattern() {
return threadConfig.get().getReviewPattern();
}
/**
* Sets the review(ARC) regex for the history listing
* @param reviewPattern the regex to search history comments
*/
public void setReviewPattern(String reviewPattern) {
threadConfig.get().setReviewPattern(reviewPattern);
}
/**
* Get the name of the web app Look And Feel (LAF) theme to use. It gets
* used to construct the stylesheet and image include links in web pages
* and names a directory, which contains all this stuff.
* @return the name of the web app LAF
*/
public String getWebappLAF() {
return threadConfig.get().getWebappLAF();
}
/**
* Set the name of the web app Look And Feel (LAF) theme to use. It gets
* used to construct the stylesheet and image include links in web pages
* and names a directory, which contains all this stuff.
* @param laf the name of the web app LAF
*/
public void setWebappLAF(String laf) {
threadConfig.get().setWebappLAF(laf);
}
/**
* Check whether file histories should be retrieved from the related remote
* repositories when needed.
* @return {@code true} if fetch on demand is enabled.
*/
public boolean isRemoteScmSupported() {
return threadConfig.get().isRemoteScmSupported();
}
/**
* Set whether file histories should be retrieved from the related remote
* repositories when needed.
* @param supported {@code true} if fetch on demand is enabled.
*/
public void setRemoteScmSupported(boolean supported) {
threadConfig.get().setRemoteScmSupported(supported);
}
/**
* Check, whether the lucene index database should be optimized (compact
* segments etc.) after indexing has been run.
* @return {@code true} if optimization is enabled.
*/
public boolean isOptimizeDatabase() {
return threadConfig.get().isOptimizeDatabase();
}
/**
* Set, whether the lucene index database should be optimized (compact
* segments etc.) after indexing has been run.
* @param optimizeDatabase {@code true} if optimization should be done.
*/
public void setOptimizeDatabase(boolean optimizeDatabase) {
threadConfig.get().setOptimizeDatabase(optimizeDatabase);
}
/**
* Check, wheter lucene should lock index files when indexing to avoid
* concurrent access, reading invalid data.
* @return {@code true} if locking should be used.
*/
public boolean isUsingLuceneLocking() {
return threadConfig.get().isUsingLuceneLocking();
}
/**
* Set wheter lucene should lock index files when indexing to avoid
* concurrent access, reading invalid data.
* @param useLuceneLocking {@code true} if locking should be used.
*/
public void setUsingLuceneLocking(boolean useLuceneLocking) {
threadConfig.get().setUsingLuceneLocking(useLuceneLocking);
}
/**
* Check, whether unversioned files should not be indexed.
* If {@code true}, it takes precedence over {@link #getIncludedNames()},
* i.e. files are not indexed when unversioned, even if they match an
* include name.
* @return {@code true} if indexing of unversioned files is disabled.
*/
public boolean isIndexVersionedFilesOnly() {
return threadConfig.get().isIndexVersionedFilesOnly();
}
/**
* Set, whether unversioned files should not be indexed.
* If set to {@code true}, it takes precedence over {@link #getIncludedNames()},
* i.e. files are not indexed when unversioned, even if they match an
* include name.
* @param indexVersionedFilesOnly {@code true} to disable indexing of
* unversioned files
*/
public void setIndexVersionedFilesOnly(boolean indexVersionedFilesOnly) {
threadConfig.get().setIndexVersionedFilesOnly(indexVersionedFilesOnly);
}
/**
* Get the date of the last index update.
* @return the time of the last index update.
*/
public Date getDateForLastIndexRun() {
return threadConfig.get().getDateForLastIndexRun();
}
/**
* Get the fully quallified class name of the history database driver to use.
* @return the database driver class name.
*/
public String getDatabaseDriver() {
return threadConfig.get().getDatabaseDriver();
}
/**
* Set the fully quallified class name of the history database driver to use.
* @param databaseDriver the database driver class name to set.
*/
public void setDatabaseDriver(String databaseDriver) {
threadConfig.get().setDatabaseDriver(databaseDriver);
}
/**
* Get the JDBC connection URL to use to connect to the history database.
* @return the JDBC connection URL to use.
*/
public String getDatabaseUrl() {
return threadConfig.get().getDatabaseUrl();
}
/**
* Set the JDBC connection URL to use to connect to the history database.
* @param databaseUrl the JDBC connection URL to set.
*/
public void setDatabaseUrl(String databaseUrl) {
threadConfig.get().setDatabaseUrl(databaseUrl);
}
/**
* Get the optional file to be used to handover additional options to the
* ctags command.
* @return {@code null} if not set, the options filename otherwise.
*/
public String getCtagsOptionsFile() {
return threadConfig.get().getCtagsOptionsFile();
}
/**
* Set the file to be used to handover additional options to the ctags
* command.
* @param filename the options filename to use. Might be {@code null}.
*/
public void setCtagsOptionsFile(String filename) {
threadConfig.get().setCtagsOptionsFile(filename);
}
/**
* Get the pathnames of symlinks, which are allowed to be processed (e.g.
* indexed, tokenized, etc.).
* @return a possible empty set.
*/
public Set<String> getAllowedSymlinks() {
return threadConfig.get().getAllowedSymlinks();
}
/**
* Set the pathnames of symlinks, which are allowed to be processed (e.g.
* indexed, tokenized, etc.).
* @param allowedSymlinks symlinks to allow. {@code null} means none.
*/
public void setAllowedSymlinks(Set<String> allowedSymlinks) {
threadConfig.get().setAllowedSymlinks(allowedSymlinks);
}
/**
* Check whether e-mail addresses should be obfuscated in the xref.
* @return {@code true} if obfuscation is needed.
*/
public boolean isObfuscatingEMailAddresses() {
return threadConfig.get().isObfuscatingEMailAddresses();
}
/**
* Set whether e-mail addresses should be obfuscated in the xref.
* @param obfuscate {@code true} if obfuscation is needed.
*/
public void setObfuscatingEMailAddresses(boolean obfuscate) {
threadConfig.get().setObfuscatingEMailAddresses(obfuscate);
}
/**
* Should status.jsp print internal settings, like paths and database
* URLs?
*
* @return {@code true} if status.jsp should show the configuration.
*/
public boolean isChattyStatusPage() {
return threadConfig.get().isChattyStatusPage();
}
/**
* Set whether status.jsp should print internal settings.
*
* @param chatty {@code true} if internal settings should be printed.
*/
public void setChattyStatusPage(boolean chatty) {
threadConfig.get().setChattyStatusPage(chatty);
}
/**
* Read an configuration file and set it as the current configuration.
* @param file the file to read
* @throws IOException if an error occurs
*/
public void readConfiguration(File file) throws IOException {
setConfiguration(Configuration.read(file));
}
/**
* Write the current configuration to a file
* @param file the file to write the configuration into
* @throws IOException if an error occurs
*/
public void writeConfiguration(File file) throws IOException {
threadConfig.get().write(file);
}
/**
* Write the current configuration to a socket
* @param host the host address to receive the configuration
* @param port the port to use on the host
* @throws IOException if an error occurs
*/
public void writeConfiguration(InetAddress host, int port) throws IOException {
Socket sock = new Socket(host, port);
XMLEncoder e = new XMLEncoder(sock.getOutputStream());
e.writeObject(threadConfig.get());
e.close();
IOUtils.close(sock);
}
/**
* Send the current configuration to the web application [server].
* @throws IOException
*/
protected void writeConfiguration() throws IOException {
writeConfiguration(configServerSocket.getInetAddress(),
configServerSocket.getLocalPort());
}
/**
* Set the current configuration for this instance to the given parameter.
* Involves invalidating known repositories of the {@link HistoryGuru}.
* @param configuration configuration to set.
*/
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
register();
HistoryGuru.getInstance()
.invalidateRepositories(configuration.getRepositories());
}
/**
* Get the current configuration for this instance.
* @return the current configuration.
*/
public Configuration getConfiguration() {
return this.threadConfig.get();
}
private ServerSocket configServerSocket;
/**
* Try to stop the configuration listener thread
*/
public void stopConfigurationListenerThread() {
IOUtils.close(configServerSocket);
}
/**
* Start a thread to listen on a socket to receive new configurations
* to use.
* @param endpoint The socket address to listen on
* @return true if the endpoint was available (and the thread was started)
*/
public boolean startConfigurationListenerThread(SocketAddress endpoint) {
boolean ret = false;
try {
configServerSocket = new ServerSocket();
configServerSocket.bind(endpoint);
ret = true;
final ServerSocket sock = configServerSocket;
Thread t = new Thread(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
ByteArrayOutputStream bos = new ByteArrayOutputStream(1<<13);
while (!sock.isClosed()) {
Socket s = null;
BufferedInputStream in = null;
try {
s = sock.accept();
bos.reset();
log.info("Re-configure request from " +
s.getInetAddress().getHostAddress());
in = new BufferedInputStream(s.getInputStream());
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
bos.write(buf, 0, len);
}
buf = bos.toByteArray();
if (log.isLoggable(Level.FINE)) {
log.fine("New config: \n" + new String(buf));
}
XMLDecoder d = new XMLDecoder(new ByteArrayInputStream(buf));
Object obj = d.readObject();
d.close();
if (obj instanceof Configuration) {
setConfiguration((Configuration)obj);
log.log(Level.INFO, "Configuration updated: {0}", configuration.getSourceRoot());
}
} catch (IOException e) {
log.warning("Error reading config file: " + e.getMessage());
log.log(Level.FINE, "run", e);
} catch (RuntimeException e) {
log.warning("Error parsing config file: " + e.getMessage());
log.log(Level.FINE, "run", e);
} finally {
IOUtils.close(s);
IOUtils.close(in);
}
}
}
});
t.start();
} catch (UnknownHostException ex) {
log.log(Level.FINE,"Problem resolving sender", ex);
} catch (IOException ex) {
log.log(Level.FINE,"I/O error when waiting for config", ex);
}
if (!ret && configServerSocket != null) {
IOUtils.close(configServerSocket);
}
return ret;
}
}