Basically the sample code (attached) is a copy of DefaultFormatter with
some modifications to `formatEntry`
On Tue, Jul 16, 2019 at 6:13 PM Michael Wall <[email protected]> wrote:
> If you are still having issues, can you share your code?
>
> Mike
>
> On Tue, Jul 16, 2019 at 6:30 PM Christopher <[email protected]> wrote:
>
>> It's possible that the shell itself doesn't have it on its class path.
>> The output of the classpath command is probably the classpath of the
>> tabletserver you've connected to, not the current local shell process.
>>
>> Try `export CLASSPATH=path/to/MyFormatter.jar; bin/accumulo shell ...`
>>
>> On Tue, Jul 16, 2019 at 6:01 PM mhd wrk <[email protected]> wrote:
>> >
>> > Yes the jar is on all the tservers and I tried `config`command and
>> `formatter` commands as alternatives.
>> > Unfortunately no luck yet.
>> >
>> > On Tue, Jul 16, 2019 at 1:30 PM Michael Wall <[email protected]> wrote:
>> >>
>> >> Couple of thoughts
>> >>
>> >> 1 - Are you putting the jar on all the tservers and restarting
>> everthing?
>> >> 2 - Have you tried setting the formatter on the table as opposed to
>> the scan command. Something like
>> >> 'config -t mytable -s table.formatter=com.example.MyFormatter'
>> >>
>> >> Mike
>> >>
>> >> On Tue, Jul 16, 2019 at 2:50 PM mhd wrk <[email protected]>
>> wrote:
>> >>>
>> >>> I'm trying to use a custom formatter with scan command in Accumulo
>> shell, but seems that scan simply ignores the custom formatter and uses the
>> default one!
>> >>>
>> >>> The steps I follow are:
>> >>>
>> >>> 1- Create custom Formatter by extending
>> org.apache.accumulo.core.util.format.Formatter
>> >>>
>> >>> 2- Copy the jar to ${ACCUMULO_HOME}/lib
>> >>>
>> >>> 3- Restart Accumulo to make sure everything is reloaded.
>> >>>
>> >>> 4- run `accumulo shell`
>> >>>
>> >>> 5- run `classpath` and confirm that the formatter jar is included in
>> the path.
>> >>>
>> >>> 5- `scan -f 50 -fm com.example.MyFormatter -st -t mytable`
>> >>>
>> >>>
>> >>> Accumulo version: 1.7.2
>> >>>
>> >>>
>> >>> Any known issues?
>> >>>
>> >>>
>> >>> Thanks,
>> >>>
>> >>> Mohammad
>> >>>
>> >>>
>> >>>
>>
>
package com.example;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.security.ColumnVisibility;
import org.apache.accumulo.core.util.format.Formatter;
import org.apache.hadoop.io.Text;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
public class MyFormatter implements Formatter {
private Iterator<Map.Entry<Key,Value>> si;
private boolean doTimestamps;
public static class DefaultDateFormat extends DateFormat {
private static final long serialVersionUID = 1L;
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
toAppendTo.append(Long.toString(date.getTime()));
return toAppendTo;
}
@Override
public Date parse(String source, ParsePosition pos) {
return new Date(Long.parseLong(source));
}
}
private static final ThreadLocal<DateFormat> formatter = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new DefaultDateFormat();
}
};
@Override
public void initialize(Iterable<Map.Entry<Key,Value>> scanner, boolean printTimestamps) {
checkState(false);
si = scanner.iterator();
doTimestamps = printTimestamps;
}
@Override
public boolean hasNext() {
checkState(true);
return si.hasNext();
}
@Override
public String next() {
DateFormat timestampFormat = null;
if (doTimestamps) {
timestampFormat = formatter.get();
}
return next(timestampFormat);
}
protected String next(DateFormat timestampFormat) {
checkState(true);
return formatEntry(si.next(), timestampFormat);
}
@Override
public void remove() {
checkState(true);
si.remove();
}
protected void checkState(boolean expectInitialized) {
if (expectInitialized && si == null)
throw new IllegalStateException("Not initialized");
if (!expectInitialized && si != null)
throw new IllegalStateException("Already initialized");
}
/* so a new date object doesn't get created for every record in the scan result */
private static ThreadLocal<Date> tmpDate = new ThreadLocal<Date>() {
@Override
protected Date initialValue() {
return new Date();
}
};
public static String formatEntry(Map.Entry<Key,Value> entry, DateFormat timestampFormat) {
StringBuilder sb = new StringBuilder();
Key key = entry.getKey();
Text buffer = new Text();
// append row
Text row = key.getRow(buffer);
sb.append("row_").append(appendBytes(sb, row.getBytes(), 0, row.getLength())).append(" ");
// append column family
Text cf = key.getColumnFamily(buffer);
sb.append("cf_").append(appendBytes(sb, cf.getBytes(), 0, cf.getLength())).append(":");
// append column qualifier
Text cq = key.getColumnQualifier(buffer);
sb.append("cq_").append(appendBytes(sb, cq.getBytes(), 0, cq.getLength()));
// append visibility expression
sb.append(new ColumnVisibility(key.getColumnVisibility(buffer)));
// append timestamp
if (timestampFormat != null) {
tmpDate.get().setTime(entry.getKey().getTimestamp());
sb.append(" ").append(timestampFormat.format(tmpDate.get()));
}
Value value = entry.getValue();
// append value
if (value != null && value.getSize() > 0) {
sb.append("\t");
appendValue(sb, value);
}
return sb.toString();
}
static StringBuilder appendValue(StringBuilder sb, Value value) {
return appendBytes(sb, value.get(), 0, value.get().length);
}
static StringBuilder appendBytes(StringBuilder sb, byte ba[], int offset, int len) {
for (int i = 0; i < len; i++) {
int c = 0xff & ba[offset + i];
if (c == '\\')
sb.append("\\\\");
else if (c >= 32 && c <= 126)
sb.append((char) c);
else
sb.append("\\x").append(String.format("%02X", c));
}
return sb;
}
public Iterator<Map.Entry<Key,Value>> getScannerIterator() {
return si;
}
protected boolean isDoTimestamps() {
return doTimestamps;
}
}