Merge branch 'upmaster' into ruby-bundler. Fixed omission of --disableBundleAudit option.

Conflicts:
	dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java
This commit is contained in:
Dale Visser
2015-09-09 18:09:41 -04:00
40 changed files with 361 additions and 887 deletions

View File

@@ -352,6 +352,7 @@ public class Engine implements FileFilter {
LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
LOGGER.info("Analysis Starting");
final long analysisStart = System.currentTimeMillis();
// analysis phases
for (AnalysisPhase phase : AnalysisPhase.values()) {
@@ -365,8 +366,7 @@ public class Engine implements FileFilter {
* This is okay for adds/deletes because it happens per analyzer.
*/
LOGGER.debug("Begin Analyzer '{}'", a.getName());
final Set<Dependency> dependencySet = new HashSet<Dependency>();
dependencySet.addAll(dependencies);
final Set<Dependency> dependencySet = new HashSet<Dependency>(dependencies);
for (Dependency d : dependencySet) {
boolean shouldAnalyze = true;
if (a instanceof FileTypeAnalyzer) {
@@ -398,7 +398,7 @@ public class Engine implements FileFilter {
}
LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
LOGGER.info("Analysis Complete");
LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart);
}
/**
@@ -442,6 +442,7 @@ public class Engine implements FileFilter {
*/
public void doUpdates() {
LOGGER.info("Checking for updates");
final long updateStart = System.currentTimeMillis();
final UpdateService service = new UpdateService(serviceClassLoader);
final Iterator<CachedWebDataSource> iterator = service.getDataSources();
while (iterator.hasNext()) {
@@ -454,7 +455,7 @@ public class Engine implements FileFilter {
LOGGER.debug("Unable to update details for {}", source.getClass().getName(), ex);
}
}
LOGGER.info("Check for updates complete");
LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart);
}
/**

View File

@@ -89,16 +89,16 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*/
private static final Set<String> ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg");
/**
* The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need
* to be explicitly handled in {@link #extractFiles(File, File, Engine)}.
* The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need to be
* explicitly handled in {@link #extractFiles(File, File, Engine)}.
*/
private static final Set<String> EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2");
/**
* Detects files with extensions to remove from the engine's collection of dependencies.
*/
private static final FileFilter REMOVE_FROM_ANALYSIS =
FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2").build();
private static final FileFilter REMOVE_FROM_ANALYSIS
= FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2").build();
static {
final String additionalZipExt = Settings.getString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS);
@@ -184,7 +184,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
if (tempFileLocation != null && tempFileLocation.exists()) {
LOGGER.debug("Attempting to delete temporary files");
final boolean success = FileUtils.delete(tempFileLocation);
if (!success && tempFileLocation != null && tempFileLocation.exists() && tempFileLocation.list().length > 0) {
if (!success && tempFileLocation.exists() && tempFileLocation.list().length > 0) {
LOGGER.warn("Failed to delete some temporary files, see the log for more details");
}
}
@@ -195,7 +195,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
* and added to the list of dependencies within the engine.
*
* @param dependency the dependency to analyze
* @param engine the engine scanning
* @param engine the engine scanning
* @throws AnalysisException thrown if there is an analysis exception
*/
@Override
@@ -239,7 +239,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
final File tdir = getNextTempDirectory();
final String fileName = dependency.getFileName();
LOGGER.info(String.format("The zip file '%s' appears to be a JAR file, making a copy and analyzing it as a JAR.", fileName));
LOGGER.info("The zip file '{}' appears to be a JAR file, making a copy and analyzing it as a JAR.", fileName);
final File tmpLoc = new File(tdir, fileName.substring(0, fileName.length() - 3) + "jar");
try {
@@ -271,15 +271,14 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
* @return any dependencies that weren't known to the engine before
*/
private static Set<Dependency> findMoreDependencies(Engine engine, File file) {
List<Dependency> before = new ArrayList<Dependency>(engine.getDependencies());
final List<Dependency> before = new ArrayList<Dependency>(engine.getDependencies());
engine.scan(file);
List<Dependency> after = engine.getDependencies();
final List<Dependency> after = engine.getDependencies();
final boolean sizeChanged = before.size() != after.size();
final Set<Dependency> newDependencies;
if (sizeChanged) {
//get the new dependencies
newDependencies = new HashSet<Dependency>();
newDependencies.addAll(after);
newDependencies = new HashSet<Dependency>(after);
newDependencies.removeAll(before);
} else {
newDependencies = EMPTY_DEPENDENCY_SET;
@@ -287,7 +286,6 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
return newDependencies;
}
/**
* Retrieves the next temporary directory to extract an archive too.
*
@@ -311,9 +309,9 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Extracts the contents of an archive into the specified directory.
*
* @param archive an archive file such as a WAR or EAR
* @param archive an archive file such as a WAR or EAR
* @param destination a directory to extract the contents to
* @param engine the scanning engine
* @param engine the scanning engine
* @throws AnalysisException thrown if the archive is not found
*/
private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException {
@@ -359,9 +357,9 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Extracts files from an archive.
*
* @param input the archive to extract files from
* @param input the archive to extract files from
* @param destination the location to write the files too
* @param engine the dependency-check engine
* @param engine the dependency-check engine
* @throws ArchiveExtractionException thrown if there is an exception extracting files from the archive
*/
private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException {
@@ -423,7 +421,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
* Decompresses a file.
*
* @param inputStream the compressed file
* @param outputFile the location to write the decompressed file
* @param outputFile the location to write the decompressed file
* @throws ArchiveExtractionException thrown if there is an exception decompressing the file
*/
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
@@ -452,7 +450,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
*
* @param closeable to be closed
*/
private static void close(Closeable closeable){
private static void close(Closeable closeable) {
if (null != closeable) {
try {
closeable.close();

View File

@@ -17,8 +17,6 @@
*/
package org.owasp.dependencycheck.analyzer;
import ch.qos.cal10n.IMessageConveyor;
import ch.qos.cal10n.MessageConveyor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
@@ -45,7 +43,6 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Analyzer for getting company, product, and version information from a .NET assembly.
@@ -75,10 +72,6 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
* The DocumentBuilder for parsing the XML
*/
private DocumentBuilder builder;
/**
* Message Conveyer
*/
private static final IMessageConveyor MESSAGE_CONVERYOR = new MessageConveyor(Locale.getDefault());
/**
* Logger
*/

View File

@@ -18,7 +18,7 @@
package org.owasp.dependencycheck.analyzer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence;
@@ -167,7 +167,7 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
dependency.getProductEvidence().addEvidence(name, "Project",
group, Confidence.HIGH);
}
LOGGER.debug(String.format("Found %d matches.", count));
LOGGER.debug("Found {} matches.", count);
analyzeSetVersionCommand(dependency, engine, contents);
}
}
@@ -178,9 +178,8 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
int count = 0;
while (m.find()) {
count++;
LOGGER.debug(String.format(
"Found project command match with %d groups: %s",
m.groupCount(), m.group(0)));
LOGGER.debug("Found project command match with {} groups: {}",
m.groupCount(), m.group(0));
String product = m.group(1);
final String version = m.group(2);
LOGGER.debug("Group 1: " + product);

View File

@@ -134,13 +134,14 @@ public class CPEAnalyzer implements Analyzer {
* process.
*/
public void open() throws IOException, DatabaseException {
LOGGER.debug("Opening the CVE Database");
cve = new CveDB();
cve.open();
LOGGER.debug("Creating the Lucene CPE Index");
cpe = CpeMemoryIndex.getInstance();
try {
LOGGER.info("Creating the CPE Index");
final long creationStart = System.currentTimeMillis();
cpe.open(cve);
LOGGER.info("CPE Index Created ({} ms)", System.currentTimeMillis() - creationStart);
} catch (IndexException ex) {
LOGGER.debug("IndexException", ex);
throw new DatabaseException(ex);

View File

@@ -154,8 +154,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
*/
@SuppressWarnings("null")
private void removeSpuriousCPE(Dependency dependency) {
final List<Identifier> ids = new ArrayList<Identifier>();
ids.addAll(dependency.getIdentifiers());
final List<Identifier> ids = new ArrayList<Identifier>(dependency.getIdentifiers());
Collections.sort(ids);
final ListIterator<Identifier> mainItr = ids.listIterator();
while (mainItr.hasNext()) {

View File

@@ -247,7 +247,7 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
}
} catch (IllegalArgumentException iae) {
//dependency.addAnalysisException(new AnalysisException("Invalid SHA-1"));
LOGGER.info(String.format("invalid sha-1 hash on %s", dependency.getFileName()));
LOGGER.info("invalid sha-1 hash on {}", dependency.getFileName());
} catch (FileNotFoundException fnfe) {
//dependency.addAnalysisException(new AnalysisException("Artifact not found on repository"));
LOGGER.debug("Artifact not found in repository '{}'", dependency.getFileName());

View File

@@ -28,14 +28,20 @@ import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.json.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonException;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonString;
import javax.json.JsonValue;
/**
* Used to analyze Node Package Manager (npm) package.json files, and collect information that can be used to determine
* the associated CPE.
* Used to analyze Node Package Manager (npm) package.json files, and collect information that can be used to determine the
* associated CPE.
*
* @author Dale Visser <dvisser@ida.org>
*/
@@ -60,8 +66,8 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
/**
* Filter that detects files named "package.json".
*/
private static final FileFilter PACKAGE_JSON_FILTER =
FileFilterBuilder.newInstance().addFilenames(PACKAGE_JSON).build();
private static final FileFilter PACKAGE_JSON_FILTER
= FileFilterBuilder.newInstance().addFilenames(PACKAGE_JSON).build();
/**
* Returns the FileFilter
@@ -120,17 +126,17 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
"Problem occurred while reading dependency file.", e);
}
try {
JsonObject json = jsonReader.readObject();
final JsonObject json = jsonReader.readObject();
final EvidenceCollection productEvidence = dependency.getProductEvidence();
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
if (json.containsKey("name")) {
Object value = json.get("name");
final Object value = json.get("name");
if (value instanceof JsonString) {
String valueString = ((JsonString) value).getString();
final String valueString = ((JsonString) value).getString();
productEvidence.addEvidence(PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
vendorEvidence.addEvidence(PACKAGE_JSON, "name_project", String.format("%s_project", valueString), Confidence.LOW);
} else {
LOGGER.warn("JSON value not string as expected: %s", value);
LOGGER.warn("JSON value not string as expected: {}", value);
}
}
addToEvidence(json, productEvidence, "description");
@@ -146,24 +152,25 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) {
if (json.containsKey(key)) {
Object value = json.get(key);
final JsonValue value = json.get(key);
if (value instanceof JsonString) {
collection.addEvidence(PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST);
} else if (value instanceof JsonObject) {
final JsonObject jsonObject = (JsonObject) value;
for (String property : jsonObject.keySet()) {
final Object subValue = jsonObject.get(property);
for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
final String property = entry.getKey();
final JsonValue subValue = entry.getValue();
if (subValue instanceof JsonString) {
collection.addEvidence(PACKAGE_JSON,
String.format("%s.%s", key, property),
((JsonString) subValue).getString(),
Confidence.HIGHEST);
} else {
LOGGER.warn("JSON sub-value not string as expected: %s");
LOGGER.warn("JSON sub-value not string as expected: {}", subValue);
}
}
} else {
LOGGER.warn("JSON value not string or JSON object as expected: %s", value);
LOGGER.warn("JSON value not string or JSON object as expected: {}", value);
}
}
}

View File

@@ -26,7 +26,7 @@ import java.io.FilenameFilter;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.input.AutoCloseInputStream;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.dependency.Confidence;

View File

@@ -93,17 +93,12 @@ public final class LuceneUtils {
* @return the escaped text.
*/
public static String escapeLuceneQuery(final CharSequence text) {
if (text == null) {
return null;
}
int size = text.length();
size = size >> 1;
final int size = text.length() << 1;
final StringBuilder buf = new StringBuilder(size);
appendEscapedLuceneQuery(buf, text);
return buf.toString();
}
}

View File

@@ -490,7 +490,7 @@ public class CveDB {
deleteReferences = getConnection().prepareStatement(statementBundle.getString("DELETE_REFERENCE"));
deleteSoftware = getConnection().prepareStatement(statementBundle.getString("DELETE_SOFTWARE"));
updateVulnerability = getConnection().prepareStatement(statementBundle.getString("UPDATE_VULNERABILITY"));
String ids[] = {"id"};
final String ids[] = {"id"};
insertVulnerability = getConnection().prepareStatement(statementBundle.getString("INSERT_VULNERABILITY"),
//Statement.RETURN_GENERATED_KEYS);
ids);

View File

@@ -24,7 +24,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.zip.GZIPInputStream;
import javax.xml.parsers.ParserConfigurationException;
@@ -69,8 +68,8 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
for (Cpe cpe : cpes) {
getCveDB().addCpe(cpe.getValue(), cpe.getVendor(), cpe.getProduct());
}
final Date now = new Date();
getProperties().save(LAST_CPE_UPDATE, Long.toString(now.getTime()));
final long now = System.currentTimeMillis();
getProperties().save(LAST_CPE_UPDATE, Long.toString(now));
LOGGER.info("CPE update complete");
}
} finally {
@@ -134,14 +133,14 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
* @return true if the CPE data should be refreshed
*/
private boolean updateNeeded() {
final Date now = new Date();
final long now = System.currentTimeMillis();
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 30);
long timestamp = 0;
final String ts = getProperties().getProperty(LAST_CPE_UPDATE);
if (ts != null && ts.matches("^[0-9]+$")) {
timestamp = Long.parseLong(ts);
}
return !DateUtil.withinDateRange(timestamp, now.getTime(), days);
return !DateUtil.withinDateRange(timestamp, now, days);
}
/**

View File

@@ -21,7 +21,6 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import org.apache.commons.io.IOUtils;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
@@ -88,7 +87,7 @@ public class EngineVersionCheck implements CachedWebDataSource {
LOGGER.debug("Begin Engine Version Check");
final DatabaseProperties properties = cveDB.getDatabaseProperties();
final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0"));
final long now = (new Date()).getTime();
final long now = System.currentTimeMillis();
updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, "");
final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0");
LOGGER.debug("Last checked: {}", lastChecked);

View File

@@ -19,7 +19,6 @@ package org.owasp.dependencycheck.data.update;
import java.net.MalformedURLException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -214,11 +213,11 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
if (!getProperties().isEmpty()) {
try {
final long lastUpdated = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_UPDATED, "0"));
final Date now = new Date();
final long now = System.currentTimeMillis();
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
if (lastUpdated == updates.getTimeStamp(MODIFIED)) {
updates.clear(); //we don't need to update anything.
} else if (DateUtil.withinDateRange(lastUpdated, now.getTime(), days)) {
} else if (DateUtil.withinDateRange(lastUpdated, now, days)) {
for (NvdCveInfo entry : updates) {
if (MODIFIED.equals(entry.getId())) {
entry.setNeedsUpdate(true);

View File

@@ -179,7 +179,7 @@ public class CPEHandler extends DefaultHandler {
/**
* A simple class to maintain information about the current element while parsing the CPE XML.
*/
protected class Element {
protected static final class Element {
/**
* A node type in the CPE Schema 2.2

View File

@@ -185,6 +185,7 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
final URL url1 = new URL(nvdCveInfo.getUrl());
final URL url2 = new URL(nvdCveInfo.getOldSchemaVersionUrl());
LOGGER.info("Download Started for NVD CVE - {}", nvdCveInfo.getId());
final long startDownload = System.currentTimeMillis();
try {
Downloader.fetchFile(url1, first);
Downloader.fetchFile(url2, second);
@@ -204,7 +205,8 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
extractGzip(second);
}
LOGGER.info("Download Complete for NVD CVE - {}", nvdCveInfo.getId());
LOGGER.info("Download Complete for NVD CVE - {} ({} ms)", nvdCveInfo.getId(),
System.currentTimeMillis() - startDownload);
if (this.processorService == null) {
return null;
}

View File

@@ -157,6 +157,7 @@ public class ProcessTask implements Callable<ProcessTask> {
*/
private void processFiles() throws UpdateException {
LOGGER.info("Processing Started for NVD CVE - {}", filePair.getNvdCveInfo().getId());
final long startProcessing = System.currentTimeMillis();
try {
importXML(filePair.getFirst(), filePair.getSecond());
cveDB.commit();
@@ -178,6 +179,7 @@ public class ProcessTask implements Callable<ProcessTask> {
} finally {
filePair.cleanup();
}
LOGGER.info("Processing Complete for NVD CVE - {}", filePair.getNvdCveInfo().getId());
LOGGER.info("Processing Complete for NVD CVE - {} ({} ms)", filePair.getNvdCveInfo().getId(),
System.currentTimeMillis() - startProcessing);
}
}

View File

@@ -28,7 +28,7 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.owasp.dependencycheck.data.nexus.MavenArtifact;
import org.owasp.dependencycheck.utils.Checksum;
import org.slf4j.Logger;

View File

@@ -17,8 +17,8 @@
*/
package org.owasp.dependencycheck.dependency;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;

View File

@@ -24,7 +24,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.owasp.dependencycheck.utils.DependencyVersion;
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
import org.owasp.dependencycheck.utils.Filter;

View File

@@ -19,7 +19,7 @@ package org.owasp.dependencycheck.reporting;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,7 +65,7 @@ public class EscapeTool {
if (text == null || text.isEmpty()) {
return text;
}
return StringEscapeUtils.escapeHtml(text);
return StringEscapeUtils.escapeHtml4(text);
}
/**
@@ -78,6 +78,6 @@ public class EscapeTool {
if (text == null || text.isEmpty()) {
return text;
}
return StringEscapeUtils.escapeXml(text);
return StringEscapeUtils.escapeXml11(text);
}
}

View File

@@ -22,7 +22,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
/**
* <p>