org.seedstack.seed.core.internal.data.DataManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.seedstack.seed.core.internal.data.DataManagerImpl.java

Source

/**
 * Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.seed.core.internal.data;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.collect.Lists;
import com.google.inject.Injector;
import org.seedstack.seed.DataExporter;
import org.seedstack.seed.DataImporter;
import org.seedstack.seed.DataManager;
import org.seedstack.seed.SeedException;

import javax.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Implementation of the {@link DataManager}.
 */
class DataManagerImpl implements DataManager {
    private static final String UTF_8 = "UTF-8";
    private static final String DATA_SET = "dataSet";
    private static final String IMPORTER_CLASS = "importerClass";
    private static final String GROUP = "group";
    private static final String NAME = "name";
    private static final String ITEMS = "items";
    private static final String CLASSES_MAP_KEY = "%s:%s";

    @Inject
    private Map<String, Map<String, DataExporterDefinition<Object>>> allDataExporters;

    @Inject
    private Map<String, Map<String, DataImporterDefinition<Object>>> allDataImporters;

    @Inject
    private Injector injector;

    private final JsonFactory jsonFactory;
    private final ObjectMapper objectMapper;

    DataManagerImpl() {
        this.jsonFactory = new JsonFactory();
        this.objectMapper = new ObjectMapper();
        this.jsonFactory.setCodec(this.objectMapper);
    }

    @Override
    public void exportData(OutputStream outputStream, String group) {
        Map<String, DataExporterDefinition<Object>> dataExporterDefinitions = allDataExporters.get(group);

        if (dataExporterDefinitions == null) {
            throw SeedException.createNew(DataErrorCode.NO_EXPORTER_FOUND).put(DATA_SET, group);
        }

        List<DataSetMarker<Object>> allIterators = new ArrayList<>();
        for (DataExporterDefinition<Object> dataExporterDefinition : dataExporterDefinitions.values()) {
            allIterators
                    .add(new DataSetMarker<>(dataExporterDefinition.getGroup(), dataExporterDefinition.getName(),
                            injector.getInstance(dataExporterDefinition.getDataExporterClass()).exportData()));
        }

        dumpAll(allIterators, outputStream);
    }

    @Override
    public void exportData(OutputStream outputStream, String group, String name) {
        Map<String, DataExporterDefinition<Object>> dataExporterDefinitionMap = allDataExporters.get(group);

        if (dataExporterDefinitionMap == null) {
            throw SeedException.createNew(DataErrorCode.NO_EXPORTER_FOUND).put(DATA_SET,
                    String.format(CLASSES_MAP_KEY, group, name));
        }

        DataExporterDefinition<Object> dataExporterDefinition = dataExporterDefinitionMap.get(name);

        if (dataExporterDefinition == null) {
            throw SeedException.createNew(DataErrorCode.NO_EXPORTER_FOUND).put(DATA_SET,
                    String.format(CLASSES_MAP_KEY, group, name));
        }

        dumpAll(Lists
                .newArrayList(new DataSetMarker(dataExporterDefinition.getGroup(), dataExporterDefinition.getName(),
                        injector.getInstance(dataExporterDefinition.getDataExporterClass()).exportData())),
                outputStream);
    }

    @Override
    public void exportData(OutputStream outputStream) {
        List<DataSetMarker<Object>> dataSets = new ArrayList<>();

        for (Map<String, DataExporterDefinition<Object>> dataExporterDefinitionMap : allDataExporters.values()) {
            for (DataExporterDefinition<Object> dataExporterDefinition : dataExporterDefinitionMap.values()) {
                DataExporter<Object> dataExporter = injector
                        .getInstance(dataExporterDefinition.getDataExporterClass());
                dataSets.add(new DataSetMarker<>(dataExporterDefinition.getGroup(),
                        dataExporterDefinition.getName(), dataExporter.exportData()));
            }
        }

        dumpAll(dataSets, outputStream);
    }

    @Override
    public void importData(InputStream inputStream, String acceptGroup, String acceptName, boolean clear) {
        Set<DataImporter<Object>> usedDataImporters = new HashSet<>();

        try {
            ParsingState state = ParsingState.START;
            String group = null;
            String name = null;
            JsonParser jsonParser = this.jsonFactory
                    .createParser(new InputStreamReader(inputStream, Charset.forName(UTF_8)));
            JsonToken jsonToken = jsonParser.nextToken();

            while (jsonToken != null) {
                switch (state) {
                case START:
                    if (jsonToken == JsonToken.START_ARRAY) {
                        state = ParsingState.DEFINITION_START;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "start array expected");
                    }

                    break;
                case DEFINITION_START:
                    if (jsonToken == JsonToken.START_OBJECT) {
                        state = ParsingState.DEFINITION_GROUP;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "start object expected");
                    }

                    break;
                case DEFINITION_GROUP:
                    if (jsonToken == JsonToken.FIELD_NAME && GROUP.equals(jsonParser.getCurrentName())) {
                        group = jsonParser.nextTextValue();
                        state = ParsingState.DEFINITION_NAME;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "group field expected");
                    }

                    break;
                case DEFINITION_NAME:
                    if (jsonToken == JsonToken.FIELD_NAME && NAME.equals(jsonParser.getCurrentName())) {
                        name = jsonParser.nextTextValue();
                        state = ParsingState.DEFINITION_ITEMS;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "name field expected");
                    }

                    break;
                case DEFINITION_ITEMS:
                    if (jsonToken == JsonToken.FIELD_NAME && ITEMS.equals(jsonParser.getCurrentName())) {
                        usedDataImporters.add(consumeItems(jsonParser, group, name, acceptGroup, acceptName));
                        state = ParsingState.DEFINITION_END;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "items field expected");
                    }

                    break;
                case DEFINITION_END:
                    if (jsonToken == JsonToken.END_OBJECT) {
                        group = null;
                        name = null;
                        state = ParsingState.END;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "end object expected");
                    }

                    break;
                case END:
                    if (jsonToken == JsonToken.START_OBJECT) {
                        state = ParsingState.DEFINITION_GROUP;
                    } else if (jsonToken == JsonToken.END_ARRAY) {
                        state = ParsingState.START;
                    } else {
                        throwParsingError(jsonParser.getCurrentLocation(), "start object or end array expected");
                    }

                    break;
                default:
                    throwParsingError(jsonParser.getCurrentLocation(), "unexpected parser state");
                }

                jsonToken = jsonParser.nextToken();
            }
        } catch (Exception e1) {
            for (DataImporter<Object> usedDataImporter : usedDataImporters) {
                try {
                    usedDataImporter.rollback();
                } catch (Exception e2) {
                    e2.initCause(e1);
                    throw SeedException.wrap(e2, DataErrorCode.FAILED_TO_ROLLBACK_IMPORT).put(IMPORTER_CLASS,
                            usedDataImporter.getClass().getName());
                }
            }

            throw SeedException.wrap(e1, DataErrorCode.IMPORT_FAILED);
        }

        for (DataImporter<Object> usedDataImporter : usedDataImporters) {
            try {
                usedDataImporter.commit(clear);
            } catch (Exception e) {
                throw SeedException.wrap(e, DataErrorCode.FAILED_TO_COMMIT_IMPORT).put(IMPORTER_CLASS,
                        usedDataImporter.getClass().getName());
            }
        }
    }

    private void throwParsingError(JsonLocation jsonLocation, String message) {
        throw SeedException.createNew(DataErrorCode.FAILED_TO_PARSE_DATA_STREAM).put("parsingError", message)
                .put("line", jsonLocation.getLineNr()).put("col", jsonLocation.getColumnNr())
                .put("offset", jsonLocation.getCharOffset());
    }

    private DataImporter<Object> consumeItems(JsonParser jsonParser, String group, String name, String acceptGroup,
            String acceptName) throws IOException {

        Map<String, DataImporterDefinition<Object>> dataImporterDefinitionMap = allDataImporters.get(group);

        if (dataImporterDefinitionMap == null) {
            throw SeedException.createNew(DataErrorCode.NO_IMPORTER_FOUND).put(GROUP, group).put(NAME, name);
        }

        DataImporterDefinition<Object> currentImporterDefinition = dataImporterDefinitionMap.get(name);

        if (currentImporterDefinition == null) {
            throw SeedException.createNew(DataErrorCode.NO_IMPORTER_FOUND).put(GROUP, group).put(NAME, name);
        }

        if (!group.equals(currentImporterDefinition.getGroup())) {
            throw SeedException.createNew(DataErrorCode.UNEXPECTED_DATA_TYPE)
                    .put(DATA_SET, String.format(CLASSES_MAP_KEY, group, name))
                    .put(IMPORTER_CLASS, currentImporterDefinition.getDataImporterClass().getName());
        }

        if (!name.equals(currentImporterDefinition.getName())) {
            throw SeedException.createNew(DataErrorCode.UNEXPECTED_DATA_TYPE)
                    .put(DATA_SET, String.format(CLASSES_MAP_KEY, group, name))
                    .put(IMPORTER_CLASS, currentImporterDefinition.getDataImporterClass().getName());
        }

        DataImporter<Object> currentDataImporter = null;
        if ((acceptGroup == null || acceptGroup.equals(group)) && (acceptName == null || acceptName.equals(name))) {

            currentDataImporter = injector.getInstance(currentImporterDefinition.getDataImporterClass());

            // Check if items contains an array

            if (jsonParser.nextToken() != JsonToken.START_ARRAY) {
                throw new IllegalArgumentException("Items should be an array");
            }

            jsonParser.nextToken();

            // If the array is not empty consume it
            if (jsonParser.getCurrentToken() != JsonToken.END_ARRAY) {
                Iterator<Object> objectIterator = jsonParser
                        .readValuesAs(currentImporterDefinition.getImportedClass());

                while (objectIterator.hasNext()) {
                    currentDataImporter.importData(objectIterator.next());
                }

                // The array should end correctly
                if (jsonParser.getCurrentToken() != JsonToken.END_ARRAY) {
                    throw new IllegalArgumentException("end array expected");
                }
            }
        }

        // the data importer containing the data
        return currentDataImporter;
    }

    @Override
    public boolean isInitialized(String group, String name) {
        Map<String, DataImporterDefinition<Object>> dataImporterDefinitionMap = allDataImporters.get(group);

        if (dataImporterDefinitionMap == null) {
            throw SeedException.createNew(DataErrorCode.NO_IMPORTER_FOUND).put(GROUP, group).put(NAME, name);
        }

        DataImporterDefinition<Object> dataImporterDefinition = dataImporterDefinitionMap.get(name);

        if (dataImporterDefinition == null) {
            throw SeedException.createNew(DataErrorCode.NO_IMPORTER_FOUND).put(GROUP, group).put(NAME, name);
        }

        DataImporter<Object> dataImporter = injector.getInstance(dataImporterDefinition.getDataImporterClass());
        return dataImporter.isInitialized();
    }

    private void dumpAll(List<DataSetMarker<Object>> dataSetMarker, OutputStream outputStream) {
        try {
            JsonGenerator jsonGenerator = this.jsonFactory
                    .createGenerator(new OutputStreamWriter(outputStream, Charset.forName(UTF_8)));
            ObjectWriter objectWriter = objectMapper.writer();

            jsonGenerator.writeStartArray();

            for (DataSetMarker<Object> objectDataSetMarker : dataSetMarker) {
                // start
                jsonGenerator.writeStartObject();

                // metadata
                jsonGenerator.writeStringField(GROUP, objectDataSetMarker.getGroup());
                jsonGenerator.writeStringField(NAME, objectDataSetMarker.getName());

                // items
                jsonGenerator.writeArrayFieldStart(ITEMS);
                while (objectDataSetMarker.getItems().hasNext()) {
                    objectWriter.writeValue(jsonGenerator, objectDataSetMarker.getItems().next());
                }
                jsonGenerator.writeEndArray();

                // end
                jsonGenerator.writeEndObject();
            }

            jsonGenerator.writeEndArray();

            jsonGenerator.flush();
        } catch (Exception e) {
            throw SeedException.wrap(e, DataErrorCode.EXPORT_FAILED);
        }
    }

    private enum ParsingState {
        START, DEFINITION_START, DEFINITION_GROUP, DEFINITION_NAME, DEFINITION_ITEMS, DEFINITION_END, END
    }
}