package com.yahoo.dtf.actions.file; import java.io.File; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import java.util.regex.Pattern; import com.yahoo.dtf.exception.DTFException; import com.yahoo.dtf.exception.ParseException; import com.yahoo.dtf.recorder.Event; /** * @dtf.tag monitor_grep * * @dtf.since 1.0 * @dtf.author Rodney Gomes * * @dtf.tag.desc With this tag you can grep for any lines in the files that you * monitored for changes since the moment you created the monitor. * The expression for matching is just a normal regular expression * and all of the lines that match this expression will generate * an event as specified below. * * @dtf.event grep * @dtf.event.attr line * @dtf.event.attr.desc The line number at which the match was found. * * @dtf.event grep * @dtf.event.attr match * @dtf.event.attr.desc The line contents that matched the regular expression * defined above. * * @dtf.event grep * @dtf.event.attr file * @dtf.event.attr.desc The full path name and filename of the file that contains * the line that was matched. * * @dtf.tag.example * <sequence> * <monitor_create id="LOGS" file="tests/ut/output/${logfile}.*"/> * <sleep time="3s"/> * <monitor_grep id="LOGS" expression="NEW.*"/> * <monitor_destroy id="LOGS"/> * </sequence> * */ public class Monitor_grep extends Monitor_create { /** * @dtf.attr expression * @dtf.attr.desc the regular expression to match each of the lines of the * monitored files against. */ private String expression = null; /** * @dtf.attr insensitive * @dtf.attr.desc defines if the comparison should be done with case * senstivity enabled or disabled. */ private String insensitive = null; /** * @dtf.attr update * @dtf.attr.desc defines if we should move the current pointer in the file * forward or if we should keep analyzing all files from the * same point that we already picked at the beginning of the * test. The default is to not update and stay at the same * point since this allows for you to run multiple expressions * against the same file. By setting this attribute to true * you can move the files virtual "pointer" forward and keep * checking only the latest data for changes. * */ private String update = null; /** * Grabs the previous results and attaches any new files to the list of * files that are to be monitored. * * @return * @throws ParseException * @throws IOException */ protected HashMap<String, DTFBufferedReader> getNewChanges() throws ParseException, IOException { HashMap<String, FileMonitor> monitors = getMonitors(); FileMonitor previous = monitors.get(getId()); HashMap<String, DTFBufferedReader> files = previous.getFiles(); /* * Now find files that have been created in this same directory * after the monitor was started. */ File directory = previous.getDirectory(); final HashMap<String, DTFBufferedReader> filesFinal = files; final String patternFinal = previous.getPattern(); File[] newfiles = directory.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { String file = dir.getAbsolutePath() + File.separatorChar + name; return (!filesFinal.containsKey(file) && name.matches(patternFinal) && new File(dir,name).isFile()); } }); // Add the previous files to the files HashMap so that then you can // process the new files as well. StringBuffer sb = new StringBuffer(); if ( newfiles != null ) { for (int i = 0; i < newfiles.length; i++) { DTFBufferedReader br = new DTFBufferedReader(newfiles[i],0); files.put(newfiles[i].getAbsolutePath(),br); sb.append(newfiles[i].getName() + (i == newfiles.length-1 ? "" : ", ")); } getLogger().info("New files [" + sb.toString() + "] in " + previous.getDirectory().getAbsolutePath()); } else { getLogger().info("No files to monitor at [" + previous.getDirectory().getAbsolutePath() + "]"); } return files; } public void execute() throws DTFException { /* * Retrieve what the state of each of the old files was, i.e. what their * length was. So we can seek up to that length and then monitor for * a grep expression beyond that point. * * Also remember to get the list of files that were created since then * that still belong to the monitor so we can report on any matches * within those files. */ try { HashMap<String, DTFBufferedReader> files = getNewChanges(); int flags = 0; if (getInsensitive()) flags |= Pattern.CASE_INSENSITIVE; Pattern pattern = Pattern.compile(getExpression(), flags); Iterator<Entry<String,DTFBufferedReader>> entries = files.entrySet().iterator(); while (entries.hasNext()) { Entry<String,DTFBufferedReader> entry = entries.next(); DTFBufferedReader br = entry.getValue(); /* * If we're not updating then we just let the BufferedReaders * move forward otherwise we make sure to keep the BufferedReaders * pointing to the same place as when we created them with the * monitor_create */ if ( !getUpdate() ) { DTFBufferedReader aux = new DTFBufferedReader(br.getFile(),br.getCurrentPos()); files.put(entry.getKey(), aux); } String line = null; while ( (line = br.readLine()) != null) { if (pattern.matcher(line).matches()) { Event event = new Event("grep"); event.start(); event.stop(); event.addAttribute("file", entry.getKey()); event.addAttribute("match", line); event.addAttribute("line", br.getCurrentPos()); getRecorder().record(event); } } } if ( !getUpdate() ) { // we need to set the current set of files as the new base // for this monitor HashMap<String, FileMonitor> monitors = getMonitors(); FileMonitor previous = monitors.get(getId()); previous.replaceFiles(files); } } catch (FileNotFoundException e) { throw new DTFException("Monitored file was deleted.", e); } catch (IOException e) { throw new DTFException("Monitored file failed to skip.", e); } } public String getExpression() throws ParseException { return replaceProperties(expression); } public void setExpression(String expression) { this.expression = expression; } public boolean getInsensitive() throws ParseException { return toBoolean("insensitive",insensitive); } public void setInsensitive(String insensitive) { this.insensitive = insensitive; } public boolean getUpdate() throws ParseException { return toBoolean("update",update); } public void setUpdate(String update) { this.update = update; } }