/* * Copyright 2012 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.fmk.datasource; import java.beans.PropertyDescriptor; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import org.apache.commons.collections.map.LinkedMap; import org.apache.commons.jxpath.JXPathContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.solmix.api.VelocityExpression; import org.solmix.api.criterion.ErrorMessage; import org.solmix.api.datasource.ClientParameter; import org.solmix.api.datasource.ConvertDSContextToMap; import org.solmix.api.datasource.DSRequest; import org.solmix.api.datasource.DSResponse; import org.solmix.api.datasource.DSResponse.Status; import org.solmix.api.datasource.DataSource; import org.solmix.api.datasource.DataSourceData; import org.solmix.api.datasource.DataSourceGenerator; import org.solmix.api.datasource.IType; import org.solmix.api.event.IValidationEvent.Level; import org.solmix.api.exception.SlxException; import org.solmix.api.jaxb.Efield; import org.solmix.api.jaxb.Eoperation; import org.solmix.api.jaxb.EserverType; import org.solmix.api.jaxb.TdataSource; import org.solmix.api.jaxb.Tfield; import org.solmix.api.jaxb.ToperationBinding; import org.solmix.api.jaxb.Tsecurity; import org.solmix.api.jaxb.Tvalidator; import org.solmix.api.serialize.JSParser; import org.solmix.api.serialize.JSParserFactory; import org.solmix.api.types.ClientParameterType; import org.solmix.api.types.Texception; import org.solmix.api.types.Tmodule; import org.solmix.api.types.TransactionPolicy; import org.solmix.commons.collections.DataTypeMap; import org.solmix.commons.logs.SlxLog; import org.solmix.commons.util.DataUtils; import org.solmix.fmk.SlxContext; import org.solmix.fmk.datasource.ValidationContext.Vtype; import org.solmix.fmk.event.EventWorker; import org.solmix.fmk.event.EventWorkerFactory; import org.solmix.fmk.internal.DatasourceCM; import org.solmix.fmk.js.JSExpression; import org.solmix.fmk.serialize.JSParserFactoryImpl; import org.solmix.fmk.util.DataTools; import org.solmix.fmk.util.DefaultValidators; import org.solmix.runtime.SystemContext; import org.solmix.runtime.cm.ConfigureUnit; import org.solmix.runtime.cm.ConfigureUnitManager; import org.w3c.dom.Element; /** * @author solmix.f@gmail.com * @since 0.0.1 * @version 110035 2010-12-26 solmix-ds */ @SuppressWarnings("unchecked") public class BasicDataSource implements DataSource { private static final Logger log = LoggerFactory.getLogger(BasicDataSource.class.getName()); public static final String PID = "org.solmix.framework.datasource"; protected static JSParser jsParser; private String dsName; protected DataSourceData data; protected DataSourceGenerator dataSourceGenerator; protected EventWorker worker; protected DataTypeMap config; protected SystemContext sc; final static Map<Object, Object> buildInValidator; static { buildInValidator = new HashMap<Object, Object>(); buildInValidator.put(Efield.TEXT, "isString"); buildInValidator.put(Efield.BOOLEAN, "isBoolean"); buildInValidator.put(Efield.INTEGER, "isInteger"); buildInValidator.put(Efield.FLOAT, "isFloat"); buildInValidator.put(Efield.DATE, "isDate"); buildInValidator.put(Efield.TIME, "isTime"); buildInValidator.put(Efield.DATETIME, "isTime"); buildInValidator.put(Efield.ENUM, null); buildInValidator.put(Efield.INT_ENUM, "integer"); buildInValidator.put(Efield.SEQUENCE, "isInteger"); buildInValidator.put(Efield.LINK, null); buildInValidator.put(Efield.IMAGE, null); buildInValidator.put(Efield.BINARY, null); buildInValidator.put(Efield.IMAGE_FILE, null); buildInValidator.put(Efield.MODIFIER, null); buildInValidator.put(Efield.MODIFIER_TIMESTAMP, null); buildInValidator.put(Efield.PASSWORD, null); } public BasicDataSource() { this(SlxContext.getThreadSystemContext()); } public BasicDataSource(final SystemContext sc) { setSystemContext(sc); } @Resource public void setSystemContext(final SystemContext sc) { this.sc = sc; } public static synchronized JSParser getJsParser() { if (jsParser == null) { JSParserFactory jsFactory = JSParserFactoryImpl.getInstance(); jsParser = jsFactory.get(); } return jsParser; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#getProviderType() */ @Override public String getServerType() { return EserverType.BASIC.value(); } @Override public DataSource instance(DataSourceData data) throws SlxException { BasicDataSource basic = new BasicDataSource(sc); basic.init(data); return basic; } /** * {@inheritDoc} * * @throws SlxException * * @see org.solmix.api.datasource.DataSource#init(org.solmix.api.datasource.DataSourceData) */ @Override public void init(DataSourceData data) throws SlxException { if (data == null) return; this.config = getConfig(); this.setContext(data); if (log.isTraceEnabled()) log.trace((new StringBuilder()).append("Creating instance of DataSource '").append(data.getName()).append("'").toString()); // If dataSource used as other build in datasource ,will not contain a TdataSouece. if (data.getTdataSource() != null && DataUtils.isNotEqual(data.getTdataSource().getServerType(), EserverType.CUSTOM)) { autoFitDS(this).buildDS(this).validateDS(this); } } /** * @return */ protected DataTypeMap getConfig() throws SlxException { ConfigureUnitManager cum = sc.getExtension(ConfigureUnitManager.class); ConfigureUnit cu = null; try { cu = cum.getConfigureUnit(getPID()); } catch (IOException e) { throw new SlxException(Tmodule.SQL, Texception.IO_EXCEPTION, e); } if (cu != null) return cu.getProperties(); else return new DataTypeMap(); } /** * @return */ protected String getPID() { return PID; } /** * Auto fit the datasource.<br> * <b>Note:</b><br> * as a part of process to create a new datasource. sub datasource want to custom design would be override it. * * @param ds * @return * @throws SlxException */ protected BasicDataSource autoFitDS(BasicDataSource ds) throws SlxException { _autoGenerateSchema(ds)._autoGetSuperDS(ds); return ds; } protected BasicDataSource buildDS(BasicDataSource ds) throws SlxException { _buildSuperDS(ds)._buildRequires(ds)._buildRequireRoles(ds)._buildAllFields(ds)._buildNativeFiles(ds); return ds; } protected BasicDataSource validateDS(BasicDataSource ds) throws SlxException { if (ds.getContext() == null) throw new SlxException(Tmodule.DATASOURCE, Texception.DEFAULT, "Datasource must init with a context"); return ds; } @Override public Object fetchById(Object id) throws Exception { if (data.getPrimaryKeys() == null) { throw new Exception("Cannot fetch by ID - DataSource has no primary key field"); } else { Map<String,Object> criteria = new HashMap<String,Object>(); criteria.put(data.getPrimaryKey(), id); DSRequest req = new DSRequestImpl(getName(), Eoperation.FETCH); req.getContext().setCriteria(criteria); // req.context =this DSResponse resp = req.execute(); return resp.getSingleRecord(); } } /** * @return the dataSourceGenerator */ @Override public synchronized DataSourceGenerator getDataSourceGenerator() { if (dataSourceGenerator == null) dataSourceGenerator = new BasicGenerator(this); return dataSourceGenerator; } /** * @param dataSourceGenerator the dataSourceGenerator to set */ @Override public void setDataSourceGenerator(DataSourceGenerator dataSourceGenerator) { this.dataSourceGenerator = dataSourceGenerator; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.IType#getName() */ @Override public String getName() { if (dsName == null) dsName = data.getName(); return dsName; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#getDataSourceData() */ @Override public DataSourceData getContext() { assert data != null; return data; } @Override public void setContext(DataSourceData context) { this.data = context; } @Override public DSResponse execute(Eoperation operationBindingType, String operationBindingID) throws SlxException { DSRequest req = new DSRequestImpl(); req.getContext().setOperation(operationBindingID); req.getContext().setOperationType(operationBindingType); return execute(req); } /** * {@inheritDoc} * * @throws SlxException * * @see org.solmix.api.datasource.DataSource#execute(org.solmix.api.datasource.DSRequest) */ @Override public DSResponse execute(DSRequest req) throws SlxException { if (req == null) return null; req.registerFreeResourcesHandler(this); try { // avoid a dsrequest without datasource.used for the processor from datasource not from dsrequest. // for example: if (req.getDataSource() == null && req.getDataSourceName() == null) { req.setDataSource(this); } if (!req.isServiceCalled()) { DSResponse _dsResponse = ServiceDataSource.execute(req, req.getDSCall(), req.getRequestContext()); if (_dsResponse != null) return _dsResponse; } Eoperation _opType = req.getContext().getOperationType(); if (_opType != Eoperation.CUSTOM) { DSResponse validationFailure = validateDSRequest(req); if (validationFailure != null) return validationFailure; } req.setRequestStarted(true); if (DataTools.isFetch(_opType)) { return executeFetch(req); } else if (DataTools.isRemove(_opType)) { return executeRemove(req); } else if (DataTools.isUpdate(_opType)) { return executeUpdate(req); } else if (DataTools.isAdd(_opType)) { return executeAdd(req); } else if (DataTools.isReplace(_opType)) { return executeReplace(req); } else if (DataTools.isDownload(_opType)) { return executeDownload(req); } else if (DataTools.isValidate(_opType)) { return executeValidate(req); } else { return executeCustomer(req); } } finally { if (req.isFreeOnExecute()) { req.freeResources(); } } } /** * @param req * @return * @throws SlxException */ public DSResponse executeCustomer(DSRequest req) throws SlxException { return notSupported(req); } /** * @param req * @return */ public DSResponse executeValidate(DSRequest req) throws SlxException { return notSupported(req); } /** * @param req * @return * @throws SlxException */ public DSResponse executeDownload(DSRequest req) throws SlxException { return notSupported(req); } /** * @param req * @return * @throws SlxException */ public DSResponse executeReplace(DSRequest req) throws SlxException { return notSupported(req); } /** * @param req * @return * @throws SlxException */ public DSResponse executeRemove(DSRequest req) throws SlxException { return notSupported(req); } /** * @param req * @return * @throws SlxException */ public DSResponse executeUpdate(DSRequest req) throws SlxException { return notSupported(req); } /** * @param req * @return * @throws SlxException */ public DSResponse executeFetch(DSRequest req) throws SlxException { return notSupported(req); } protected DSResponse notSupported(DSRequest req) throws SlxException { throw new SlxException(Tmodule.DATASOURCE, Texception.DS_NO_SUPPORT_OPERATION_TYPE, (new StringBuilder()).append("Operation type '").append( req.getContext().getOperationType()).append("' not supported by this DataSource (").append(getServerType()).append(")").toString()); } public DSResponse executeAdd(DSRequest req) throws SlxException { return notSupported(req); } @Override public DSResponse validateDSRequest(DSRequest req) throws SlxException { if (req.isValidated()) return null; req.setValidated(true); List<Object> errors = validateDSRequst(this, req); if (errors != null) { LoggerFactory.getLogger(SlxLog.VALIDATION_LOGNAME).info( (new StringBuilder()).append("Validation error: ").append(DataTools.prettyPrint(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; } } /** * Validation DataSource and relative DSRequest data.this Datasource operation must be a modification operation ( * {@link org.solmix.fmk.util.DataTools#isModificationOperation DataTools.isModificationOperation} ) or * {@link org.solmix.api.jaxb.Eoperation#VALIDATE VALIDATE} operation. and * {@link org.solmix.api.datasource.DataSourceData#isValidateRecords()} is true. * * @param ds basic datasource. * @param request datasource request. * @return * @throws SlxException */ public List<Object> validateDSRequst(BasicDataSource ds, DSRequest request) throws SlxException { Eoperation _opType = request.getContext().getOperationType(); if (!DataTools.isModificationOperation(_opType) && _opType != Eoperation.VALIDATE) return null; if (!ds.getContext().isValidateRecords()) return null; // Initial Validation context. ValidationContext vcontext = ValidationContext.instance(); if (request.getContext().getValidationMode() != null && request.getContext().getValidationMode().equals("partial")) vcontext.setPropertiesOnly(true); vcontext.setVtype(Vtype.DS_REQUEST); vcontext.setRpcManager(request.getDSCall()); vcontext.setRequestContext(request.getRequestContext()); vcontext.setDSRequstContext(request.getContext()); vcontext.setVfactory(ValidationEventFactory.getFieldValidator()); if (request.getContext().getOperationType() == Eoperation.UPDATE) vcontext.setPropertiesOnly(); request.setValidatedValues(ds.toRecords(request.getContext().getValueSets(), vcontext)); if (log.isDebugEnabled()) log.debug("post-validation valueSet: \n" + DataTools.prettyPrint(request.getContext().getValueSets())); Map<String, Object> errors = vcontext.getErrors(); if (errors != null) return new ArrayList<Object>(errors.values()); else return null; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#clearState() */ @Override public void clearState() { if (data.getSuperDS() != null) { DefaultDataSourceManager.freeDataSource(data.getSuperDS()); } } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#freeResources() */ @Override public void freeResources() { DefaultDataSourceManager.freeDataSource(this); } /** * Indicate whether join transaction or not.request transaction stage override datasource transaction stage. * * @param req * @return */ @Override public boolean shouldAutoJoinTransaction(DSRequest req) throws SlxException { if (req != null && req.getDSCall() != null) { Boolean reqOverride = req.isCanJoinTransaction(); if (reqOverride != null) return reqOverride.booleanValue(); } Boolean work = autoJoinAtOperationLevel(req); if (work == null) { // check datasource level work = autoJoinAtDataSourceLevel(); 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(); } @Override public Object getTransactionObject(DSRequest req) throws SlxException { if (req == null) throw new SlxException(Tmodule.DATASOURCE, Texception.OBJECT_IS_NULL, "Datasource request is null"); if (req.getDSCall() == null) return null; else return req.getDSCall().getAttribute(getTransactionObjectKey()); } /** * Used this key to cache transaction object in context,the subclass may override this method to used it. * * @return */ @Override public String getTransactionObjectKey() throws SlxException { return null; } /** * @param req * @return * @throws SlxException */ protected Boolean autoJoinAtProviderLevel(DSRequest req) throws SlxException { return false; } /** * @return */ protected Boolean autoJoinAtDataSourceLevel() { return data.getTdataSource().isAutoJoinTransactions(); } protected Boolean autoJoinAtGlobalLevel(DSRequest req) throws SlxException { String autoJoin = DatasourceCM.getProperties().getString(DatasourceCM.P_AUTO_JOIN_TRANSACTIONS); if (autoJoin == null) return null; 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().requestQueueIncludesUpdates(req)); if (autoJoin.equals("ANY_CHANGE")) return Boolean.valueOf(req.getDSCall().requestQueueIncludesUpdates(req)); } return null; } /** * @param req * @return */ protected Boolean autoJoinAtOperationLevel(DSRequest req) { Eoperation opType = req.getContext().getOperationType(); String opId = req.getContext().getOperationId(); ToperationBinding operationBinding = data.getOperationBinding(opType, opId); if (operationBinding == null) return null; else return operationBinding.isAutoJoinTransactions(); } protected boolean policyShouldOverrideConfig(DSRequest req) throws SlxException { if (req != null && req.getDSCall() != null) { Boolean reqOverride = req.isCanJoinTransaction(); if (reqOverride != null) return reqOverride.booleanValue(); } Boolean work = autoJoinAtOperationLevel(req); if (work == null) work = autoJoinAtDataSourceLevel(); return work == null; } /** * @param req * @param b * @return * @throws SlxException */ public boolean shouldAutoStartTransaction(DSRequest req, boolean ignoreExistingTransaction) throws SlxException { if (req == null) return false; if (!shouldAutoJoinTransaction(req)) return false; if (req.getDSCall() == null) return false; boolean isUpdate = DataTools.isModificationRequest(req); boolean shouldOverride = policyShouldOverrideConfig(req); TransactionPolicy policy = req.getDSCall().getTransactionPolicy(); if (isUpdate) { if (shouldOverride) { if (policy == TransactionPolicy.NONE) return false; } return true; } else { if (shouldOverride) { switch (policy) { case NONE: return false; case ALL: return true; case ANY_CHANGE: return req.getDSCall().requestQueueIncludesUpdates(req); case FROM_FIRST_CHANGE: } } } return false; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#toRecords(java.lang.Object, java.lang.Object) */ public Object toRecords(Object data, ValidationContext vcontext) throws SlxException { if (data instanceof List<?>) { List<Object> _recordList = (List) data; long start = System.currentTimeMillis(); Object result = toRecords(_recordList, vcontext); long end = System.currentTimeMillis(); String __info = new StringBuilder().append("Done validating ").append(_recordList.size()).append(" '").append(getName()).append( "'s at path '").append(vcontext.getPath()).append("': ").append(end - start).append("ms").append( _recordList.size() != 0 ? (new StringBuilder()).append(" (avg ").append((end - start) / _recordList.size()).append(")").toString() : "").toString(); getEventWork().createAndFireTimeEvent(end - start, __info); LoggerFactory.getLogger(SlxLog.TIME_LOGNAME).debug(__info); return result; } long start = System.currentTimeMillis(); Object result = toRecord(data, vcontext); long end = System.currentTimeMillis(); String __info = (new StringBuilder()).append("Done validating a '").append(getName()).append("' at path '").append(vcontext.getPath()).append( "': ").append(end - start).append("ms").toString(); getEventWork().createAndFireTimeEvent(end - start, __info); LoggerFactory.getLogger(SlxLog.TIME_LOGNAME).debug(__info); return result; } public EventWorker getEventWork() { if (worker == null) { EventWorkerFactory factory = EventWorkerFactory.getInstance(); worker = factory.createWorker(sc); } return worker; } public Object toRecords(List<Object> data, ValidationContext context) throws SlxException { if (data == null) return null; if (log.isDebugEnabled()) log.debug((new StringBuilder()).append("Validating ").append(data.size()).append(" '").append(getName()).append("'s at path '").append( context.getPath()).append("'").toString()); List<Object> records = new ArrayList<Object>(); for (int i = 0; i < data.size(); i++) records.add(toRecord(data.get(i), context)); return records; } /** * format the object to datasource record. * * @param object * @param context * @return */ protected Object toRecord(Object data, ValidationContext vcontext) throws SlxException { if (data == null) return null; if (log.isDebugEnabled()) log.debug((new StringBuilder()).append("Validating a '").append(getName()).append("' at path '").append(vcontext.getPath()).append("'").toString()); if (data instanceof Element) { throw new SlxException(Tmodule.DATASOURCE, Texception.NO_SUPPORT, "no support " + data.getClass().getName() + " used as a request object."); // TODO } else if (data instanceof Map<?, ?>) { Map<Object, Object> record = (Map<Object, Object>) data; if (record.keySet().size() == 1 && (record.get("ref") instanceof String)) return new JSExpression((String) record.get("ref")); vcontext.addPath(getName()); vcontext.addToTemplateContext("datasource", this); for (Object key : record.keySet()) { String fieldName = (String) key; Tfield field = this.data.getField(fieldName); Object value = record.get(fieldName); if (field == null) handleExtraValue(record, fieldName, value, vcontext); else if (value != null && !(value instanceof JSExpression)) record.put(fieldName, validateFieldValue(record, field, value, vcontext)); }// END record CYCLE checkStructure(record, vcontext); checkAutoConstruct(record, vcontext); vcontext.removePathSegment(); return record; } else { if (!vcontext.isIdAllowed() || !(data instanceof String)) LoggerFactory.getLogger(SlxLog.VALIDATION_LOGNAME).warn( (new StringBuilder()).append("Unexpected Java type '").append(data.getClass()).append("' passed to DataSource '").append( getName()).append("'").append(" at path '").append(vcontext.getPath()).append("'").toString()); return data; } } /** * @param record * @param vcontext */ protected void checkAutoConstruct(Map<Object, Object> record, ValidationContext vcontext) { // TODO Auto-generated method stub } /** * @param record * @param vcontext * @throws SlxException */ protected void checkStructure(Map<Object, Object> record, ValidationContext vcontext) throws SlxException { List<String> fieldNames = this.data.getFieldNames(); if (fieldNames != null) for (String name : fieldNames) { Tfield field = data.getField(name); Object value = record.get(name); if (checkRequired(record, field, value, vcontext) && (value != null || record.containsKey(name))) { if (value instanceof JSExpression) return; if (field.isMultiple() != null && field.isMultiple()) value = DataUtils.makeListIfSingle(value); // if (field.getUniqueProperty() != null) { // value=DataUtils.indexOnProperty(DataUtils.makeListIfSingle(value), field.getUniqueProperty()); // } } } } /** * @param record * @param field * @param value * @param vcontext * @return * @throws SlxException */ protected boolean checkRequired(Map<Object, Object> record, Tfield field, Object value, ValidationContext vcontext) throws SlxException { if (field.isRequired() != null && field.isRequired() && ("".equals(value) || value == null && (!vcontext.isPropertiesOnly() || record.containsKey(field.getName())))) { vcontext.addError(field.getName(), DefaultValidators.localizedErrorMessage(new ErrorMessage("%validator_requiredField"), vcontext)); return false; } else { return true; } } /** * @param record * @param field * @param value * @param vcontext * @return * @throws SlxException */ protected Object validateFieldValue(Map<Object, Object> record, Tfield field, Object value, ValidationContext vcontext) throws SlxException { return validateFieldValue(record, field, null, value, vcontext); } /** * @param record * @param name * @param object * @param value * @param vcontext * @return * @throws SlxException */ protected Object validateFieldValue(Map<Object, Object> record, Tfield field, IType declaredType, Object value, ValidationContext vcontext) throws SlxException { String _fieldName = field.getName(); vcontext.addPath(_fieldName); vcontext.addToTemplateContext("field", field); vcontext.addToTemplateContext("record", record); IType _fieldType = getFieldType(field, vcontext); IType type = declaredType == null ? _fieldType : declaredType; if (type == null && field != null) { String __vinfo = (new StringBuilder()).append("No such type '").append(field.getType()).append("', not processing field value at ").append( vcontext.getPath()).toString(); getEventWork().createFieldValidationEvent(Level.WARNING, __vinfo); vcontext.removePathSegment(); return value; } if (LoggerFactory.getLogger(SlxLog.VALIDATION_LOGNAME).isDebugEnabled()) { String __vinfo = (new StringBuilder()).append("Validating field:").append(vcontext.getPath()).append(" as ").append(getName()).append(".").append( _fieldName).append(" type: ").append(type.getName()).toString(); getEventWork().createFieldValidationEvent(Level.DEBUG, __vinfo); } if (field != null && !vcontext.isIdAllowed()) { vcontext.setIdAllowed(true); } vcontext.setCurrentRecord(record); vcontext.setCurrentDataSource(this); Object __return = type.create(value, vcontext); vcontext.removePathSegment(); return __return; } protected IType getFieldType(Tfield field, ValidationContext context) { String _fieldName = field.getName(); String _typeId = field.getType().value(); IType type = this.data.getCachedFiledType(_fieldName); if (type != null) return type; boolean _isEnumType = false; if (field.getType() == Efield.ENUM) _isEnumType = true; boolean _explicitExists = false; List<Tvalidator> fieldValidators = null; if (field.getValidators() != null) fieldValidators = field.getValidators().getValidator(); if (field.getForeignKey() == null) _explicitExists = false; if (fieldValidators == null) { _explicitExists = false; } BulidInType baseType = getBuildInType(field.getType(), context); Object typeValidators = null; if (baseType != null) typeValidators = baseType.getValidators(); List<Object> allValidators = DataUtils.makeListIfSingle(DataUtils.combineAsLists(typeValidators, fieldValidators)); if (allValidators == null) allValidators = new ArrayList<Object>(); boolean _foundvm = false; List list = null; // TODO // found in validator. if (_isEnumType && !_foundvm) if (field.getValueMap() != null) { list = field.getValueMap().getValue(); if (field.getValueMap().getValue() == null || field.getValueMap().getValue().isEmpty()) { log.warn((new StringBuilder()).append("invalid field of enum type has no field.valueMap at field: ").append(_fieldName).append( " of type: ").append(_typeId).toString()); } else { if (log.isDebugEnabled()) log.debug((new StringBuilder()).append("for field: ").append(_fieldName).append( " adding automatically generated isOneOf validator with values: ").append(DataTools.prettyPrint(list)).toString()); allValidators.add(0, DataUtils.buildMap("type", "isOneOf", "valueMapList", list)); } } if (LoggerFactory.getLogger(SlxLog.VALIDATION_LOGNAME).isDebugEnabled()) LoggerFactory.getLogger(SlxLog.VALIDATION_LOGNAME).debug( (new StringBuilder()).append("Creating field validator for field ").append(getName()).append(".").append(field.getName()).append( ", of simple type: ").append(field.getType()).append(", with inline validators: ").append(fieldValidators).append( ", and type validators: ").append(typeValidators).toString()); type = new BulidInType(_typeId, allValidators); this.data.addCachedFieldType(_fieldName, type); return type; } protected BulidInType getBuildInType(Efield fieldType, ValidationContext context) { Object validator = buildInValidator.get(fieldType); if (validator != null) return new BulidInType(fieldType.value(), validator); else return null; } /** * Handled extra value not in the datasource configuration context. * * @param record * @param fieldName * @param value * @param vcontext */ private void handleExtraValue(Map<Object, Object> record, String fieldName, Object value, ValidationContext vcontext) { if (value != null) { LoggerFactory.getLogger(SlxLog.VALIDATION_LOGNAME).debug( (new StringBuilder()).append("Value provided for unknown field: ").append(getName()).append(".").append(fieldName).append( ": value is: ").append(value).toString()); } } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#hasRecord(java.lang.String, java.lang.Object) */ @Override public boolean hasRecord(String realFieldName, Object value) { // TODO Auto-generated method stub return false; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#transformFieldValue(org.solmix.api.jaxb.Tfield, java.lang.Object) */ @Override public Object transformFieldValue(Tfield field, Object obj) { return obj; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#escapeValueForWhereClause(java.lang.Object, java.lang.Object) */ @Override public String escapeValue(Object value, Object field) { return value.toString(); } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#getProperties(java.lang.Object) */ @Override public Map<Object, Object> getProperties(Object data) { TdataSource tds= getContext().getTdataSource(); boolean dropExtra=true; if(tds!=null) dropExtra=DataUtils.booleanValue( tds.isDropExtraFields()); return getProperties(data, dropExtra, true); } public Map<Object, Object> getProperties(Object obj, boolean dropExtraFields, boolean dropIgnoredFields) { return getProperties(obj, ((Collection<String>) (null)), dropExtraFields, dropIgnoredFields); } public Map<Object, Object> getProperties(Object obj, boolean dropExtraFields, boolean dropIgnoredFields, ValidationContext validationContext) { return getProperties(obj, null, dropExtraFields, dropIgnoredFields, validationContext); } public Map<Object, Object> getProperties(Object obj, Collection<String> propsToKeep) { return getProperties(obj, propsToKeep, false, false); } public Map<Object, Object> getProperties(Object obj, Collection<String> propsToKeep, boolean dropExtraFields, boolean dropIgnoredFields) { return getProperties(obj, propsToKeep, dropExtraFields, dropIgnoredFields, null); } public Map<Object, Object> getProperties(Object data, Collection<String> popToKeep, boolean dropExtraFields, boolean dropIgnoreFields, 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; List<Tfield> __f = this.data.getFields(); if (__f == null) return Collections.emptyMap(); for (Tfield field : __f) { if (dropIgnoreFields && DataUtils.booleanValue(field.isIgnore())) continue; if (dropExtraFields && field.getType() == Efield.UNKNOWN) continue; prop.add(field.getName()); if (field.getValueXPath() != null) { if (xpaths == null) xpaths = new HashMap<String, String>(); xpaths.put(field.getName(), field.getValueXPath()); } } 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))); } } } return result; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#isAdvancedCriteria(java.util.Map) */ @Override public boolean isAdvancedCriteria(Map<String, ?> criteria) { if (criteria == null) return false; String constructor = (String) criteria.get("_constructor"); if ("AdvancedCriteria".equals(constructor)) return true; String fieldName = (String) criteria.get("fieldName"); String operator = (String) criteria.get("operator"); return data.getField("fieldName") == null && data.getField("operator") == null && data.getField(fieldName) != null && operator != null; } /** * Auto generate DataSource,if set <b>autoDeriveSchema = true</b> * * @param ds * @return * @throws SlxException */ protected BasicDataSource _autoGenerateSchema(BasicDataSource ds) throws SlxException { DataSourceData data = ds.getContext(); // auto generate schema. if (DataUtils.booleanValue(data.getTdataSource().isAutoDeriveSchema())) { DataSourceGenerator gen = getDataSourceGenerator(); DataSource autoSchema = null; if (gen == null) { String __info = "Config file of " + data.getUrlString() + " set autoDeriveSchema is true, but the DataSource type:[" + this.getServerType() + "] not supprote auto derive schema."; throw new SlxException(Tmodule.DATASOURCE, Texception.DS_DSCONFIG_ERROR, __info); } else { if (log.isTraceEnabled()) { log.trace(new StringBuilder().append("the datasource set autoDeriveSchema is true,used DataSourceGenerator:").append( gen.getClass().toString()).append(" to generate schema").toString()); } autoSchema = gen.deriveSchema(data); } // cache auto derived datasource schema if (autoSchema != null) { data.setAutoDeriveSchema(autoSchema); } } return ds; } protected BasicDataSource _autoGetSuperDS(BasicDataSource ds) throws SlxException { String _superName = ds.getContext().getTdataSource().getInheritsFrom(); if (DataUtils.isNotNullAndEmpty(_superName)) { DataSource _super = DefaultDataSourceManager.getDataSource(_superName); if (_super != null) { data.setSuperDSName(_superName); data.setSuperDS(_super); if (log.isTraceEnabled()) log.trace("Found the super datasource:" + _superName + " for " + ds.getName()); } else { log.warn("can not found the super datasource [" + _superName + "] of datasource [" + data.getName() + "]"); } } return ds; } /** * Used the auto derived schema as a super datasource. * * @return * @throws SlxException */ protected BasicDataSource _buildSuperDS(BasicDataSource ds) throws SlxException { DataSourceData context = ds.getContext(); boolean autoDerive = DataUtils.booleanValue(ds.getContext().getTdataSource().isAutoDeriveSchema()); Object schema = ds.getContext().getAutoDeriveSchema(); // pro process datasource if (autoDerive && schema != null) { if (ds.getContext().getSuperDS() == null && schema instanceof DataSource) { // pro process super datasource DataSource _after = (DataSource) schema; ds.getContext().setSuperDS(_after); ds.getContext().setSuperDSName(_after.getName()); } } // Check out super datasource name setting? if (context.getSuperDS() != null && context.getSuperDSName() == null) { context.setSuperDSName(context.getSuperDS().getName()); } if (context.getSuperDSName() != null && context.getTdataSource().getInheritsFrom() == null) { context.getTdataSource().setInheritsFrom(context.getSuperDSName()); } return ds; } /** * @param ds */ protected BasicDataSource _buildRequires(BasicDataSource ds) throws SlxException { DataSourceData data = ds.getContext(); /** requires */ Tsecurity sec = data.getTdataSource().getSecurity(); if (data.getRequires() == null && sec != null && DataUtils.isNotNullAndEmpty(sec.getRequires())) { String value = sec.getRequires(); data.setRequires(new VelocityExpression(value)); } return ds; } protected BasicDataSource _buildRequireRoles(BasicDataSource ds) throws SlxException { DataSourceData data = ds.getContext(); Tsecurity sec = data.getTdataSource().getSecurity(); /** requireRols */ if (data.getRequiresRoles() == null && sec != null && DataUtils.isNotNullAndEmpty(sec.getRequireRoles())) { List<String> equiresRoles = null; String req[] = sec.getRequireRoles().split(";"); for (String key : req) { equiresRoles = new ArrayList<String>(); equiresRoles.add(key); } data.setRequiresRoles(equiresRoles); } DataSource _superDS = ds.getContext().getSuperDS(); if (_superDS == null) return ds; /***************************************************************** * Requires Roles. ****************************************************************/ List<String> _sRequireRoles = _superDS.getContext().getRequiresRoles(); if (DataUtils.isNotNullAndEmpty(_sRequireRoles)) ds.getContext().getRequiresRoles().addAll(_sRequireRoles); return ds; } protected BasicDataSource _buildAllFields(BasicDataSource ds) throws SlxException { _buildFields(ds); DataSource _superDS = ds.getContext().getSuperDS(); if (_superDS == null) return ds; _buildFields(_superDS); /**************************************************************** * build fields. ***************************************************************/ Map<String, Tfield> mapFields = new LinkedHashMap<String, Tfield>(); Map<String, Tfield> _fields = ds.getContext().getMapFields(); Map<String, Tfield> _sfields = _superDS.getContext().getMapFields(); if (DataUtils.booleanValue(ds.getContext().getTdataSource().isShowLocalFieldsOnly())) { mapFields = _fields; } else { if (DataUtils.booleanValue(ds.getContext().getTdataSource().isUseParentFieldOrder())) { DataUtils.mapMerge(_sfields, mapFields); DataUtils.mapMerge(_fields, mapFields); } else { DataUtils.mapMerge(_fields, mapFields); DataUtils.mapMerge(_sfields, mapFields); } } if (DataUtils.isNullOrEmpty(mapFields)) throw new SlxException(Tmodule.DATASOURCE, Texception.DS_DSCONFIG_ERROR, "ds config file must have a filed at last"); ds.getContext().setMapFields(mapFields); return ds; } protected BasicDataSource _buildNativeFiles(BasicDataSource ds) throws SlxException { Map<String, Tfield> fields = ds.getContext().getMapFields(); if (fields == null) return ds; for (String fieldName : fields.keySet()) { Tfield __f = ds.getContext().getMapFields().get(fieldName); if (__f.getName() == null) { __f.setName(fieldName); } if (__f.getType() == null) { __f.setType(Efield.TEXT); } if (__f.isPrimaryKey()) ds.getContext().addToPrimaryKeys(fieldName); if (__f.getNativeName() == null) { ds.getContext().setDs2NativeFieldMap(fieldName, fieldName); } else { ds.getContext().setDs2NativeFieldMap(fieldName, __f.getNativeName()); } } /** Native2DSFieldMap */ ds.getContext().setNative2DSFieldMap(DataUtils.reverseMap(ds.getContext().getDs2NativeFieldMap())); return ds; } protected DataSource _buildFields(DataSource ds) throws SlxException { DataSourceData _tmp = ds.getContext(); /** fields */ if (DataUtils.isNullOrEmpty(_tmp.getMapFields()) && _tmp.getTdataSource().getFields() != null && DataUtils.isNotNullAndEmpty(_tmp.getTdataSource().getFields().getField())) { Map<String, Tfield> _tmpFields = new LinkedHashMap<String, Tfield>(); List<Tfield> fields = _tmp.getTdataSource().getFields().getField(); for (Tfield _field : fields) { String _name = _field.getName(); // field name is unique if (_tmpFields.containsKey(_name)) { Tfield old = _tmpFields.get(_name); String __vinfo = new StringBuilder().append("Field name is unique.").append("the old Field is").append( getJsParser().toJavaScript(old)).append("will replace by:").append(getJsParser().toJavaScript(_field)).toString(); getEventWork().createFieldValidationEvent(Level.WARNING, __vinfo); } if (_field.getType() == null) { _field.setType(Efield.TEXT); getEventWork().createFieldValidationEvent(Level.DEBUG, "DS Field not set type used text by default"); } _tmpFields.put(_field.getName(), _field); Efield _type = _field.getType(); String _title = _field.getTitle(); // add fields if (DataTools.isBinaryType(_type)) { Tfield filename = new Tfield(); filename.setName(_name + "_filename"); filename.setType(Efield.TEXT); filename.setLength(255); filename.setTitle("Name"); filename.setHidden(new Boolean(true)); filename.setCanEdit(new Boolean(false)); filename.setCustomSQL(_field.isCustomSQL()); _tmpFields.put(filename.getName(), filename); Tfield filesize = new Tfield(); filesize.setName(_name + "_filesize"); filesize.setType(Efield.INTEGER); filesize.setTitle("Size"); filesize.setHidden(new Boolean(true)); filesize.setCanEdit(new Boolean(false)); filesize.setCustomSQL(_field.isCustomSQL()); _tmpFields.put(filesize.getName(), filesize); Tfield date_created = new Tfield(); date_created.setName(_name + "_date_created"); date_created.setType(Efield.DATE); date_created.setTitle("Date Created"); date_created.setHidden(new Boolean(true)); date_created.setCanEdit(new Boolean(false)); date_created.setCustomSQL(_field.isCustomSQL()); _tmpFields.put(date_created.getName(), date_created); }// end Add Fields } _tmp.setMapFields(_tmpFields); }// END BUILD FIELD. return ds; } /** * {@inheritDoc} * * @see org.solmix.api.datasource.DataSource#getAutoOperationId(org.solmix.api.jaxb.Eoperation) */ @Override public String getAutoOperationId(Eoperation _opType) { return DataTools.autoCreateOperationID(getName(), _opType); } protected Map<String, ?> toClientValueMap(Object tds, JXPathContext jxpc, Map<String, Object> parent, boolean isRoot) { Map<String, Object> context; if (isRoot) { context = parent; } else { context = new HashMap<String, Object>(); } isRoot = false; Field[] _fields = tds.getClass().getDeclaredFields(); if (_fields == null) return null; Map<String, PropertyDescriptor> properties = null; try { properties = DataUtils.getPropertyDescriptors(tds); } catch (Exception e) { // just ignore it. return null; } Set<String> fieldNames = properties.keySet(); List<String> list = new ArrayList<String>(fieldNames); Collections.sort(list); for (Field f : _fields) { ClientParameter cpara = f.getAnnotation(ClientParameter.class); if (cpara != null) { ClientParameterType type = cpara.type(); String path = cpara.path(); String property = f.getName(); PropertyDescriptor pd = properties.get(property); Method method = pd.getReadMethod(); Object value = null; if (method != null) try { value = method.invoke(tds); } catch (Exception e) { // just ignore it. } if (value != null) { switch (type) { case DEFAULT: { if ("./".equals(path)) { context.put(property, value); } else { jxpc.createPathAndSetValue(path, value); } } break; case MAP: { Map<String, ?> child = toClientValueMap(value, jxpc, context, isRoot); if (child != null && child.size() > 0) if ("./".equals(path)) { context.put(property, child); } else { jxpc.createPathAndSetValue(path, child); } } break; case ARRAY: { List<?> child = toClientArray(value, jxpc); if ("./".equals(path)) { context.put(property, child); } else { jxpc.createPathAndSetValue(path, child); } } break; } } }// END if (cpara != null) { } return context; } protected List<?> toClientArray(Object list, JXPathContext jxpc) { List<Object> _return = null; if (list instanceof List<?>) { List tmp = (List) list; _return = new ArrayList<Object>(); for (Object o : tmp) { _return.add(this.toClientValueMap(o, jxpc, null, false)); } } return _return; } @Override public Map<String, ?> toClientValueMap() { long _s = System.currentTimeMillis(); TdataSource tds = data.getTdataSource(); Map<String, ?> context = ConvertDSContextToMap.toClientValueMap(tds); long s_ = System.currentTimeMillis(); getEventWork().createAndFireTimeEvent(s_ - _s, "time used to convert datasource context to map value"); return context; } }