/*
* 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.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.solmix.api.application.Application;
import org.solmix.api.application.ApplicationManager;
import org.solmix.api.call.DSCall;
import org.solmix.api.context.WebContext;
import org.solmix.api.datasource.DSRequest;
import org.solmix.api.datasource.DSRequestData;
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.FreeResourcesHandler;
import org.solmix.api.exception.SlxException;
import org.solmix.api.jaxb.Efield;
import org.solmix.api.jaxb.Eoperation;
import org.solmix.api.jaxb.Tfield;
import org.solmix.api.jaxb.ToperationBinding;
import org.solmix.api.jaxb.request.Roperation;
import org.solmix.api.types.Texception;
import org.solmix.api.types.Tmodule;
import org.solmix.commons.io.SlxFile;
import org.solmix.commons.util.DataUtils;
import org.solmix.ds.context.Context;
import org.solmix.fmk.SlxContext;
import org.solmix.fmk.internal.DatasourceCM;
import org.solmix.fmk.upload.UploadItem;
import org.solmix.fmk.util.DataTools;
import org.solmix.fmk.util.ErrorReport;
import org.solmix.runtime.SystemContext;
/**
* Implements of {@link org.solmix.api.datasource.DSRequest DSRequest}
*
* @author solmix.f@gmail.com
* @since 0.0.1
* @version 110035 2011-1-3 solmix-ds
*/
@SuppressWarnings("unchecked")
public class DSRequestImpl implements DSRequest
{
private static Logger log = LoggerFactory.getLogger(DSRequestImpl.class.getName());
private DSRequestData data;
private Application app;
private DSCall dsc;
boolean requestStarted;
private Context requestContext;
private DataSource dataSource;
private String dataSourceName;
private Boolean joinTransaction;
private Boolean freeOnExecute ;
private boolean partOfTransaction;
private boolean serviceCalled = false;
private boolean validated = false;
FreeResourcesHandler freeResourcesHandler;
public DSRequestImpl()
{
this((Roperation) null);
data.setAppID(Application.BUILT_IN_APPLICATION);
}
public DSRequestImpl(DataSource dataSource)
{
this(dataSource, (Eoperation) null, (String) null);
}
public DSRequestImpl(String dataSourceName, Eoperation opType)
{
this(dataSourceName, opType, (String) null);
}
public DSRequestImpl(DataSource dataSource, Eoperation opType)
{
this(dataSource, opType, (String) null);
}
public DSRequestImpl(DataSource dataSource, Eoperation opType, String operationID)
{
this();
this.dataSource = dataSource;
data.setOperationType(opType);
data.setOperation(operationID);
}
public DSRequestImpl(String dsName, Eoperation opType, String operationID)
{
this();
this.dataSourceName = dsName;
data.setOperationType(opType);
data.setOperation(operationID);
}
public DSRequestImpl(String dataSourceName, String opType)
{
this(dataSourceName, Eoperation.fromValue(opType));
}
public DSRequestImpl(Roperation operation, Context context) throws SlxException
{
this(operation);
if (context != null) {
this.requestContext = context;
data.setIsClientRequest(true);
if (context instanceof WebContext)
parseUploadedFiles((WebContext) context);
}
}
public DSRequestImpl(DataSource datasource, Eoperation opType, DSCall rpc2)
{
this(datasource, opType, (String) null);
setDSCall(dsc);
}
public DSRequestImpl(String dataSourceName, Eoperation opType, DSCall rpc)
{
this(dataSourceName, opType, (String) null);
setDSCall(rpc);
}
/**
* construct function,initial {@link org.solmix.api.datasource.DSRequest}
*
* @param operation
*/
public DSRequestImpl(Roperation operation)
{
data = new DSRequestData();
data.init();
if (operation == null)
return;
data.setRoperation(operation);
String _operation = data.getOperationId();
// parser values.
if (operation.getValues() != null) {
data.setValues(operation.getValues());
}
if (operation.getCriteria() != null) {
data.setCriteria(operation.getCriteria());
}
if (operation.getOldValues() != null) {
data.setRawOldValues(operation.getOldValues());
}
// operationType and sourceName.
if (operation.getDataSource() != null) {
data.setOperationType(operation.getOperationType() == null ? null : Eoperation.fromValue(operation.getOperationType()));
dataSourceName = operation.getDataSource();
data.setRepo(operation.getRepo());
} else if (_operation != null && _operation.indexOf('_') != -1) {
if (log.isDebugEnabled())
log.debug("cannot find Datasource name use " + _operation + " transform to datasource");
data.setOperationType(Eoperation.fromValue(_operation.substring(_operation.lastIndexOf('_') + 1)));
dataSourceName = _operation.substring(0, _operation.lastIndexOf('_'));
}
// Application
if (!data.getAppID().equals("builtinApplication")) {
// List<String> qualifiedUserTypes = DataUtils.makeList("*");
// try {
// DataUtils.addAll(qualifiedUserTypes, app.userIsOfTypes());
// } catch (Exception e) {
// log.error("Can't look up app users", e);
// }
}
if (data.getOutputs() == null) {
if (operation.getOutputs() != null)
data.setOutputs(DataUtils.commaSeparatedStringToList(operation.getOutputs()));
}
if (data.getSortBy() == null && operation.getSortBy() != null && operation.getSortBy().size() > 1)
data.setRawSortBy(operation.getSortBy());
}
@Override
public boolean isFreeOnExecute() {
if(freeOnExecute==null){
if(getDSCall()!=null)
return false;
else
return true;
}else{
return freeOnExecute.booleanValue();
}
}
@Override
public void setFreeOnExecute(boolean freeOnExecute) {
this.freeOnExecute = freeOnExecute;
}
/**
* @return the beenThroughDMI
*/
@Override
public boolean isServiceCalled() {
return serviceCalled;
}
@Override
public void setServiceCalled(boolean serviceCalled) {
this.serviceCalled = serviceCalled;
}
@Override
public boolean isValidated() {
return validated;
}
@Override
public void setValidated(boolean validated) {
this.validated = validated;
}
/**
* @return the app
* @throws SlxException
*/
public synchronized Application getApp() throws SlxException {
if (app == null) {
SystemContext sc;
if (requestContext instanceof WebContext) {
sc = ((WebContext) requestContext).getSystemContext();
} else {
sc = SlxContext.getThreadSystemContext();
}
ApplicationManager am = sc.getExtension(ApplicationManager.class);
if (am != null)
app = am.findByID(data.getAppID());
}
return app;
}
/**
* @param app the app to set
*/
public void setApp(Application app) {
this.app = app;
}
/**
* @return the joinTransaction
*/
@Override
public Boolean isCanJoinTransaction() {
return joinTransaction;
}
@Override
public boolean isJoinTransaction() {
return partOfTransaction;
}
@Override
public void setJoinTransaction(boolean partOfTransaction) {
this.partOfTransaction = partOfTransaction;
}
/**
* @param joinTransaction the joinTransaction to set
* @throws SlxException
*/
@Override
public void setCanJoinTransaction(Boolean joinTransaction) throws SlxException {
if (requestStarted) {
throw new SlxException(Tmodule.DATASOURCE, Texception.DS_REQUEST_ALREADY_STARTED,
"Request processing has started; join transactions setting cannot be changed");
} else {
this.joinTransaction = joinTransaction;
return;
}
}
/**
* @return the requestStarted
*/
@Override
public boolean isRequestStarted() {
return requestStarted;
}
/**
* Indicate this request already started.
*
* @param requestStarted the requestStarted to set
*/
@Override
public void setRequestStarted(boolean requestStarted) {
this.requestStarted = requestStarted;
}
/**
* @return the dataSourceName
*/
@Override
public String getDataSourceName() {
if (this.dataSourceName == null && this.dataSource != null) {
dataSourceName = this.dataSource.getName();
}
return dataSourceName;
}
/**
* @param dataSourceName the dataSourceName to set
*/
@Override
public void setDataSourceName(String dataSourceName) {
if (this.dataSourceName != null && !this.dataSourceName.equals(dataSourceName))
DefaultDataSourceManager.freeDataSource(dataSource);
this.dataSourceName = dataSourceName;
}
/**
* @return the dataSource
* @throws SlxException
*/
@Override
public DataSource getDataSource() throws SlxException {
if (dataSource == null && getDataSourceName() != null) {
dataSource = DefaultDataSourceManager.getDataSource(getDataSourceName());
freeResourcesHandler = dataSource;
}
return dataSource;
}
/**
* @param dataSource the dataSource to set
*/
@Override
public void setDataSource(DataSource dataSource) {
if (this.dataSource != null)
DefaultDataSourceManager.freeDataSource(this.dataSource);
this.dataSource = dataSource;
this.dataSourceName = dataSource.getName();
}
/**
* @return the dsc
*/
@Override
public DSCall getDSCall() {
return dsc;
}
/**
* @param dsc the dsc to set
*/
@Override
public void setDSCall(DSCall rpc) {
this.dsc = rpc;
}
@Override
public void setValidatedValues(Object values) {
if (data.getBeforeValidatedValues() != null) {
log.warn("setValidatedValues called more than once for this DSRequest object");
return;
} else {
data.setBeforeValidatedValues(data.getRawValues());
data.setValues(values);
}
}
/**
* @throws SlxException
*/
private void parseUploadedFiles(WebContext webContext) throws SlxException {
Map<String, Object> criteria = data.getCriteria();
if (criteria != null && criteria.get("download_fieldname") != null) {
data.setDownloadFieldName((String) criteria.get("download_fieldname"));
data.setDownloadFileName((String) criteria.get("download_filename"));
}
if (!webContext.isMultipart())
return;
DataSource _ds = getDataSource();
DataSourceData _dsData = _ds.getContext();
int _dftMaxSize = DatasourceCM.getProperties().getInt(DatasourceCM.P_MAX_UPLOAD_FILESIZE);
Map<String, Object> _values = data.getValues();
Map<String, Object> _addFields = new HashMap<String, Object>();
if (_values != null) {
for (String fieldName : _values.keySet()) {
Tfield field = _dsData.getField(fieldName);
if (field != null && DataTools.isBinary(field)) {
String filename = SlxFile.canonicalizePath((String) _values.get(fieldName));
String shortFilename = filename;
if (shortFilename.indexOf("/") != -1)
shortFilename = shortFilename.substring(shortFilename.lastIndexOf("/") + 1);
UploadItem _file = null;
List<Object> errors = null;
if (webContext.getRequest().getParameter("singleUpload") != null) {
long fileSize = webContext.getRequest().getContentLength();
int maxSize = _dftMaxSize;
if (field.getMaxFileSize() != null)
maxSize = field.getMaxFileSize();
if (fileSize > maxSize) {
errors = new ArrayList<Object>();
ErrorReport errorReport = new ErrorReport();
errors.add(errorReport);
String __errorString = (new StringBuilder()).append("Size of '").append(shortFilename).append("' (").append(
DataUtils.formatFileSize(fileSize)).append(") exceeded maximum allowed file size of ").append(
DataUtils.formatFileSize(maxSize)).toString().toString();
log.debug(__errorString);
errorReport.addError(fieldName, __errorString);
}
}
_file = (UploadItem) webContext.getUploadedFile(fieldName, errors);
if (_file != null) {
_file.setFieldName(fieldName);
_file.setShortFileName(shortFilename);
data.addToUploadedFiles(_file);
_addFields.put(fieldName + "_filename", shortFilename);
_addFields.put(fieldName + "_filesize", new Long(_file.getSize()));
_addFields.put(fieldName + "_date_created", new Date());
}
}
}// end loop values
}
data.setValues(DataUtils.mapMerge(_addFields, _values));
}
private DSResponse validateDSRequest() throws SlxException {
if (getDataSourceName() == null) {
return createResponse(Status.STATUS_VALIDATION_ERROR, "DataSource name must be assigned");
}
return null;
}
private DSResponse createResponse(Status status, Object... errors) throws SlxException {
DSResponse dsResponse = new DSResponseImpl(getDataSource(), this);
dsResponse.setStatus(status);
dsResponse.setErrors(errors);
return dsResponse;
}
private DSResponse prepareReturn(DSResponse _dsResponse) throws SlxException {
if (isFreeOnExecute()) {
freeResources();
if (dsc != null)
dsc.freeDataSources();
}
return _dsResponse;
}
/**
* {@inheritDoc}
*
* @see org.solmix.api.datasource.DSRequest#execute()
*/
@Override
public DSResponse execute() throws SlxException {
DSResponse _dsResponse = validateDSRequest();
if (_dsResponse != null)
return prepareReturn(_dsResponse);
try {
_dsResponse = securityChecks();
if (_dsResponse != null)
return prepareReturn(_dsResponse);
List<Object> _tmpFiles = data.getUploadedFiles();
if (_tmpFiles != null)
for (Object o : _tmpFiles) {
UploadItem file = (UploadItem) o;
List<Object> errors = file.getErrors();
if (errors != null) {
_dsResponse = createResponse(Status.STATUS_FAILURE, errors.toArray(new Object[errors.size()]));
// _dsResponse.getContext().setRequestConnectionClose(true);
}
}// end upload files loop
if (_dsResponse != null) {
return prepareReturn(_dsResponse);
}
/**
* checkout datasource is passed DMI datasource or not.
*/
if (!this.isServiceCalled()) {
if (dsc != null)
dsc.applyEarlierResponseValues(this);
if (data.getOperationType() == Eoperation.ADD || data.getOperationType() == Eoperation.UPDATE) {
hashFieldValues();
populateModifierAndCreatorFields(data.getOperationType() == Eoperation.ADD);
}
_dsResponse = ServiceDataSource.execute(this, dsc, requestContext, getApp());
}
if (_dsResponse == null)
_dsResponse = getApp().execute(this, requestContext);
Eoperation opType = data.getOperationType();
String opId = data.getOperationId();
ToperationBinding operationBinding = null;
List<String> outputColumns = new ArrayList<String>();
if (dataSource != null)
operationBinding = dataSource.getContext().getOperationBinding(opType, opId);
if (operationBinding != null && operationBinding.getOutputs() != null) {
String outputArray[] = operationBinding.getOutputs().split(",");
for (String str : outputArray)
outputColumns.add(str.trim());
}
// if(opType == Eoperation.LOADSCHEMA)
// merge ds request outputs with datasource outputs
List<String> _output = data.getOutputs();
if (_output != null) {
{
if (outputColumns.containsAll(_output))
outputColumns = _output;
else {
log.warn((new StringBuilder()).append(
"The dsRequest contains a client-specified 'outputs', but this is not a subset of the server-specified 'outputs' for this operation binding (").append(
opId).append("). Ignoring the client-specified ").append("value.").toString());
}
}
}
/*} catch (Exception e) {
log.error("execute()", e);
_dsResponse = new DSResponseImpl(getDataSource(),this);
_dsResponse.setRawData(e.getMessage());
_dsResponse.setStatus(Status.STATUS_FAILURE);*/
} finally {
if (isFreeOnExecute()) {
this.freeResources();
if (dsc != null)
dsc.freeDataSources();
}
}
return _dsResponse;
}
/**
* @param b
* @throws SlxException
*/
private void populateModifierAndCreatorFields(boolean addMode) throws SlxException {
if (getDataSource() == null)
return;
DataSourceData _dsData = getDataSource().getContext();
String _modifier = data.getUserId();
Date _modifierTimestamp;
if (dsc != null)
_modifierTimestamp = (Date) dsc.getAttribute("transactionDate");
else
_modifierTimestamp = new Date();
List<Tfield> _fields = _dsData.getFields();
for (Tfield _field : _fields) {
if (_field.getType() == Efield.MODIFIER)
data.getValues().put(_field.getName(), _modifier);
else if (_field.getType() == Efield.MODIFIER_TIMESTAMP)
data.getValues().put(_field.getName(), _modifierTimestamp);
else if (addMode) {
if (_field.getType() == Efield.CREATOR)
data.getValues().put(_field.getName(), _modifier);
else if (_field.getType() == Efield.CREATOR_TIMESTAMP)
data.getValues().put(_field.getName(), _modifierTimestamp);
}
}
}
/**
* @throws SlxException
*/
private void hashFieldValues() throws SlxException {
if (getDataSource() == null)
return;
DataSourceData _dsData = getDataSource().getContext();
List<Tfield> fields = _dsData.getFields();
for (Tfield field : fields) {
if (field.getStoreWithHash() != null) {
String value = (String) data.getValues().get(field.getName());
if (value != null)
try {
value = DataUtils.hashValue(value, field.getStoreWithHash());
} catch (Exception e) {
throw new SlxException(Tmodule.DATASOURCE, Texception.NO_SUCH_ALGORITHM, "hash fields with no such algorithm.", e);
}
data.getValues().put(field.getName(), value);
}
}
}
/**
* Authentication checks.the check order is: OperationID > OperationType > DataSource > Project.
* <p>
* {@link javax.servlet.http.HttpServletRequest#getRemoteUser() getRemoteUser} get User. validate User's roles in
* dsc and httpServletRequest.
*
* @return
* @throws Exception
*/
protected DSResponse securityChecks() throws SlxException {
return null;
}
/**
* {@inheritDoc}
*
* @see org.solmix.api.datasource.DSRequest#getContext()
*/
@Override
public DSRequestData getContext() {
return data;
}
/**
* {@inheritDoc}
*
* @see org.solmix.api.datasource.DSRequest#setConf(org.solmix.api.datasource.DSRequestData)
*/
@Override
public void setContext(DSRequestData data) {
this.data = data;
}
/**
* {@inheritDoc}
*
* @see org.solmix.api.datasource.DSRequest#freeResources()
*/
@Override
public void freeResources() {
if (freeResourcesHandler != null)
freeResourcesHandler.freeResources();
dataSource = null;
}
/**
* {@inheritDoc}
*
* @see org.solmix.api.datasource.DSRequest#registerFreeResourcesHandler(org.solmix.api.datasource.FreeResourcesHandler)
*/
@Override
public void registerFreeResourcesHandler(FreeResourcesHandler handler) {
freeResourcesHandler = handler;
}
@Override
public boolean isModificationRequest() throws SlxException {
return DataTools.isModificationOperation(data.getOperationType());
}
/**
* {@inheritDoc}
*
* @see org.solmix.api.datasource.DSRequest#getRequestContext()
*/
@Override
public Context getRequestContext() {
return requestContext;
}
@Override
public void setRequestContext(Context context) throws SlxException {
requestContext = context;
}
}