@Override
    public Map<String, String> run(final Session<?> session) throws BackgroundException {
        final Metadata feature = session.getFeature(Metadata.class);
        if(log.isDebugEnabled()) {
            log.debug(String.format("Run with feature %s", feature));
        }
        // Map for metadata entry key > File & Metadata Values
        final Map<String, Map<Path, String>> graphMetadata = new HashMap<>();
        for(Path next : files) {
            // Read online metadata
            next.attributes().setMetadata(feature.getMetadata(next));
            // take every entry of current metadata and store it in metaGraph
            for(Map.Entry<String, String> entry : next.attributes().getMetadata().entrySet()) {
                if(graphMetadata.containsKey(entry.getKey())) {
                    // if existing, get map, put value
                    graphMetadata.get(entry.getKey()).put(next, entry.getValue());
                }
                else {
                    // if not existent create hashmap and put it back
                    Map<Path, String> map = new HashMap<>();
                    graphMetadata.put(entry.getKey(), map);
                    map.put(next, entry.getValue());
                }
            }
        }
        // Store result metadata in hashmap
        Map<String, String> metadata = new HashMap<>();
        for(Map.Entry<String, Map<Path, String>> entry : graphMetadata.entrySet()) {
            if(entry.getValue().size() != files.size()) {
                metadata.put(entry.getKey(), null);
            }
            else {
                // single use of streams, reason: distinct is easier in Streams than it would be writing it manually
                Stream<String> values = entry.getValue().values().stream().distinct();
                // Use reducing collector, that returns null on non-unique values
                final String value = values.collect(Collectors.reducing((a, v) -> null)).orElse(null);
                // store it
                metadata.put(entry.getKey(), value);
            }
        }
        return metadata;
    }
@Test
    public void testRun() throws Exception {
        final List<Path> files = new ArrayList<>();
        files.add(new Path("a", EnumSet.of(Path.Type.file)));
        files.add(new Path("b", EnumSet.of(Path.Type.file)));
        files.add(new Path("c", EnumSet.of(Path.Type.file)));
        ReadMetadataWorker worker = new ReadMetadataWorker(files) {
            @Override
            public void cleanup(final Map<String, String> result) {
                fail();
            }
        };
        final Map<String, String> map = worker.run(new NullSession(new Host(new TestProtocol())) {
            @Override
            @SuppressWarnings("unchecked")
            public <T> T _getFeature(final Class<T> type) {
                if(type == Metadata.class) {
                    return (T) new Metadata() {
                        @Override
                        public Map<String, String> getDefault(final Local local) {
                            return Collections.emptyMap();
                        }

                        @Override
                        public Map<String, String> getMetadata(final Path file) {
                            final HashMap<String, String> map = new HashMap<>();
                            switch(file.getName()) {
                                case "a":
                                    map.put("key1", "v1");
                                    map.put("key2", "v");
                                    map.put("key3", "v");
                                    return map;
                                case "b":
                                    map.put("key2", "v");
                                    map.put("key3", "v");
                                    return map;
                                case "c":
                                    map.put("key2", "v2");
                                    map.put("key3", "v");
                                    return map;
                                default:
                                    fail();
                                    break;
                            }
                            return map;
                        }

                        @Override
                        public void setMetadata(final Path file, final TransferStatus status) {
                            throw new UnsupportedOperationException();
                        }
                    };
                }
                return super._getFeature(type);
            }
        });
        assertTrue(map.containsKey("key1"));
        assertTrue(map.containsKey("key2"));
        assertNull(map.get("key1"));
        assertNull(map.get("key2"));
        assertNotNull(map.get("key3"));
    }