/* * Copyright 2013 Brian Rosenberger (Brutex Network) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.brutex.xservices.util.cache; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.ws.rs.core.Response; import net.brutex.xservices.types.scm.ItemListType; import net.brutex.xservices.types.scm.ItemType; import net.brutex.xservices.types.scmfindings.FindingDetailsType; import net.brutex.xservices.types.scmfindings.FindingType; import net.brutex.xservices.types.scmfindings.FindingsListType; import net.brutex.xservices.types.scmfindings.GroupMatchListType; import net.brutex.xservices.types.scmfindings.ObjectFactory; import net.brutex.xservices.ws.rs.CVSInfoImpl; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.jcs.JCS; import org.apache.jcs.access.exception.CacheException; import org.apache.log4j.Logger; /** * @author Brian Rosenberger, bru(at)brutex.de * */ public class FindingsCacheServlet extends HttpServlet { private static final long serialVersionUID = 4041338473949999960L; private final static Logger logger = Logger .getLogger(FindingsCacheServlet.class); private final List configfiles = new ArrayList(); private final ObjectFactory FACTORY = new ObjectFactory(); @Override public void init() throws ServletException { super.init(); ExecutorService executor = (ExecutorService) getServletContext() .getAttribute("CACHE_EXECUTOR"); if(! this.initConfigList()) return; if(! this.initConfigFindings()) return; int i = 1; for(File f: configfiles) { //Initialise configuration bean using default values FindingsConfigBean cbean = new FindingsConfigBean(i, Logger.getLogger("worker-"+i+ "." + this.getClass().getName())); i++; //Read cvs-cache-interval parameter try { int cacheinterval = Integer.parseInt(getServletContext() .getInitParameter("cvs-cache-interval")); cbean.setCacheinterval(cacheinterval); logger.info("FindingsCacheServlet set to "+ cacheinterval + " minutes interval."); } catch (NumberFormatException e) { logger.warn("Could not read parameter 'cvs-cache-interval' from web.xml. Using default value '" + cbean.getCacheinterval()+ "' minutes"); } PropertiesConfiguration config = null; try { config = new PropertiesConfiguration(f); } catch (ConfigurationException e) { logger.error("Could not read parameter file at '"+f.getAbsolutePath()+"'"); return; } File cvsconfig = new File(config.getString("CVSROOTCONFIGFILE")); cbean.setCvsconfig(cvsconfig); FindingsCacheServlet.logger.debug("Fetching list of files using '" + cvsconfig.getAbsolutePath() + "' config file"); List filepatterns = config.getList("FILESEARCH"); cbean.setFilepatterns(filepatterns); FindingsCacheServlet.logger.debug("Checking '" + filepatterns.size() + "' patterns for file name and path matching."); List contentpatterns = config.getList("CONTENTSEARCH"); cbean.setContentpatterns(contentpatterns); FindingsCacheServlet.logger.debug("Checking '" + contentpatterns.size() + "' patterns for content matching"); executor.submit(new ThisRunnable(cbean)); } logger.info("FindingsCacheServlet has been initialized."); } /* * Initialise CVS findings configuration */ private boolean initConfigFindings() { String filename = getServletContext().getInitParameter( "cvs-findings-configuration"); if (filename == null) { logger.warn("'cvs-findings-configuration' init parameter is not specified."); return false; } final File findingsconfig = new File(filename); logger.info("CVS findings configuration file found at '" + findingsconfig.getAbsolutePath() + "'"); if ((!findingsconfig.canRead()) || (findingsconfig.isDirectory())) { logger.info("CVS findings configuration file '" + findingsconfig.getAbsolutePath() + "' does not exist."); return false; } return true; } /* * Add all specified CVS configuration files to the list. Parameter pattern * is "cvs-config-XX". */ private boolean initConfigList() { Enumeration attributes = getServletContext() .getInitParameterNames(); while (attributes.hasMoreElements()) { String name = (String) attributes.nextElement(); if (name.startsWith("cvs-config-")) { String configfile = getServletContext().getInitParameter(name); logger.info("Adding CVS configuration file: " + configfile); this.configfiles.add(new File(configfile)); } } /* * Verify, that all configuration files do exists and are readable. */ List removelist = new ArrayList(); for (File f : configfiles) { if (!f.exists()) { logger.warn("CVS configuration file '" + f.getAbsolutePath() + "' is specified, but does not exist. Removing from list."); removelist.add(f); } else if (!f.canRead()) { logger.warn("CVS configuration file '" + f.getAbsolutePath() + "' does exist, but is not readable. Removing from list."); removelist.add(f); } } configfiles.removeAll(removelist); return true; } class ThisRunnable implements Runnable { boolean isInterrupted = false; FindingsConfigBean configuration; public ThisRunnable(FindingsConfigBean configuration) { this.configuration = configuration; } public void run() { CVSInfoImpl instance = new CVSInfoImpl(); ItemListType fileslist = (ItemListType) instance .getRepositoryFiles(null, configuration.getCvsconfig(), "", true, true, true) .getEntity(); ObjectFactory FACTORY = new ObjectFactory(); FindingsListType findingsList = FACTORY.createFindingsListType(); FindingsCacheServlet.logger.info("Processing '" + fileslist.getItems().size() + "' files and directories."); while (!this.isInterrupted) { Pattern p; for (ItemType i : fileslist.getItems()) { if (this.isInterrupted) break; Iterator iterF = configuration.getFilepatterns().iterator(); while(iterF.hasNext()) { Object o = iterF.next(); if (this.isInterrupted) break; FindingsCacheServlet.logger.debug("Scanning filename '" + i.getFullname() + "' for pattern '" + (String) o + "'"); p = Pattern.compile((String) o); Matcher m = p.matcher(i.getFullname()); if (m.find()) { FindingType finding = FACTORY.createFindingType(); finding.setFilesearch(p.toString()); ItemType it = (ItemType) instance.getFileContent( null, configuration.getCvsconfig(), i.getFullname(), true) .getEntity(); finding.setContent(it.getContent()); finding.setData(it.getData()); finding = copyDetails(finding, i); findingsList.getFindings().add(finding); FindingsCacheServlet.logger .debug("Match found for '" + i.getFullname() + "'"); break; } FindingsCacheServlet.logger .debug("No match found for '" + i.getFullname() + "'"); } } FindingsCacheServlet.logger .debug("Processing file content for '" + findingsList.getFindings().size() + "' entries in the list."); for (FindingType t : findingsList.getFindings()) { if (this.isInterrupted) break; boolean isFound = false; Matcher m; Iterator iter = configuration.getContentpatterns().iterator(); while (iter.hasNext()) { Object o = iter.next(); if (this.isInterrupted) break; FindingsCacheServlet.logger .debug("Scanning file content for file '" + t.getFullname() + "' for pattern '" + (String) o + "'"); Pattern p1 = Pattern.compile((String) o); m = p1.matcher(t.getContent()); t.setContentsearch(p1.toString()); isFound = true; int s = m.start(); int e = m.end(); String c = m.group(); FindingDetailsType fd = FACTORY .createFindingDetailsType(); GroupMatchListType gm = FACTORY .createGroupMatchListType(); gm.setMatchAtIndex(s); gm.setMatchToIndex(e); gm.setMatchString(c); gm.setMatchGroup(0); fd.setFullmatch(gm); for (int i = 1; i <= m.groupCount(); i++) { GroupMatchListType gmg = FACTORY .createGroupMatchListType(); s = m.start(i); e = m.end(i); c = m.group(i); gmg.setMatchAtIndex(s); gmg.setMatchToIndex(e); gmg.setMatchString(c); gmg.setMatchGroup(i); fd.getMatchLists().add(gmg); } t.getFindingLists().add(fd); FindingsCacheServlet.logger .debug("Found matching content at index '" + s + "' in file '" + t.getFullname() + "' with pattern '" + p1.toString() + "'"); } if (!isFound) { findingsList.getFindings().remove(t); FindingsCacheServlet.logger .debug("Found matching filename for '" + t.getFullname() + "' but content didn't match. Removing."); } try { instance.getCacheInstance().put( "FINDINGS-" + t.getROOT(), findingsList); FindingsCacheServlet.logger .info("FINDINGS for CVSROOT '" + t.getROOT() + "' have been updated in cache."); } catch (CacheException e) { FindingsCacheServlet.logger.error(e.getMessage(), e); } } try { int cacheinterval = configuration.getCacheinterval(); FindingsCacheServlet.logger.debug("Now sleeping for '" + cacheinterval + "' minutes"); Thread.currentThread(); Thread.sleep(cacheinterval * 60000); FindingsCacheServlet.logger.debug("Waking up after '" + cacheinterval + "' minutes of sleep"); } catch (InterruptedException e) { this.isInterrupted = true; FindingsCacheServlet.logger .warn("FindingsCacheServlet cache was interrupted. Shutting down."); } } } private FindingType copyDetails(FindingType finding, ItemType item) { finding.setDescription(item.getDescription()); finding.setFullname(item.getFullname()); finding.setName(item.getName()); finding.setPath(item.getPath()); finding.setRemotefullname(item.getRemotefullname()); finding.setRemotename(item.getRemotename()); finding.setRemotepath(item.getRemotepath()); finding.setROOT(item.getROOT()); return finding; } } }