org.solmix.datax.support.BaseDataService.java Source code

Java tutorial

Introduction

Here is the source code for org.solmix.datax.support.BaseDataService.java

Source

/*
 * Copyright 2014 The Solmix Project
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */
package org.solmix.datax.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.map.LinkedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.solmix.commons.collections.DataTypeMap;
import org.solmix.commons.util.Assert;
import org.solmix.commons.util.DataUtils;
import org.solmix.commons.util.Reflection;
import org.solmix.commons.util.StringUtils;
import org.solmix.commons.util.TransformUtils;
import org.solmix.datax.DATAX;
import org.solmix.datax.DSCallException;
import org.solmix.datax.DSRequest;
import org.solmix.datax.DSResponse;
import org.solmix.datax.DSResponse.Status;
import org.solmix.datax.DataService;
import org.solmix.datax.DataServiceNoFoundException;
import org.solmix.datax.DataxException;
import org.solmix.datax.OperationNoFoundException;
import org.solmix.datax.call.DSCall;
import org.solmix.datax.call.DSCallFactory;
import org.solmix.datax.call.DSCallTemplateContext;
import org.solmix.datax.call.DSCallUtils;
import org.solmix.datax.model.BatchOperations;
import org.solmix.datax.model.DataServiceInfo;
import org.solmix.datax.model.FieldInfo;
import org.solmix.datax.model.FieldType;
import org.solmix.datax.model.ForwardInfo;
import org.solmix.datax.model.LookupType;
import org.solmix.datax.model.MergedType;
import org.solmix.datax.model.OperationInfo;
import org.solmix.datax.model.OperationType;
import org.solmix.datax.model.ParamInfo;
import org.solmix.datax.model.TransactionPolicy;
import org.solmix.datax.model.TransformerInfo;
import org.solmix.datax.model.ValidatorInfo;
import org.solmix.datax.script.VelocityExpression;
import org.solmix.datax.transformer.Transformer;
import org.solmix.datax.transformer.TransformerException;
import org.solmix.datax.util.DataTools;
import org.solmix.datax.validation.BuiltinCreator;
import org.solmix.datax.validation.DefaultValidatorService;
import org.solmix.datax.validation.ErrorMessage;
import org.solmix.datax.validation.ValidationContext;
import org.solmix.datax.validation.ValidationCreator;
import org.solmix.datax.validation.ValidationEvent;
import org.solmix.datax.validation.ValidationEvent.Level;
import org.solmix.datax.validation.ValidationEventFactory;
import org.solmix.datax.validation.ValidationException;
import org.solmix.runtime.Container;
import org.solmix.runtime.bean.ConfiguredBeanProvider;
import org.solmix.runtime.event.EventService;
import org.solmix.runtime.event.TimeMonitorEvent;
import org.solmix.runtime.i18n.ResourceBundleManager;
import org.solmix.runtime.io.CachedOutputStream;
import org.solmix.runtime.resource.ResourceInjector;
import org.solmix.runtime.resource.ResourceManager;
import org.solmix.runtime.resource.support.ResourceManagerImpl;
import org.solmix.service.template.TemplateService;
import org.w3c.dom.Element;

/**
 * 
 * @author solmix.f@gmail.com
 * @version $Id$  201573
 */

public class BaseDataService implements DataService {
    public static final String PARAM_VELOCITY = VelocityExpression.class.getName() + ".param";
    public static final String PARAM_VELOCITY_CONTEXT = VelocityExpression.class.getName() + ".paramCtx";
    private static final Logger LOG = LoggerFactory.getLogger(BaseDataService.class);
    private static final Logger VALIDATION = LoggerFactory.getLogger(DATAX.VALIDATION_LOGNAME);
    private Container container;
    private DataTypeMap properties;
    private DataServiceInfo info;

    private DSCallFactory dsCallFactory;

    private EventService eventService;

    public BaseDataService(DataServiceInfo info, Container container, DataTypeMap prop) {
        Assert.assertNotNull(info);
        setContainer(container);
        this.info = info;
        this.properties = prop;
        init();
    }

    protected void setContainer(Container container) {
        Assert.assertNotNull(container);
        this.container = container;
    }

    protected void init() {
        if (LOG.isTraceEnabled()) {
            LOG.trace((new StringBuilder()).append("Creating instance of DataSource '").append(info.getId())
                    .append("'").toString());
        }
    }

    /**
     * 
     */
    @Override
    public void freeResources() {

    }

    @Override
    public String getId() {
        return info.getId();
    }

    @Override
    public String getServerType() {
        return BaseDataServiceFactory.BASE;
    }

    @Override
    public DSResponse execute(DSRequest req) throws DSCallException {
        if (req == null) {
            return null;
        }
        req.registerFreeResourcesHandler(this);
        if (req.getDataService() == null && req.getDataServiceId() == null) {
            req.setDataService(this);
        }

        OperationInfo oi = info.getOperationInfo(req.getOperationId());
        if (oi == null) {
            throw new OperationNoFoundException(
                    "Not found operation" + req.getOperationId() + " in Dataservice:" + getId());
        }
        if (oi.getBatch() != null) {
            DSResponse res = executeBatch(req, oi);
            List<ForwardInfo> forwards = oi.getBatch().getForwards();
            if (forwards == null) {
                return res;
            } else {
                return forwardToTemplate(res, req, forwards);
            }
        }
        //?
        Map<String, ParamInfo> params = oi.getParams();
        if (params != null && !params.isEmpty()) {
            prepareParams(req, params);
        }
        //?
        List<TransformerInfo> trans = oi.getTransformers();
        List<Transformer> transformers = prepareTransformer(oi, req, trans);
        if (transformers != null && !transformers.isEmpty()) {
            transformRequest(req, transformers);
        }
        //?
        OperationType type = oi.getType();
        DSResponse response = null;
        //??
        if (type != OperationType.CUSTOM) {
            response = validateDSRequest(req);
            if (response != null) {
                return transformResponse(response, transformers, req, oi);
            }
        }
        //???
        if (oi.getRedirect() != null) {
            Object redirect = evaluteExpression(oi.getRedirect(), req);
            if (redirect != null) {
                if (req.setOperationId(redirect.toString())) {
                    return req.execute();
                }
            }
        }

        req.setRequestStarted(true);
        if (LOG.isDebugEnabled()) {
            LOG.debug("RawValues==>> {}", req.getRawValues());
        }
        // ?invoker?
        if ((!req.isInvoked()) && oi.getInvoker() != null) {
            response = DMIDataService.execute(container, req, req.getDSCall());
            return transformResponse(response, transformers, req, oi);
        } else {
            response = executeDefault(req, type);
            return transformResponse(response, transformers, req, oi);
        }
    }

    /**?forward ????,?rawData
     * @throws DSCallException */
    protected DSResponse forwardToTemplate(DSResponse res, DSRequest req, List<ForwardInfo> forwards)
            throws DSCallException {
        TemplateService templateService = container.getExtension(TemplateService.class);
        Assert.assertNotNull(templateService, "TemplateService is null");
        String forward = res.getForward();
        ForwardInfo selected = null;
        if (forward == null) {
            if (forwards.size() == 1) {
                selected = forwards.get(0);
            } else {
                selected = forwards.get(0);
                LOG.warn("Multi forward with null forward");
            }

        } else {
            for (ForwardInfo i : forwards) {
                if (forward.equals(i.getName())) {
                    selected = i;
                    break;
                }
            }
        }
        if (selected == null) {
            throw new ForwardException("No found forwardinfo for :" + forward);
        }
        DSCallTemplateContext context = new DSCallTemplateContext(container, req, res);
        CachedOutputStream outputSteam = new CachedOutputStream();
        try {
            String tempalte = selected.getPath();
            templateService.evaluate(tempalte, context, outputSteam);
            res.setRawData(outputSteam);
            //Content-Type
            String type = selected.getContentType();
            if (type == null) {
                //html
                type = DATAX.TEMPLATE_CONTENT_TYPE_DEFAULT;
            }
            res.setAttribute("Content-Type", type);

        } catch (Exception e) {
            throw new DSCallException("evaluate template error:", e);
        }
        return res;
    }

    /**
     * ??Transformers
     */
    private List<Transformer> prepareTransformer(OperationInfo oi, DSRequest req, List<TransformerInfo> trans) {
        List<Transformer> transformers = null;
        if (trans != null) {
            transformers = new ArrayList<Transformer>();
            for (TransformerInfo tran : trans) {
                Transformer transformer = null;
                LookupType type = tran.getLookup();
                String serviceName = tran.getName();
                Class<? extends Transformer> serviceClass = tran.getClazz();
                if (type == LookupType.CONTAINER) {
                    if (serviceName == null) {
                        if (serviceClass == null || serviceClass == Transformer.class) {
                            //validator
                            throw new ValidationException(
                                    "transformer must specify name to determine validator service ");
                        } else {
                            transformer = container.getExtension(serviceClass);
                        }

                    } else {
                        ConfiguredBeanProvider provider = container.getExtension(ConfiguredBeanProvider.class);
                        if (provider != null) {
                            transformer = provider.getBeanOfType(serviceName,
                                    serviceClass == null ? Transformer.class : serviceClass);
                        }
                    }
                } else if (serviceClass != null && type == LookupType.NEW) {
                    try {
                        transformer = Reflection.newInstance(serviceClass);
                    } catch (Exception e) {
                        throw new ValidationException("Instance object", e);
                    }
                }
                if (transformer != null) {
                    ResourceManager rma = container.getExtension(ResourceManager.class);
                    ResourceManagerImpl rm = new ResourceManagerImpl(rma.getResourceResolvers());
                    rm.addResourceResolver(new RequestContextResourceResolver(req.getRequestContext()));
                    rm.addResourceResolver(new DSRequestResolver(req));
                    ResourceInjector injector = new ResourceInjector(rm);
                    injector.inject(transformer);
                    transformer.init(tran);
                    transformers.add(transformer);

                }
            }
        }
        customTransformer(transformers, req, oi);
        return transformers;
    }

    protected void customTransformer(List<Transformer> transformers, DSRequest req, OperationInfo oi) {

    }

    protected void transformRequest(DSRequest req, List<Transformer> trans) {
        for (Transformer transformer : trans) {
            try {
                Object transformedValues = transformer.transformRequest(req.getRawValues(), req);
                req.setRawValues(transformedValues);
            } catch (Exception e) {
                throw new TransformerException("Transformer DSRequest", e);
            }
        }
    }

    protected DSResponse transformResponse(DSResponse response, List<Transformer> transformers, DSRequest req,
            OperationInfo oi) throws DSCallException {

        List<ForwardInfo> forwards = oi.getForwards();

        if (transformers != null && transformers.size() > 0) {
            for (Transformer transformer : transformers) {
                try {
                    Object transformedObject = transformer.transformResponse(response.getRawData(), response, req);
                    response.setRawData(transformedObject);
                } catch (Exception e) {
                    throw new TransformerException("Transformer DSRequest", e);
                }
            }
        }

        if (forwards != null && forwards.size() > 0) {
            response = forwardToTemplate(response, req, forwards);
        }
        return response;
    }

    protected DSRequest prepareParams(DSRequest req, Map<String, ParamInfo> params) throws DSCallException {
        Object values = req.getRawValues();
        // ???
        if (values == null) {
            values = new HashMap<Object, Object>();
        }
        List<?> list = null;
        try {
            if (values instanceof List<?> && values.getClass().isArray()) {
                list = req.getValueSets();
                List<Object> afters = new ArrayList<Object>();
                for (Object o : list) {
                    afters.add(injectParma(o, params, req));
                }
                req.setRawValues(afters);
            } else {
                req.setRawValues(injectParma(values, params, req));
            }
        } catch (Exception e) {
            throw new DSCallException("Prepare params error", e);
        }

        return req;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Object injectParma(Object values, Map<String, ParamInfo> params, DSRequest req) throws Exception {
        Map map;
        boolean isMap = false;
        if (values instanceof Map) {
            map = (Map) values;
            isMap = true;
        } else {
            map = TransformUtils.transformType(Map.class, values);
        }
        for (String name : params.keySet()) {
            ParamInfo param = params.get(name);
            if (map.containsKey(name)) {
                if (param.isOverride()) {
                    map.put(name, getParamValue(param, req));
                }
            } else {
                Object o = getParamValue(param, req);
                if (o != null) {
                    map.put(name, o);
                }
            }
        }
        if (!isMap) {
            return DataUtils.setProperties(map, values);
        } else {
            return map;
        }

    }

    protected Object getParamValue(ParamInfo param, DSRequest req) {
        if (param == null) {
            return null;
        }
        Object o = param.getValue();
        if (o != null) {
            return o.toString().trim();
        }
        String expre = StringUtils.trimToNull(param.getExpression());
        if (expre != null) {
            return evaluteExpression(expre, req);
        }
        return null;
    }

    protected Object evaluteExpression(String expression, DSRequest req) {
        VelocityExpression velocity = (VelocityExpression) req.getAttribute(PARAM_VELOCITY);
        @SuppressWarnings("unchecked")
        Map<String, Object> vcontext = (Map<String, Object>) req.getAttribute(PARAM_VELOCITY_CONTEXT);
        if (velocity == null) {
            velocity = new VelocityExpression(container);
            vcontext = velocity.prepareContext(req, req.getRequestContext());
            req.setAttribute(PARAM_VELOCITY, velocity);
            req.setAttribute(PARAM_VELOCITY_CONTEXT, vcontext);
        }
        Object o = velocity.evaluateValue(expression, vcontext);
        if (o != null && o instanceof String) {
            return StringUtils.trimToNull((String) o);
        } else {
            return o;
        }
    }

    /**
     * ?.
     * 
     * @param req
     * @return
     * @throws DataxException 
     */
    protected DSResponse executeBatch(DSRequest req, OperationInfo oi) throws DSCallException {
        BatchOperations bos = oi.getBatch();
        MergedType merged = bos.getMergedType();
        TransactionPolicy policy = bos.getTransactionPolicy();
        DSCall old = DSCallUtils.getDSCall();
        try {
            DSCall newdsc = dsCallFactory.createDSCall(policy);
            DSCallUtils.setDSCall(newdsc);
            for (OperationInfo op : bos.getOperations()) {
                DSRequest request = createNewRequest(req, op);
                newdsc.execute(request);
            }
            return newdsc.getMergedResponse(merged);
        } finally {
            DSCallUtils.setDSCall(old);
        }
    }

    /**
     * ?OperationInfo??request
     * 
     * @param req
     * @param op
     * @return
     */
    protected DSRequest createNewRequest(DSRequest req, OperationInfo op) {
        DSRequestImpl newreq = ((DSRequestImpl) req).clone();
        newreq.setOperationId(op.getId());
        return newreq;
    }

    /**
     * Transformer
     * 
     * @param req
     * @return
     */
    protected DSResponse executeDefault(DSRequest req, OperationType type) throws DSCallException {
        if (DataTools.isFetch(type)) {
            return executeFetch(req);
        } else if (DataTools.isRemove(type)) {
            return executeRemove(req);
        } else if (DataTools.isUpdate(type)) {
            return executeUpdate(req);
        } else if (DataTools.isAdd(type)) {
            return executeAdd(req);
        } else {
            return executeCustom(req);
        }
    }

    protected DSResponse executeCustom(DSRequest req) throws DSCallException {
        return notSupported(req);
    }

    /**
     * @param req
     * @return
     */
    protected DSResponse notSupported(DSRequest req) {
        OperationInfo oi = info.getOperationInfo(req.getOperationId());
        throw new UnsupportedOperationException(new StringBuilder().append("Default operation type '")
                .append(oi.getType()).append("' not supported by this DataSource (").append(getServerType())
                .append(")").toString());
    }

    /**
     * @param req
     * @return
     */
    protected DSResponse executeAdd(DSRequest req) throws DSCallException {
        return notSupported(req);
    }

    /**
     * @param req
     * @return
     */
    protected DSResponse executeUpdate(DSRequest req) throws DSCallException {
        return notSupported(req);
    }

    /**
     * @param req
     * @return
     */
    protected DSResponse executeRemove(DSRequest req) throws DSCallException {
        return notSupported(req);
    }

    /**
     * @param req
     * @return
     */
    protected DSResponse executeFetch(DSRequest req) throws DSCallException {
        return notSupported(req);
    }

    /**
     * @param req
     * @return
     */
    protected DSResponse validateDSRequest(DSRequest req) {
        if (req.isValidated())
            return null;
        req.setValidated(true);
        List<Object> errors = validateDSRequst(req);
        if (errors != null) {
            VALIDATION.info((new StringBuilder()).append("Validation error: ").append(errors).toString());
            DSResponse dsResponse = new DSResponseImpl(this, req);
            dsResponse.setStatus(Status.STATUS_VALIDATION_ERROR);
            dsResponse.setErrors(errors.toArray(new Object[errors.size()]));
            return dsResponse;
        } else {
            return null;
        }
    }

    /**
     * @param baseDataService
     * @param req
     * @return
     */
    @Override
    public List<Object> validateDSRequst(DSRequest req) throws ValidationException {
        if (req.getDataService() == null) {
            throw new DataServiceNoFoundException("Not found dataservice:" + req.getDataServiceId());
        }
        List<FieldInfo> fields = req.getDataService().getDataServiceInfo().getFields();
        //?Fields,??
        if (fields == null || fields.isEmpty()) {
            return null;
        }
        OperationInfo oi = req.getOperationInfo();
        Boolean validate = oi.getValidate();
        OperationType type = oi.getType();
        if (validate == null) {
            /*if(DataTools.isModificationOperation(type)){
            validate=Boolean.TRUE;
            }else{??*/
            validate = Boolean.FALSE;
            //            }
        }
        if (!validate) {
            return null;
        }
        ValidationContext vcontext = ValidationContext.instance();
        vcontext.setContainer(this.container);
        vcontext.setDSRequest(req);
        vcontext.setDSCall(req.getDSCall());
        vcontext.setRequestContext(req.getRequestContext());
        vcontext.setValidationEventFactory(ValidationEventFactory.instance());
        vcontext.setValidatorService(DefaultValidatorService.getInstance());
        vcontext.setLocale(getLocale());
        vcontext.setResourceBundleManager(getResourceBundleManager());
        //propertynull
        if (type == OperationType.UPDATE) {
            vcontext.setPropertiesOnly(true);
        }
        Object validated = validateRecords(req.getRawValues(), vcontext);
        if (oi.getUsedValidatedValues() != null && oi.getUsedValidatedValues()) {
            req.setRawValues(validated);
        } else {
            req.setAttribute(DATAX.AFTER_VALIDATE_VALUES, validated);
        }

        Map<String, Object> errors = vcontext.getErrors();
        if (errors != null)
            return new ArrayList<Object>(errors.values());
        else
            return null;
    }

    private Locale getLocale() {
        String locale = this.getConfigProperties().getString("locale", null);
        if (locale != null) {
            return new Locale(locale);
        } else {
            return Locale.getDefault();
        }
    }

    protected ResourceBundleManager getResourceBundleManager() {
        return container.getExtension(ResourceBundleManager.class);
    }

    /**
     * @param valueSets
     * @param vcontext
     */
    protected Object validateRecords(Object rdata, ValidationContext vcontext) {
        if (rdata == null) {
            return null;
        }
        long start = System.currentTimeMillis();
        Object validatedObject;
        if (rdata instanceof List<?>) {
            List<?> data = (List<?>) rdata;
            List<Object> records = new ArrayList<Object>();
            for (int i = 0; i < data.size(); i++) {
                records.add(validateRecord(data.get(i), vcontext));
            }
            long end = System.currentTimeMillis();
            String __info = new StringBuilder().append("Done validating ").append(data.size()).append(" '")
                    .append(getId()).append("'s at path '").append(vcontext.getPath()).append("': ")
                    .append(end - start).append("ms").append(
                            data.size() != 0
                                    ? (new StringBuilder()).append(" (avg ").append((end - start) / data.size())
                                            .append(")").toString()
                                    : "")
                    .toString();
            createAndFireTimeMonitorEvent(end - start, __info);
            validatedObject = records;
        } else {
            validatedObject = validateRecord(rdata, vcontext);
        }
        long end = System.currentTimeMillis();
        String __info = new StringBuilder().append("Done validating ").append("at path '")
                .append(vcontext.getPath()).append("': ").append(end - start).append("ms").toString();
        createAndFireTimeMonitorEvent(end - start, __info);
        return validatedObject;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Object validateRecord(Object data, ValidationContext vcontext) {
        if (data == null)
            return null;
        if (data instanceof Element) {
            throw new UnsupportedOperationException("data is element");
        } else if (data instanceof Map<?, ?>) {
            vcontext.addPath(getId());
            if (VALIDATION.isDebugEnabled()) {
                VALIDATION.debug((new StringBuilder()).append("Validating a '").append(getId())
                        .append("' at path '").append(vcontext.getPath()).append("'").toString());
            }
            vcontext.addToTemplateContext(ValidationContext.TEMPLATE_DATASERVICE, this);

            Map<String, Object> record = (Map<String, Object>) data;
            for (Object key : record.keySet()) {
                String fieldName = (String) key;
                FieldInfo field = info.getField(fieldName);
                Object value = record.get(fieldName);
                if (field == null) {
                    handleExtraValue(record, fieldName, value, vcontext);
                } else {
                    record.put(fieldName, validateFieldValue(record, field, value, vcontext));
                }
            }
            checkStructure(record, vcontext);
            vcontext.removePathSegment();
            return record;
        } else {
            Map records = null;
            try {
                records = TransformUtils.transformType(Map.class, data);
            } catch (Exception e) {
                vcontext.addError(
                        new ErrorMessage("validate value can't transform to map ,cause:" + e.getMessage()));
            }
            if (records != null) {
                return validateRecord(records, vcontext);
            }
            return data;
        }

    }

    /**
     * @param record
     * @param vcontext
     */
    protected void checkStructure(Map<String, Object> record, ValidationContext vcontext) {
        List<FieldInfo> fields = info.getFields();
        if (fields != null) {
            for (FieldInfo field : fields) {
                Object value = record.get(field.getName());
                checkRequired(record, field, value, vcontext);
            }
        }

    }

    /**
     * @param record
     * @param field
     * @param value
     * @param vcontext
     */
    protected boolean checkRequired(Map<String, Object> record, FieldInfo field, Object value,
            ValidationContext vcontext) {
        if (field.getRequired() != null && field.getRequired() && ("".equals(value)
                || value == null && (!vcontext.isPropertiesOnly() || record.containsKey(field.getName())))) {
            vcontext.addError(field.getName(),
                    vcontext.localizedErrorMessage(new ErrorMessage("%validator_requiredField")));
            return false;
        } else {
            return true;
        }

    }

    /**
     * ?Field
     */
    protected Object validateFieldValue(Map<String, Object> record, FieldInfo field, Object value,
            ValidationContext vcontext) {
        return validateFieldValue(record, field, value, vcontext, null);
    }

    protected Object validateFieldValue(Map<String, Object> record, FieldInfo field, Object value,
            ValidationContext vcontext, ValidationCreator creator) {
        String name = field.getName();
        vcontext.addPath(name);
        vcontext.addToTemplateContext(ValidationContext.TEMPLATE_FIELD, field);
        vcontext.addToTemplateContext(ValidationContext.TEMPLATE_RECORD, record);
        if (creator == null) {
            creator = getFieldCreator(field, vcontext);
        }
        if (creator == null && field != null) {
            String vinfo = (new StringBuilder()).append("No such type '").append(field.getType())
                    .append("', not processing field value at ").append(vcontext.getPath()).toString();
            vcontext.removePathSegment();
            createAndFireValidationEvent(Level.WARNING, vinfo);
            return value;
        }
        if (VALIDATION.isDebugEnabled()) {
            String vinfo = (new StringBuilder()).append("Validating field at path:").append(vcontext.getPath())
                    .append(" as ").append(getId()).append(".").append(name).append(" type: ")
                    .append(creator.getName()).toString();
            createAndFireValidationEvent(Level.DEBUG, vinfo);
        }
        vcontext.setCurrentRecord(record);
        vcontext.setCurrentDataService(this);
        Object res = creator.create(value, vcontext);
        vcontext.removePathSegment();
        return res;

    }

    protected ValidationCreator getFieldCreator(FieldInfo field, ValidationContext vcontext) {
        String strType = field.getType().value();
        boolean isEnum = false;
        if (field.getType() == FieldType.ENUM || field.getType() == FieldType.INT_ENUM) {
            isEnum = true;
        }
        List<ValidatorInfo> vls = field.getValidators();

        BuiltinCreator creator = vcontext.getBuiltinCreator(field.getType(), vcontext);
        List<ValidatorInfo> typeVs = null;
        if (creator != null) {
            typeVs = creator.getValidatorInfos();
        }
        @SuppressWarnings("unchecked")
        List<ValidatorInfo> allValidators = (List<ValidatorInfo>) DataUtils.combineAsLists(typeVs, vls);
        if (allValidators == null)
            allValidators = new ArrayList<ValidatorInfo>();
        if (isEnum && field.getValueMap() != null) {
            Map<String, Object> properties = new HashMap<String, Object>();
            properties.put("valueMapList", field.getValueMap());
            ValidatorInfo vi = new ValidatorInfo("isOneOf", properties);
            allValidators.add(0, vi);
        }
        if (VALIDATION.isDebugEnabled()) {
            VALIDATION.debug((new StringBuilder()).append("Creating field validator for field ").append(getId())
                    .append(".").append(field.getName()).append(", of simple type: ").append(field.getType())
                    .append(", with inline validators: ").append(vls).append(", and type validators: ")
                    .append(typeVs).toString());
        }
        creator = new BuiltinCreator(strType, allValidators);
        return creator;
    }

    /**
     * ?DataService?Field?
     */
    protected void handleExtraValue(Map<String, Object> record, String fieldName, Object value,
            ValidationContext vcontext) {
        if (value != null) {
            VALIDATION.debug((new StringBuilder()).append("Value provided for unknown field: ").append(getId())
                    .append(".").append(fieldName).append(": value is: ").append(value).toString());
        }
    }

    protected void createAndFireTimeMonitorEvent(long time, String msg) {
        Map<String, Object> properties = new HashMap<String, Object>();

        properties.put(TimeMonitorEvent.TOTAL_TIME, time);
        properties.put(TimeMonitorEvent.TIME_UNIT, TimeUnit.MICROSECONDS);
        properties.put(TimeMonitorEvent.MESSAGE, msg);
        TimeMonitorEvent event = new TimeMonitorEvent(properties);
        if (getEventService() != null) {
            getEventService().postEvent(event);
        }
    }

    protected boolean isEventEnable() {
        return getEventService() != null;
    }

    protected void createAndFireValidationEvent(Level levle, String msg) {
        ValidationEvent event = new ValidationEvent(levle, msg);
        if (getEventService() != null) {
            getEventService().postEvent(event);
        } else {
            VALIDATION.debug(msg);
        }
    }

    public EventService getEventService() {
        return eventService;
    }

    public void setEventService(EventService eventService) {
        this.eventService = eventService;
    }

    @Override
    public DataServiceInfo getDataServiceInfo() {
        return info;
    }

    public Container getContainer() {
        return container;
    }

    public DataTypeMap getConfigProperties() {
        return properties;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.datax.DataService#getProperties(java.lang.Object)
     */
    @Override
    public Map<Object, Object> getProperties(Object data) {
        boolean dropExtra = DataUtils.asBoolean(info.getProperty("dropExtraFields"));
        return getProperties(data, dropExtra);
    }

    public Map<Object, Object> getProperties(Object obj, boolean dropExtraFields) {
        return getProperties(obj, ((Collection<String>) (null)), dropExtraFields);
    }

    public Map<Object, Object> getProperties(Object obj, Collection<String> propsToKeep) {
        return getProperties(obj, propsToKeep, false);
    }

    public Map<Object, Object> getProperties(Object obj, Collection<String> propsToKeep, boolean dropExtraFields) {
        return getProperties(obj, propsToKeep, dropExtraFields, null);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Map<Object, Object> getProperties(Object data, Collection<String> popToKeep, boolean dropExtraFields,
            ValidationContext validationContext) {
        if (data == null)
            return null;
        if (!dropExtraFields) {
            if (Map.class.isAssignableFrom(data.getClass()))
                return Map.class.cast(data);
            else {
                Map obj = null;
                try {
                    obj = DataUtils.getProperties(data);
                } catch (Exception e) {
                    //INGONE;
                }
                return obj;
            }
        }

        Map result = new LinkedMap();

        Map<Object, Object> source = null;
        Set<String> outProperties = new HashSet<String>();
        if (popToKeep != null)
            outProperties.addAll(popToKeep);
        List<String> prop = new ArrayList<String>();
        //Map<String, String> xpaths = null;--XPATH
        List<FieldInfo> fields = info.getFields();
        if (fields == null)
            return Collections.emptyMap();
        for (FieldInfo field : fields) {

            if (dropExtraFields && field.getType() == FieldType.UNKNOWN)
                continue;
            prop.add(field.getName());
            /*if (field.getValueXPath() != null) {
            if (xpaths == null)
                xpaths = new HashMap<String, String>();
            xpaths.put(field.getName(), field.getValueXPath());
            }--XPATH*/
        }
        if (prop != null)
            outProperties.addAll(prop);
        if (data instanceof Map<?, ?>) {
            source = (Map<Object, Object>) data;
            for (Object key : source.keySet()) {
                if (outProperties.contains(key)) {
                    result.put(key, source.get(key));
                }
            }
        } else {
            try {
                result = DataUtils.getProperties(data, outProperties);
            } catch (Exception e) {
                result = null;
                LOG.warn("transform bean object to map failed .caused by" + e.getMessage());
            }
        }
        /* if (xpaths != null) {
        for (String key : xpaths.keySet()) {
            Object value = result.get(key);
            if (value != null) {
                JXPathContext context = JXPathContext.newContext(value);
                result.put(key, context.getValue(xpaths.get(key)));
            }
        }
         }--XPATH*/
        return result;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.datax.DataService#hasRecord(java.lang.String, java.lang.Object)
     */
    @Override
    public boolean hasRecord(String realFieldName, Object value) {
        // XXX
        return false;
    }

    @Override
    public boolean canStartTransaction(DSRequest req, boolean ignoreExistingTransaction) {
        if (req == null)
            return false;
        if (!canJoinTransaction(req))
            return false;
        if (req.getDSCall() == null)
            return false;
        boolean isModification = DataTools.isModificationRequest(req);
        TransactionPolicy policy = req.getDSCall().getTransactionPolicy();
        if (isModification) {
            //?policy
            if (ignoreExistingTransaction) {
                return true;
            }
            if (policy == TransactionPolicy.NONE) {
                return false;
            } else {
                return true;
            }
        } else {
            if (ignoreExistingTransaction) {
                return false;
            }
            if (policy == null) {
                return false;
            } else {
                switch (policy) {
                case NONE:
                    return false;
                case ALL:
                    return true;
                case ANY_CHANGE:
                    return req.getDSCall().queueIncludesUpdates(req);
                case FROM_FIRST_CHANGE:
                    return req.getDSCall().queueIncludesUpdates(req);
                default:
                    return false;
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.solmix.datax.DataService#canJoinTransaction(org.solmix.datax.DSRequest)
     */
    @Override
    public boolean canJoinTransaction(DSRequest req) {
        if (req != null && req.getDSCall() != null) {
            Boolean reqOvrride = req.isCanJoinTransaction();
            if (reqOvrride != null) {
                return reqOvrride.booleanValue();
            }
        }
        Boolean work = autoJoinAtOperationLevel(req);
        if (work == null) {
            // check datasource level
            work = autoJoinAtDataServiceLevel();
            if (work == null) {
                if (req != null && req.getDSCall() != null) {
                    TransactionPolicy policy = req.getDSCall().getTransactionPolicy();
                    if (policy == TransactionPolicy.NONE)
                        return false;
                    if (policy == TransactionPolicy.ALL)
                        return true;
                }
                work = autoJoinAtProviderLevel(req);
                if (work == null)
                    work = autoJoinAtGlobalLevel(req);
            }
        }
        if (work == null)
            return false;
        else
            return work.booleanValue();
    }

    /**
     * ?
     * 
     * @return
     */
    protected Boolean autoJoinAtGlobalLevel(DSRequest req) {
        String autoJoin = getConfigProperties().getString("autoJoinTransactions");
        return parseAutoJoinTransactions(req, autoJoin);
    }

    protected Boolean parseAutoJoinTransactions(DSRequest req, Object join) {
        if (join == null)
            return null;
        String autoJoin = join.toString();
        if (autoJoin.toLowerCase().equals("true") || autoJoin.toLowerCase().equals("ALL"))
            return Boolean.TRUE;
        if (autoJoin.toLowerCase().equals("false") || autoJoin.toLowerCase().equals("NONE"))
            return Boolean.FALSE;
        if (req != null && req.getDSCall() != null) {
            if (autoJoin.equals("FROM_FIRST_CHANGE"))
                return Boolean.valueOf(req.getDSCall().queueIncludesUpdates(req));
            if (autoJoin.equals("ANY_CHANGE"))
                return Boolean.valueOf(req.getDSCall().queueIncludesUpdates(req));
        }
        return null;
    }

    /**
     * DataService
     * 
     * @return
     */
    protected Boolean autoJoinAtProviderLevel(DSRequest req) {
        return false;
    }

    /**
     * DataService?
     * @return
     */
    protected Boolean autoJoinAtDataServiceLevel() {
        Object aj = info.getProperty("autoJoinTransactions");
        if (aj == null) {
            return null;
        } else {
            return Boolean.valueOf(aj.toString());
        }
    }

    /**
     * operation?
     */
    protected Boolean autoJoinAtOperationLevel(DSRequest req) {
        OperationInfo oi = req.getOperationInfo();
        Object aj = oi.getProperty("autoJoinTransactions");
        if (aj == null) {
            return null;
        } else {
            return Boolean.valueOf(aj.toString());
        }
    }
    //??DSrequest?????(rule)??
    /*  protected String getTransactionObjectKey(DSRequest req){
    return null;
      }*/

    /* protected Transaction getTransactionObject(DSRequest req) {
    if (req == null){
        return null;
    }
    if (req.getDSCall() == null)
        return null;
    else
        return (Transaction) req.getDSCall().getAttribute(getTransactionObjectKey(req));
     }*/

    @Override
    public Object escapeValue(Object data, String reference) {
        // TODO Auto-generated method stub
        return null;
    }

    public void setDSCallFactory(DSCallFactory factory) {
        this.dsCallFactory = factory;
    }
}