class DefaultAtlasContext implements AtlasContext, AtlasContextMXBean {

    @Override
    public void process(AtlasSession userSession) throws AtlasException {
        if (!(userSession instanceof DefaultAtlasSession)) {
            throw new AtlasException(String.format("Unsupported session class '%s'", userSession.getClass().getName()));
        }
        if (!this.equals(userSession.getAtlasContext())) {
            throw new AtlasException("Cannot execute AtlasSession created by the other AtlasContext");
        }

        DefaultAtlasSession session = (DefaultAtlasSession) userSession;
        if (LOG.isDebugEnabled()) {
            LOG.debug("Begin process {}", (session == null ? null : session.toString()));
        }

        session.head().unset();
        session.getAudits().getAudit().clear();
        session.getValidations().getValidation().clear();

        // TODO https://github.com/atlasmap/atlasmap/issues/863 - Add an option to enable/disable runtime validation
        processValidation(session);
        for (Validation v : session.getValidations().getValidation()) {
            AtlasUtil.addAudit(session, v);
        }

        // Additional runtime only audit
        Mappings mappings = session.getMapping().getMappings();
        if (mappings != null && mappings.getMapping().isEmpty()) {
            AtlasUtil.addAudit(session, null,
                String.format("Field mappings should not be empty"),
                null, AuditStatus.WARN, null);
        }

        session.getValidations().getValidation().clear();
        if (session.hasErrors()) {
            if (LOG.isDebugEnabled()) {
                LOG.error("Aborting due to {} errors in pre-validation", session.errorCount());
            }
            return;
        }

        for (AtlasModule module : getSourceModules().values()) {
            module.processPreSourceExecution(session);
        }
        for (AtlasModule module : getTargetModules().values()) {
            module.processPreTargetExecution(session);
        }

        if (session.hasErrors()) {
            if (LOG.isDebugEnabled()) {
                LOG.error("Aborting due to {} errors in pre-execution", session.errorCount());
            }
            return;
        }

        for (BaseMapping baseMapping : session.getMapping().getMappings().getMapping()) {
            for (Mapping mapping : unwrapCollectionMappings(session, baseMapping)) {
                session.head().setMapping(mapping).setLookupTable(lookupTables.get(mapping.getLookupTableName()));

                if (mapping.getOutputField() == null || mapping.getOutputField().isEmpty()) {
                    AtlasUtil.addAudit(session, null,
                            String.format("Mapping does not contain at least one target field: alias=%s desc=%s",
                                    mapping.getAlias(), mapping.getDescription()),
                            null, AuditStatus.WARN, null);
                    continue;
                }

                try {
                    if (mapping.getInputFieldGroup() != null) {
                        if (mapping.getExpression() != null) {
                            session.head().setSourceField(mapping.getInputFieldGroup());
                            DefaultAtlasExpressionProcessor.processExpression(session, mapping.getExpression());
                        } else {
                            processSourceFieldGroup(session, mapping.getInputFieldGroup());
                        }
                    } else if (mapping.getInputField() != null && !mapping.getInputField().isEmpty()) {
                        if (mapping.getExpression() != null) {
                            FieldGroup group = new FieldGroup();
                            group.getField().addAll(mapping.getInputField());
                            session.head().setSourceField(group);
                            DefaultAtlasExpressionProcessor.processExpression(session, mapping.getExpression());
                        } else {
                            applyCopyToActions(mapping.getInputField(), mapping);
                            processSourceFieldMappings(session, mapping.getInputField());
                        }
                    } else {
                        session.head().addAudit(AuditStatus.WARN, null, null, String.format(
                            "Mapping does not contain expression or at least one source field: alias=%s desc=%s",
                            mapping.getAlias(), mapping.getDescription()));
                    }
                } catch (Exception t) {
                    Field sourceField = session.head().getSourceField();
                    String docId = sourceField != null ? sourceField.getDocId() : null;
                    String path =  sourceField != null ? sourceField.getPath() : null;
                    session.head().addAudit(AuditStatus.ERROR, docId, path, String.format(
                            "Unexpected exception is thrown while reading source field: %s", t.getMessage()));
                    if (LOG.isDebugEnabled()) {
                        LOG.error("", t);
                    }
                }

                if (!session.head().hasError()) {
                    try {
                        processTargetFieldMappings(session, mapping);
                    } catch (Exception t) {
                        Field targetField = session.head().getTargetField();
                        String docId = targetField != null ? targetField.getDocId() : null;
                        String path = targetField != null ? targetField.getPath() : null;
                        session.head().addAudit(AuditStatus.ERROR, docId, path, String.format(
                                "Unexpected exception is thrown while populating target field: %s", t.getMessage()));
                        if (LOG.isDebugEnabled()) {
                            LOG.error("", t);
                        }
                    }
                }
                session.getAudits().getAudit().addAll(session.head().getAudits());
                session.head().unset();
            }
        }

        for (AtlasModule module : getSourceModules().values()) {
            module.processPostValidation(session);
        }
        for (AtlasModule module : getTargetModules().values()) {
            module.processPostValidation(session);
        }

        for (AtlasModule module : getSourceModules().values()) {
            module.processPostSourceExecution(session);
        }
        for (AtlasModule module : getTargetModules().values()) {
            module.processPostTargetExecution(session);
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("End process {}", session == null ? null : session.toString());
        }
    }

    public  DefaultAtlasContext(URI atlasMappingUri);
    public  DefaultAtlasContext(DefaultAtlasContextFactory factory, URI atlasMappingUri);
    public  DefaultAtlasContext(DefaultAtlasContextFactory factory, AtlasMapping mapping);
    public  DefaultAtlasContext(DefaultAtlasContextFactory factory,
            AtlasContextFactory.Format format, InputStream stream);

}