37 |
38 |
39 |
40 | {{ content }}
41 |
42 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/test/java/com/rolfje/anonimatron/progress/ProgressTest.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.progress;
2 |
3 | import java.util.Date;
4 |
5 | import junit.framework.TestCase;
6 |
7 | public class ProgressTest extends TestCase {
8 | private Progress progress;
9 |
10 | @Override
11 | protected void setUp() throws Exception {
12 | super.setUp();
13 | progress = new Progress();
14 | }
15 |
16 | public void testPercentageCalculations() {
17 | progress.setTotalitemstodo(1000);
18 | progress.setTotalitemscompleted(0);
19 |
20 | assertEquals(0, progress.getCompletePercentage());
21 |
22 | progress.setTotalitemscompleted(333);
23 | assertEquals(33, progress.getCompletePercentage());
24 |
25 | progress.setTotalitemscompleted(495);
26 | assertEquals(50, progress.getCompletePercentage());
27 |
28 | progress.setTotalitemscompleted(504);
29 | assertEquals(50, progress.getCompletePercentage());
30 |
31 | progress.setTotalitemscompleted(666);
32 | assertEquals(67, progress.getCompletePercentage());
33 |
34 | progress.setTotalitemscompleted(754);
35 | assertEquals(75, progress.getCompletePercentage());
36 |
37 | progress.setTotalitemscompleted(999);
38 | assertEquals(100, progress.getCompletePercentage());
39 | }
40 |
41 | public void testETAfifty() throws Exception {
42 | progress.setTotalitemstodo(100);
43 | progress.startTimer();
44 |
45 | Thread.sleep(100);
46 | progress.setTotalitemscompleted(50);
47 |
48 | long now = System.currentTimeMillis();
49 | long eta = progress.getETA().getTime();
50 | long start = progress.getStartTime().getTime();
51 |
52 | long expected = now + (now - start);
53 |
54 | assertETA(expected, eta);
55 |
56 | }
57 |
58 | private void assertETA(long expected, long eta) {
59 | int maxerror = 1;
60 |
61 | long error = Math.abs(Math.round(100F * (eta - expected) / expected));
62 | assertTrue("ETA is more than " + maxerror + "% off, should be "
63 | + expected + "(" + new Date(expected) + ") but was " + eta
64 | + "(" + new Date(eta) + ").", error < maxerror);
65 | }
66 |
67 | public void testETAWeek() throws Exception {
68 | long millisInWeek = 7L * 24 * 60 * 60 * 100;
69 |
70 | progress.setTotalitemstodo(millisInWeek);
71 | progress.startTimer();
72 |
73 | Thread.sleep(100);
74 | progress.setTotalitemscompleted(100);
75 |
76 | long start = progress.getStartTime().getTime();
77 | long eta = progress.getETA().getTime();
78 | long expected = start + millisInWeek;
79 |
80 | assertETA(expected, eta);
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/rolfje/anonimatron/progress/ProgressPrinter.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.progress;
2 |
3 | import org.apache.log4j.Logger;
4 |
5 | import java.text.DateFormat;
6 |
7 | public class ProgressPrinter implements Runnable {
8 | private static final Logger LOG = Logger.getLogger(ProgressPrinter.class);
9 |
10 | private int printIntervalMillis = 4000;
11 | private Progress progress;
12 | private boolean printing = false;
13 | private Thread thread;
14 | private String message = "";
15 | private String lastMessage = "";
16 |
17 | private final DateFormat timeformat = DateFormat
18 | .getTimeInstance(DateFormat.MEDIUM);
19 |
20 | public ProgressPrinter(Progress p) {
21 | progress = p;
22 | }
23 |
24 | public void setMessage(String message) {
25 | this.message = message;
26 | }
27 |
28 | public void setPrintIntervalMillis(int printIntervalMillis) {
29 | this.printIntervalMillis = printIntervalMillis;
30 | }
31 |
32 | @Override
33 | public void run() {
34 | if (progress.getStartTime().getTime() <= 0) {
35 | LOG.debug("Progress timer was not started by caller, starting it now to get sensible ETA figures.");
36 | progress.startTimer();
37 | }
38 |
39 | while (printing) {
40 | print();
41 | sleep();
42 | }
43 | }
44 |
45 | private void print() {
46 | String eta = timeformat.format(progress.getETA());
47 | String toprint = message + " [" + progress.getCompletePercentage()
48 | + "%, ETA " + eta + "]";
49 | toprint = toprint.trim();
50 |
51 | if (!toprint.equals(lastMessage)) {
52 | // Only print if information changed.
53 | for (int i = 0; i < lastMessage.length(); i++) {
54 | // Clear old message with backspaces (does not work in some consoles)
55 | System.out.print('\b');
56 | }
57 | System.out.print(toprint);
58 | lastMessage = toprint;
59 | }
60 | }
61 |
62 | private void sleep() {
63 | try {
64 | Thread.sleep(printIntervalMillis);
65 | } catch (InterruptedException e) {
66 | // ignore and continue
67 | Thread.currentThread().interrupt();
68 | }
69 | }
70 |
71 | public void start() {
72 | printing = true;
73 | thread = new Thread(this);
74 | thread.start();
75 | }
76 |
77 | public void stop() {
78 | if (!printing) {
79 | return;
80 | }
81 |
82 | printing = false;
83 |
84 | while (thread != null && thread.isAlive()) {
85 | try {
86 | thread.join();
87 | } catch (InterruptedException e) {
88 | // ignore, retry
89 | Thread.currentThread().interrupt();
90 | }
91 | }
92 | print(); // Make sure 100% is printed
93 | thread = null;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/rolfje/anonimatron/anonymizer/Hasher.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.anonymizer;
2 |
3 | import org.castor.core.util.Base64Encoder;
4 |
5 | import javax.crypto.SecretKeyFactory;
6 | import javax.crypto.spec.PBEKeySpec;
7 | import java.io.ByteArrayOutputStream;
8 | import java.io.IOException;
9 | import java.io.ObjectOutputStream;
10 | import java.nio.charset.Charset;
11 | import java.nio.charset.StandardCharsets;
12 | import java.security.NoSuchAlgorithmException;
13 | import java.security.spec.InvalidKeySpecException;
14 | import java.security.spec.KeySpec;
15 |
16 |
17 | public class Hasher {
18 |
19 | // Ideally more iterations mean better protection against brute
20 | // force attacks. Since synonyms are accross the while dataset,
21 | // an attacker would only know the generated synonym type based
22 | // on the synonym file, but not the source date type or location.
23 | private static final int ITERATIONS = 64;
24 |
25 | // Roughly same space as version 4 UUID, more than 5 undecillion combinations,
26 | // very unlikely to generate collisions.
27 | private static final int SIZE = 128;
28 |
29 | private static final String ALGORITHM = "PBKDF2WithHmacSHA1";
30 | public static final Charset CHARSET = StandardCharsets.UTF_8;
31 | private byte[] salt;
32 |
33 | public Hasher(String salt) {
34 | this.salt = salt.getBytes();
35 | }
36 |
37 | public String base64Hash(Object object) {
38 | byte[] serialize = serialize(object);
39 | char[] chars = toCharArray(serialize);
40 | return new String(Base64Encoder.encode(pbkdf2(chars)));
41 | }
42 |
43 | public String base64Hash(String object) {
44 | byte[] hash = pbkdf2(object.toCharArray());
45 | return new String(Base64Encoder.encode(hash));
46 | }
47 |
48 | private char[] toCharArray(byte[] bytes) {
49 | return new String(bytes, CHARSET).toCharArray();
50 | }
51 |
52 | private byte[] serialize(Object object) {
53 | try {
54 | ByteArrayOutputStream out = new ByteArrayOutputStream();
55 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
56 | objectOutputStream.writeObject(object);
57 | objectOutputStream.close();
58 | return out.toByteArray();
59 | } catch (IOException e) {
60 | throw new RuntimeException("Unexpected problem serializing object.", e);
61 | }
62 | }
63 |
64 | private byte[] pbkdf2(char[] password) {
65 | KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, SIZE);
66 | try {
67 | SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
68 | return f.generateSecret(spec).getEncoded();
69 | } catch (NoSuchAlgorithmException ex) {
70 | throw new IllegalStateException("Missing algorithm: " + ALGORITHM, ex);
71 | } catch (InvalidKeySpecException ex) {
72 | throw new IllegalStateException("Invalid SecretKeyFactory", ex);
73 | }
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/rolfje/anonimatron/anonymizer/Anonymizer.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.anonymizer;
2 |
3 | import com.rolfje.anonimatron.synonyms.Synonym;
4 |
5 | import java.sql.Date;
6 | import java.util.Map;
7 |
8 | /**
9 | * Provides functionality for consitently anonymizing a piece of data.
10 | *
11 | * Implementations of this interface must make sure that anonymization is done
12 | * in a reproducable manner. That is, if A transforms into B, it has to
13 | * consistently do so on each and every call.
14 | *
15 | * By doing this, anonimatron can guarantee that data is transformed
16 | * consistently accross all tables of the database, and referential constraints
17 | * can be re-enforced after anonymization.
18 | */
19 | public interface Anonymizer {
20 |
21 | /**
22 | * @return The ID or name of this anonymizer, as used in the XML
23 | * configuration file. This is generally something along the lines
24 | * of "LASTNAME" or "UNEVENNUMBER". Please see the output of the
25 | * -configexample command line option when running Anonimatron.
26 | */
27 | String getType();
28 |
29 | /**
30 | * Anonymizes the given data into a non-tracable, non-reversible synonym,
31 | * and does it consistently, so that A always translates to B.
32 | *
33 | * @param from the data to be anonymized, usually passed in as a
34 | * {@link String}, {@link Integer}, {@link Date} or other classes
35 | * which can be stored in a single JDBC database column.
36 | * @param size the optional maximum size of the generated value
37 | * @param shortlived indicates that the generated synonym must have the
38 | * {@link Synonym#isShortLived()} boolean set
39 | * @return a {@link Synonym}
40 | */
41 | Synonym anonymize(Object from, int size, boolean shortlived);
42 |
43 | /**
44 | * Anonymizes the given data into a non-tracable, non-reversible synonym
45 | * with the provided parameters, and does it consistently, so that A
46 | * always translates to B.
47 | * @param from the data to be anonymized, usually passed in as a
48 | * {@link String}, {@link Integer}, {@link java.sql.Date} or other classes
49 | * which can be stored in a single JDBC database column.
50 | * @param size the optional maximum size of the generated value
51 | * @param shortlived indicates that the generated synonym must have the
52 | * {@link Synonym#isShortLived()} boolean set
53 | * @param parameters the parameters to be used by the anonymizer
54 | * @return a {@link Synonym}
55 | */
56 | default Synonym anonymize(Object from, int size, boolean shortlived, Map parameters) {
57 | return anonymize(from, size, shortlived);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/rolfje/anonimatron/anonymizer/CharacterStringPrefetchAnonymizerTest.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.anonymizer;
2 |
3 | import com.rolfje.anonimatron.synonyms.Synonym;
4 | import junit.framework.TestCase;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | public class CharacterStringPrefetchAnonymizerTest extends TestCase {
10 |
11 | CharacterStringPrefetchAnonymizer anonimyzer;
12 |
13 | @Override
14 | protected void setUp() throws Exception {
15 | super.setUp();
16 | anonimyzer = new CharacterStringPrefetchAnonymizer();
17 | }
18 |
19 | public void testPrefetch() {
20 | String sourceData = "ABC";
21 | anonimyzer.prefetch(sourceData);
22 |
23 | String from = "TEST1";
24 | Synonym synonym = anonimyzer.anonymize(from, 5, false);
25 |
26 | String to = (String) synonym.getTo();
27 | assertEquals("from and to string lengths did not match", from.length(), to.length());
28 |
29 | for (int i = 0; i < to.length(); i++) {
30 | assertTrue("'to' string contained characters which were not in the source data."
31 | , sourceData.indexOf(to.charAt(i)) > -1);
32 | }
33 | }
34 |
35 | public void testParameterizedCharacterString() {
36 | String from = "TEST1";
37 | String characters = "#$%";
38 |
39 | Map m = new HashMap<>();
40 | m.put(CharacterStringAnonymizer.PARAMETER, characters);
41 | Synonym synonym = anonimyzer.anonymize(from, 5, false, m);
42 |
43 | String to = (String) synonym.getTo();
44 | for (int i = 0; i < to.length(); i++) {
45 | assertTrue("'to' string contained characters which were not in the parameter."
46 | , characters.indexOf(to.charAt(i)) > -1);
47 | }
48 | }
49 |
50 | public void testWrongParameter() {
51 | try {
52 | anonimyzer.anonymize("any", 10, false, null);
53 |
54 | anonimyzer.anonymize("any", 10, false, new HashMap<>());
55 |
56 | anonimyzer.anonymize("any", 10, false, new HashMap() {{
57 | put("PaRaMeTeR", "any");
58 | }});
59 |
60 | fail("Using the wrong parameters should throw an exception.");
61 | } catch (UnsupportedOperationException e) {
62 | assertTrue("Exception should point to the character parameter",
63 | e.getMessage().contains(CharacterStringAnonymizer.PARAMETER));
64 | }
65 | }
66 |
67 | public void testPrefetchNull() {
68 | anonimyzer.prefetch(null);
69 | String from = "DUMMY";
70 | Synonym synonym = anonimyzer.anonymize(from, 5, false);
71 |
72 | String to = (String) synonym.getTo();
73 | assertEquals("from and to string lengths did not match", from.length(), to.length());
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/docs/documentation/anonymizerlist.md:
--------------------------------------------------------------------------------
1 | # Available Anonymizers
2 |
3 | Anonimatron comes with the following default anonymizers. Please start Anonimatron with the `-configexample`
4 | parameter to see how these are configured. For more information on how Anonimatron works and runs, check our [quickstart](index.md).
5 |
6 | | Name | Type | Input | Output |
7 | |:----------------------------------|:--------------------|:-----------------|:-------------------------------------------------------------------------|
8 | | CharacterStringAnonymizer | RANDOMCHARACTERS | Any string | A-Z, same length |
9 | | CharacterStringPrefetchAnonymizer | RANDOMCHARACTERS | Any string | Characters from all input data, same length |
10 | | CountryCodeAnonymizer | COUNTRY_CODE | Any string | ISO 3166-1 alpha 2 or alpha 3 code |
11 | | DateAnonymizer | DATE | Valid date | Date between 31 days before and 32 days after the input date |
12 | | DigitStringAnonymizer | RANDOMDIGITS | Any string | 0-9, same length, optional mask |
13 | | DutchBankAccountAnononymizer | DUTCHBANKACCOUNT | Any string | 11 proof number, minimal 9 digits |
14 | | DutchBSNAnononymizer | BURGERSERVICENUMMER | Number or string | Valid Dutch "Burger Service Nummer" or "SOFI Nummer" as number or string |
15 | | DutchZipCodeAnonymizer | DUTCH_ZIP_CODE | Any string | Valid Dutch zip/postal code |
16 | | ElvenNameGenerator | ELVEN_NAME | Any string | Pronounceable elven name, 2 to 5 syllables |
17 | | EmailAddressAnonymizer | EMAIL_ADDRESS | Any string | Valid email address in the domain "@example.com" |
18 | | IbanAnonymizer | IBAN | Any string | Valid International Bank Account Number |
19 | | RomanNameGenerator | ROMAN_NAME | Any string | Pronounceable Roman name, 2 to 5 syllables |
20 | | StringAnonymizer | STRING | Any string | Random hexadecimal string |
21 | | UkPostCodeAnonymizer | UK_POST_CODE | Any string | Valid Uk Post code |
22 | | UUIDAnonymizer | UUID | Any string | A random UUID |
--------------------------------------------------------------------------------
/src/test/java/com/javamonitor/tools/Stopwatch.java:
--------------------------------------------------------------------------------
1 | package com.javamonitor.tools;
2 |
3 | import org.apache.log4j.Level;
4 | import org.apache.log4j.Logger;
5 |
6 | import java.io.Serializable;
7 |
8 | /**
9 | * A simple stopwatch timer to trace slow method calls.
10 | * This is a faster and leaner alternative to {@link org.springframework.util.StopWatch},
11 | * please note the differences in usage and output.
12 | *
13 | * @author Kees Jan Koster <kjkoster@kjkoster.org>
14 | */
15 | public final class Stopwatch implements Serializable {
16 | private static final long serialVersionUID = 1L;
17 |
18 | private static final Logger log = Logger.getLogger(Stopwatch.class);
19 |
20 | private final long start;
21 | private long lastTime;
22 | private Level loglevel = Level.WARN;
23 |
24 | private final StringBuilder message = new StringBuilder();
25 |
26 | /**
27 | * Start a new stopwatch, specifying the class we work for.
28 | *
29 | * @param clazz The class we work for.
30 | */
31 | public Stopwatch(final Class> clazz) {
32 | this(clazz.getName());
33 | }
34 |
35 | /**
36 | * Start a new stopwatch, with a custom name.
37 | *
38 | * @param name The name of this Stopwatch
39 | */
40 | public Stopwatch(final String name) {
41 | super();
42 |
43 | start = System.currentTimeMillis();
44 | lastTime = start;
45 |
46 | message.append("entering ").append(name).append(" took ");
47 | }
48 |
49 | /**
50 | * Mark the time of the operation that we are about to perform.
51 | *
52 | * @param operation The operation we are about to perform.
53 | */
54 | public void aboutTo(final String operation) {
55 | final long now = System.currentTimeMillis();
56 | final long timeDiff = now - lastTime;
57 | lastTime = now;
58 |
59 | message.append(timeDiff).append("; ").append(operation)
60 | .append(" took ");
61 | }
62 |
63 | /**
64 | * Stop the stopwatch, logging the events in case the time was longer than
65 | * the specified threshold time value. This method is typically invoked in a
66 | * finally block.
67 | *
68 | * @param thresholdMillis The threshold above which we print the events.
69 | * @return true if the operation completes within the specified time
70 | */
71 | public boolean stop(final long thresholdMillis) {
72 | final long now = System.currentTimeMillis();
73 | final long timeDiff = now - lastTime;
74 | lastTime = now;
75 |
76 | long total = now - start;
77 | message.append(timeDiff).append(". Total: ").append(total).append(" ms.");
78 |
79 | if ((total) > thresholdMillis) {
80 | log.log(loglevel, message);
81 | return false;
82 | }
83 | return true;
84 | }
85 |
86 | public String getMessage(){
87 | return message.toString();
88 | }
89 |
90 | public Stopwatch setLoglevel(Level loglevel) {
91 | this.loglevel = loglevel;
92 | return this;
93 | }
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/src/test/java/com/rolfje/anonimatron/anonymizer/UkPostCodeAnonymizerTest.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.anonymizer;
2 |
3 | import com.rolfje.anonimatron.synonyms.Synonym;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import java.util.regex.Pattern;
8 |
9 | import static org.junit.Assert.*;
10 |
11 | public class UkPostCodeAnonymizerTest {
12 |
13 | private UkPostCodeAnonymizer ukPostCodeAnonymizer;
14 |
15 | @Before
16 | public void setUp() {
17 | ukPostCodeAnonymizer = new UkPostCodeAnonymizer();
18 | }
19 |
20 | @Test
21 | public void testValidFormatOfGeneratedCodes() {
22 | int numberOfCodes = 10_000;
23 | long max_time_ms = 1000; // Be forgiving here; not all machines perform equally
24 |
25 | long start = System.currentTimeMillis();
26 | for (int i = 0; i < 10_000; i++) {
27 | assertValidPostalCode(ukPostCodeAnonymizer.buildZipCode());
28 | }
29 | long stop = System.currentTimeMillis();
30 | long duration = stop - start;
31 |
32 | assertTrue("Generating " + numberOfCodes + " took " + duration + ".", duration < max_time_ms);
33 | }
34 |
35 | @Test
36 | public void testPostalCodeFlavor1() {
37 | assertValidPostalCode(ukPostCodeAnonymizer.buildZipCodeFlavor1());
38 | }
39 |
40 | @Test
41 | public void testPostalCodeFlavor2() {
42 | assertValidPostalCode(ukPostCodeAnonymizer.buildZipCodeFlavor2());
43 | }
44 |
45 |
46 | private void assertValidPostalCode(String s) {
47 |
48 | if (s.length() > 8) {
49 | fail("length longer than 8");
50 | }
51 |
52 | // See https://stackoverflow.com/a/164994
53 | String regex = "^([Gg][Ii][Rr] ?0[Aa]{2})|" +
54 | "((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))" +
55 | " ?[0-9][A-Za-z]{2})$";
56 |
57 | Pattern regexPattern = Pattern.compile(regex);
58 | assertTrue("Invalid postal code generated: '" + s + "'", regexPattern.matcher(s).matches());
59 | }
60 |
61 | @Test
62 | public void testGenerateCorrectSynonym() {
63 | Synonym s1 = ukPostCodeAnonymizer.anonymize("x", 10, false);
64 | assertEquals(ukPostCodeAnonymizer.getType(), s1.getType());
65 | assertNotEquals(s1.getFrom(), s1.getTo());
66 | assertFalse(s1.isShortLived());
67 |
68 | Synonym s2 = ukPostCodeAnonymizer.anonymize("x", 10, true);
69 | assertEquals(ukPostCodeAnonymizer.getType(), s2.getType());
70 | assertNotEquals(s2.getFrom(), s2.getTo());
71 | assertTrue(s2.isShortLived());
72 |
73 | assertNotEquals(s1.getTo(), s2.getTo());
74 | }
75 |
76 | @Test
77 | public void testGenerateErrorForShortCodes() {
78 | try {
79 | ukPostCodeAnonymizer.anonymize("x", 5, false);
80 | fail("Should throw a configuration error.");
81 | } catch (UnsupportedOperationException e) {
82 | assertTrue(e.getMessage().contains("will not always fit"));
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/com/javamonitor/tools/StopwatchNano.java:
--------------------------------------------------------------------------------
1 | package com.javamonitor.tools;
2 |
3 | import org.apache.log4j.Level;
4 | import org.apache.log4j.Logger;
5 |
6 | import java.io.Serializable;
7 |
8 | /**
9 | * A simple stopwatch timer to trace slow method calls.
10 | * This is a faster and leaner alternative to {@link org.springframework.util.StopWatch},
11 | * please note the differences in usage and output.
12 | *
13 | * @author Kees Jan Koster <kjkoster@kjkoster.org>
14 | */
15 | public final class StopwatchNano implements Serializable {
16 | private static final long serialVersionUID = 1L;
17 |
18 | private static final Logger log = Logger.getLogger(StopwatchNano.class);
19 |
20 | private final long startNanos;
21 | private long lastTimeNanos;
22 | private Level loglevel = Level.WARN;
23 |
24 | private final StringBuilder message = new StringBuilder();
25 |
26 | /**
27 | * Start a new stopwatch, specifying the class we work for.
28 | *
29 | * @param clazz The class we work for.
30 | */
31 | public StopwatchNano(final Class> clazz) {
32 | this(clazz.getName());
33 | }
34 |
35 | /**
36 | * Start a new stopwatch, with a custom name.
37 | *
38 | * @param name The name of this Stopwatch
39 | */
40 | public StopwatchNano(final String name) {
41 | super();
42 |
43 | startNanos = System.nanoTime();
44 | lastTimeNanos = startNanos;
45 |
46 | message.append("entering ").append(name).append(" took ");
47 | }
48 |
49 | /**
50 | * Mark the time of the operation that we are about to perform.
51 | *
52 | * @param operation The operation we are about to perform.
53 | */
54 | public void aboutTo(final String operation) {
55 | final long now = System.nanoTime();
56 | final long timeDiff = now - lastTimeNanos;
57 | lastTimeNanos = now;
58 |
59 | message.append(timeDiff).append("; ").append(operation)
60 | .append(" took ");
61 | }
62 |
63 | /**
64 | * Stop the stopwatch, logging the events in case the time was longer than
65 | * the specified threshold time value. This method is typically invoked in a
66 | * finally block.
67 | *
68 | * @param thresholdMillis The threshold above which we print the events.
69 | * @return true if the operation completes within the specified time
70 | */
71 | public boolean stop(final long thresholdMillis) {
72 | final long now = System.nanoTime();
73 | final long timeDiff = now - lastTimeNanos;
74 | lastTimeNanos = now;
75 |
76 | long total = now - startNanos;
77 | message.append(timeDiff).append(". Total: ").append(total).append(" ns.");
78 |
79 | if ((total) > (thresholdMillis * 1_000_000)) {
80 | log.log(loglevel, message);
81 | return false;
82 | }
83 | return true;
84 | }
85 |
86 | public String getMessage() {
87 | return message.toString();
88 | }
89 |
90 | public StopwatchNano setLoglevel(Level loglevel) {
91 | this.loglevel = loglevel;
92 | return this;
93 | }
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/resources/anonymizers/README.md:
--------------------------------------------------------------------------------
1 | In this directory, you can put jar files containing your own custom
2 | anonymizers. When you do so, don't forget to register them in your config.xml
3 | so that Anonimatron can instantiate them. Examples of an Anonymizer
4 | and a config below:
5 |
6 | This is an example of an anonymizer which returns lower case Strings for
7 | each String passed in. In the root of the unzipped anonimatron project, create a file called `my/package/ToLowerAnonymizer.java`:
8 |
9 | ```java
10 | package my.package;
11 |
12 | import com.rolfje.anonimatron.synonyms.StringSynonym;
13 | import com.rolfje.anonimatron.synonyms.Synonym;
14 | import com.rolfje.anonimatron.anonymizer.Anonymizer;
15 |
16 | public class ToLowerAnonymizer implements Anonymizer {
17 |
18 | @Override
19 | public String getType() {
20 | return "TO_LOWER_CASE";
21 | }
22 |
23 | @Override
24 | public Synonym anonymize(Object from, int size) {
25 | StringSynonym s = new StringSynonym();
26 | s.setFrom(from);
27 | s.setTo(((String)from).toLowerCase());
28 | return s;
29 | }
30 | }
31 | ```
32 |
33 | Now create a `.class` file with the command: `javac -classpath ./libraries/anonimatron-[version].jar my/package/ToLowerAnonymizer.java`, and a `.jar` file with `jar cvf toloweranonymizer.jar`. Move this `.jar` file into the `anonymizers` folder, and you are ready to user `TO_LOWER_CASE` in your config.xml as seen below.
34 |
35 | If you need an anonymizer with parameters, you can define it like so:
36 |
37 | ```java
38 | package my.packager;
39 |
40 | import java.util.HashMap;
41 | import java.util.Map;
42 |
43 | import com.rolfje.anonimatron.synonyms.StringSynonym;
44 | import com.rolfje.anonimatron.synonyms.Synonym;
45 | import com.rolfje.anonimatron.anonymizer.Anonymizer;
46 |
47 | public class FixedValueAnonymizer implements Anonymizer {
48 | @Override
49 | public Synonym anonymize(Object from, int size, boolean shortlived) {
50 | return anonymize(from, size, shortlived, new HashMap<>());
51 | }
52 |
53 | @Override
54 | public Synonym anonymize(Object from, int size, boolean shortlived, Map parameters) {
55 | if (parameters == null || !parameters.containsKey("value")) {
56 | throw new UnsupportedOperationException("no value");
57 | }
58 | return new StringSynonym(getType(),
59 | (String) from,
60 | parameters.get("value"),
61 | shortlived);
62 | }
63 |
64 | @Override
65 | public String getType() {
66 | return "FIXED";
67 | }
68 | }
69 |
70 | ```
71 |
72 | This is how you add it to your config.xml:
73 |
74 | ```xml
75 |
76 |
78 | my.package.ToLowerAnonymizer
79 | my.package.FixedValueAnonymizer
80 |
81 |
82 |
83 | testValue
84 |
85 |
86 |
87 | ```
88 |
89 | Have fun experimenting!
90 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # GDPR compliant testing.
2 |
3 | Did you ever have that problem where you needed "production data" to find a bug
4 | or do performance tests outside of the client’s production environment? Are
5 | you worried about protecting that data? Do you add screenshots to your bug
6 | reports? Can you live with surrogate data with the same properties? Then this
7 | tool is for you.
8 |
9 |
25 |
26 |
27 | # Features
28 |
29 | - Anonymize data in databases and files.
30 | - Generates fake email addresses, fake Roman names, and UUID’s out of the box.
31 | - Easy to configure, automatically generates example config file.
32 | - Anonymized data is consistent between runs. No need to re-write your tests
33 | to handle random data.
34 | - Extendable, easily implement and add your own anonymization handlers
35 | - 100% Java 1.8, multi platform, runs on Windows, Mac OSX, Linux derivatives.
36 | - Multi database, uses SQL92 standards and supports Oracle, PostgreSQL and
37 | MySQL out of the box. Anonimatron will autodetect the following JDBC drivers:
38 | DB2, MsSQL, Cloudscape, Pointbase, Firebird, IDS, Informix, Enhydra,
39 | Interbase, Hypersonic, jTurbo, SQLServer and Sybase.
40 | - Available as library at
41 | [Maven Central](https://search.maven.org/search?q=g:%22com.rolfje.anonimatron%22%20AND%20a:%22anonimatron%22).
42 | - 100% free of charge
43 |
44 |
45 | # Open Source
46 |
47 | Anonimatron is an open source project, and gets better with your help. Do you
48 | think you have written an interesting extension, do you want to write useful
49 | documentation, or do you have other suggestions? Please let us now by
50 | [filing a feature request, a bug report](https://github.com/realrolfje/anonimatron/issues), or [join the project at github.com](https://github.com/realrolfje/anonimatron).
51 |
52 | # DISCLAIMER
53 | Even if properly
54 | configured, the output of the Anonimatron anonymization process may contain
55 | certain (statistical) properties of the input dataset. That means that even after
56 | Anonymization, you should take reasonable care in protecting the Anonymized
57 | data to prevent leaks.
58 |
--------------------------------------------------------------------------------
/src/test/java/com/rolfje/anonimatron/anonymizer/AnonymizerServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.rolfje.anonimatron.anonymizer;
2 |
3 | import com.rolfje.anonimatron.configuration.Column;
4 | import com.rolfje.anonimatron.synonyms.Synonym;
5 | import junit.framework.TestCase;
6 |
7 | import java.sql.Date;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class AnonymizerServiceTest extends TestCase {
15 | AnonymizerService anonService;
16 |
17 | protected void setUp() throws Exception {
18 | anonService = new AnonymizerService();
19 | anonService.registerAnonymizers(
20 | Collections.singletonList(
21 | FixedValueAnonymizer.class.getName()));
22 | }
23 |
24 | public void testStringAnonymizer() {
25 | List