/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.jdr.commands;
import org.jboss.as.jdr.util.Utils;
import org.jboss.as.jdr.util.Sanitizer;
import org.jboss.as.jdr.vfs.Filters;
import org.jboss.vfs.VFS;
import org.jboss.vfs.VirtualFile;
import org.jboss.vfs.VirtualFileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class CollectFiles extends JdrCommand {
private VirtualFileFilter filter = Filters.TRUE;
private Filters.BlacklistFilter blacklistFilter = Filters.wildcardBlackList();
private LinkedList<Sanitizer> sanitizers = new LinkedList<Sanitizer>();
private Comparator<VirtualFile> sorter = new Comparator<VirtualFile>() {
@Override
public int compare(VirtualFile resource, VirtualFile resource1) {
return Long.signum(resource.getLastModified() - resource1.getLastModified());
}
};
// -1 means no limit
private long limit = -1;
public CollectFiles(VirtualFileFilter filter) {
this.filter = filter;
}
public CollectFiles(String pattern) {
this.filter = Filters.wildcard(pattern);
}
public CollectFiles sanitizer(Sanitizer ... sanitizers) {
for (Sanitizer s : sanitizers) {
this.sanitizers.add(s);
}
return this;
}
public CollectFiles sorter(Comparator<VirtualFile> sorter){
this.sorter = sorter;
return this;
}
public CollectFiles limit(final long limit){
this.limit = limit;
return this;
}
public CollectFiles omit(String pattern) {
blacklistFilter.add(pattern);
return this;
}
@Override
public void execute() throws Exception {
VirtualFile root = VFS.getChild(this.env.getJbossHome());
List<VirtualFile> matches = root.getChildrenRecursively(Filters.and(this.filter, this.blacklistFilter));
// order the files in some arbitrary way.. basically prep for the limiter so things like log files can
// be gotten in chronological order. Keep in mind everything that might be collected per the filter for
// this collector. If the filter is too broad, you may collect unrelated logs, sort them, and then
// get some limit on that set, which probably would be wrong.
if(sorter != null){
Collections.sort(matches, sorter);
}
// limit how much data we collect
Limiter limiter = new Limiter(limit);
Iterator<VirtualFile> iter = matches.iterator();
while(iter.hasNext() && !limiter.isDone()) {
VirtualFile f = iter.next();
InputStream stream = limiter.limit(f);
for (Sanitizer sanitizer : this.sanitizers) {
if(sanitizer.accepts(f)){
stream = sanitizer.sanitize(stream);
}
}
this.env.getZip().add(f, stream);
Utils.safelyClose(stream);
}
}
/**
* A Limiter is constructed with a number, and it can be repeatedly given VirtualFiles for which it will return an
* InputStream that possibly is adjusted so that the number of bytes the stream can provide, when added to what the
* Limiter already has seen, won't be more than the limit.
*
* If the VirtualFiles's size minus the amount already seen by the Limiter is smaller than the limit, the
* VirtualFiles's InputStream is simply returned and its size added to the number of bytes the Limiter has seen.
* Otherwise, the VirtualFiles's InputStream is skipped ahead so that the total number of bytes it will provide
* before exhaustion will make the total amount seen by the Limiter equal to its limit.
*/
private static class Limiter {
private long amountRead = 0;
private long limit = -1;
private boolean done = false;
public Limiter(long limit){
this.limit = limit;
}
public boolean isDone(){
return done;
}
/**
* @return
* @throws IOException
*/
public InputStream limit(VirtualFile resource) throws IOException {
InputStream is = resource.openStream();
long resourceSize = resource.getSize();
// if we're limiting and know we're not going to consume the whole file, we skip
// ahead so that we get the tail of the file instead of the beginning of it, and we
// toggle the done switch.
if(limit != -1){
long leftToRead = limit - amountRead;
if(leftToRead < resourceSize){
Utils.skip(is, resourceSize - leftToRead);
done = true;
} else {
amountRead += resourceSize;
}
}
return is;
}
}
}