org.openbravo.service.datasource.DataSourceServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.service.datasource.DataSourceServlet.java

Source

/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.1  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html 
 * Software distributed under the License  is  distributed  on  an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific  language  governing  rights  and  limitations
 * under the License. 
 * The Original Code is Openbravo ERP. 
 * The Initial Developer of the Original Code is Openbravo SLU 
 * All portions are Copyright (C) 2009-2015 Openbravo SLU
 * All Rights Reserved. 
 * Contributor(s):  ______________________________________.
 ************************************************************************
 */
package org.openbravo.service.datasource;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.Vector;

import javax.inject.Inject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.hibernate.Query;
import org.hibernate.criterion.Restrictions;
import org.hibernate.exception.SQLGrammarException;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.exception.OBSecurityException;
import org.openbravo.base.model.Entity;
import org.openbravo.base.model.Property;
import org.openbravo.base.model.domaintype.EnumerateDomainType;
import org.openbravo.base.secureApp.VariablesSecureApp;
import org.openbravo.base.session.OBPropertiesProvider;
import org.openbravo.client.application.Parameter;
import org.openbravo.client.application.Process;
import org.openbravo.client.application.RefWindow;
import org.openbravo.client.application.process.BaseProcessActionHandler;
import org.openbravo.client.application.window.OBViewUtil;
import org.openbravo.client.kernel.BaseKernelServlet;
import org.openbravo.client.kernel.KernelUtils;
import org.openbravo.client.kernel.OBUserException;
import org.openbravo.client.kernel.RequestContext;
import org.openbravo.client.kernel.reference.NumberUIDefinition;
import org.openbravo.client.kernel.reference.UIDefinition;
import org.openbravo.client.kernel.reference.UIDefinitionController;
import org.openbravo.dal.core.DalUtil;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.core.SessionHandler;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.database.SessionInfo;
import org.openbravo.erpCommon.businessUtility.Preferences;
import org.openbravo.erpCommon.utility.OBMessageUtils;
import org.openbravo.erpCommon.utility.PropertyException;
import org.openbravo.erpCommon.utility.PropertyNotFoundException;
import org.openbravo.erpCommon.utility.Utility;
import org.openbravo.model.ad.datamodel.Column;
import org.openbravo.model.ad.domain.Reference;
import org.openbravo.model.ad.ui.Element;
import org.openbravo.model.ad.ui.Field;
import org.openbravo.model.ad.ui.FieldTrl;
import org.openbravo.model.ad.ui.Tab;
import org.openbravo.model.ad.ui.Window;
import org.openbravo.portal.PortalAccessible;
import org.openbravo.service.json.DefaultJsonDataService;
import org.openbravo.service.json.JsonConstants;
import org.openbravo.service.json.JsonUtils;
import org.openbravo.service.web.InvalidContentException;
import org.openbravo.service.web.InvalidRequestException;
import org.openbravo.service.web.ResourceNotFoundException;
import org.openbravo.service.web.WebServiceUtil;
import org.openbravo.utils.Replace;

/**
 * A web service which provides a JSON REST service using the {@link DataSourceService}
 * implementation. Retrieves the data source using the {@link DataSourceServiceProvider}.
 * 
 * @author mtaal
 */
public class DataSourceServlet extends BaseKernelServlet {
    private static final Logger log = Logger.getLogger(DataSourceServlet.class);

    private static final long serialVersionUID = 1L;

    private static String servletPathPart = "org.openbravo.service.datasource";

    public static String getServletPathPart() {
        return servletPathPart;
    }

    @Inject
    private DataSourceServiceProvider dataSourceServiceProvider;

    @Override
    public void init(ServletConfig config) {
        if (config.getInitParameter(DataSourceConstants.URL_NAME_PARAM) != null) {
            servletPathPart = config.getInitParameter(DataSourceConstants.URL_NAME_PARAM);
        }

        super.init(config);
    }

    public void service(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {

        try {
            if (OBContext.getOBContext() != null && OBContext.getOBContext().isPortalRole()) {
                if (!(getDataSource(request) instanceof PortalAccessible)) {
                    log.error("Portal user " + OBContext.getOBContext().getUser() + " with role "
                            + OBContext.getOBContext().getRole() + " is trying to access to non granted datasource "
                            + request.getRequestURL());
                    throw new OBSecurityException();
                }
            }

            SessionInfo.setModuleId(request.getParameter("moduleId"));
            SessionInfo.setCommand(request.getParameter(DataSourceConstants.OPERATION_TYPE_PARAM));
            SessionInfo.setProcessId(request.getParameter("tabId"));
            SessionInfo.setProcessType("W");

            super.service(request, response);
        } catch (final InvalidRequestException e) {
            if (SessionHandler.isSessionHandlerPresent()) {
                SessionHandler.getInstance().setDoRollback(true);
            }
            response.setStatus(400);
            log.error(e.getMessage(), e);
            writeResult(response, JsonUtils.convertExceptionToJson(e));
        } catch (final InvalidContentException e) {
            if (SessionHandler.isSessionHandlerPresent()) {
                SessionHandler.getInstance().setDoRollback(true);
            }
            response.setStatus(409);
            log.error(e.getMessage(), e);
            writeResult(response, JsonUtils.convertExceptionToJson(e));
        } catch (final ResourceNotFoundException e) {
            if (SessionHandler.isSessionHandlerPresent()) {
                SessionHandler.getInstance().setDoRollback(true);
            }
            response.setStatus(404);
            log.error(e.getMessage(), e);
            writeResult(response, JsonUtils.convertExceptionToJson(e));
        } catch (final OBSecurityException e) {
            if (SessionHandler.isSessionHandlerPresent()) {
                SessionHandler.getInstance().setDoRollback(true);
            }
            response.setStatus(401);
            log.error(e.getMessage(), e);
            writeResult(response, JsonUtils.convertExceptionToJson(e));
        } catch (final Throwable t) {
            t.printStackTrace(System.err);
            if (SessionHandler.isSessionHandlerPresent()) {
                SessionHandler.getInstance().setDoRollback(true);
            }
            response.setStatus(500);
            log.error(t.getMessage(), t);
            writeResult(response, JsonUtils.convertExceptionToJson(t));
        }
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        final Map<String, String> parameters = getParameterMap(request);
        doFetch(request, response, parameters);
    }

    private void doFetch(HttpServletRequest request, HttpServletResponse response, Map<String, String> parameters)
            throws IOException, ServletException {
        // checks and set parameters, if not valid then go away
        if (!checkSetParameters(request, response, parameters)) {
            return;
        }

        if (log.isDebugEnabled()) {
            getRequestContent(request);
        }
        try {
            if (!hasAccess(request, parameters.get("tabId"))) {
                throw new OBUserException("AccessTableNoView");
            }

            String filterClass = parameters.get(DataSourceConstants.DS_FILTERCLASS_PARAM);
            if (filterClass != null) {
                try {
                    DataSourceFilter filter = (DataSourceFilter) Class.forName(filterClass).newInstance();
                    filter.doFilter(parameters, request);
                } catch (Exception e) {
                    log.error("Error trying to apply datasource filter with class: " + filterClass, e);
                }
            }
            // now do the action
            boolean isExport = "true".equals(parameters.get("exportToFile"));
            if (isExport) {
                String exportAs = parameters.get("exportAs");
                if (StringUtils.isEmpty(exportAs)) {
                    exportAs = "csv";
                }
                if ("csv".equals(exportAs)) {
                    try {
                        OBContext.setAdminMode();
                        try {
                            Window window = parameters.get("tab") == null
                                    || parameters.get("tab").equals("undefined") ? null
                                            : OBDal.getInstance().get(Tab.class, parameters.get("tab")).getWindow();
                            String encoding = Preferences.getPreferenceValue("OBSERDS_CSVTextEncoding", true,
                                    OBContext.getOBContext().getCurrentClient(),
                                    OBContext.getOBContext().getCurrentOrganization(),
                                    OBContext.getOBContext().getUser(), OBContext.getOBContext().getRole(), window);
                            response.setContentType("text/csv; charset=" + encoding);
                        } catch (PropertyNotFoundException e) {
                            // There is no preference for encoding, using standard one which works on Excel
                            response.setContentType("text/csv; charset=iso-8859-1");
                        }
                    } finally {
                        OBContext.restorePreviousMode();
                    }
                    response.setHeader("Content-Disposition", "attachment; filename=ExportedData.csv");
                    QueryJSONWriterToCSV writer;
                    if (getDataSource(request) instanceof DefaultDataSourceService) {
                        writer = new QueryJSONWriterToCSV(request, response, parameters,
                                getDataSource(request).getEntity());
                        // when exporting a OB grid, the isActive filter should not be set
                        parameters.put(JsonConstants.NO_ACTIVE_FILTER, "true");
                        ((DefaultDataSourceService) getDataSource(request)).fetch(parameters, writer);
                    } else {
                        String result = getDataSource(request).fetch(parameters);
                        JSONObject jsonResult = new JSONObject(result);
                        JSONArray data = jsonResult.getJSONObject("response").getJSONArray("data");
                        writer = new QueryJSONWriterToCSV(request, response, parameters,
                                getDataSource(request).getEntity());
                        for (int i = 0; i < data.length(); i++) {
                            writer.write(data.getJSONObject(i));
                        }
                    }

                    writer.writeCSVFooterNote(parameters);
                } else {
                    log.error("Unsupported export format: " + exportAs);
                }
            } else {
                String result = getDataSource(request).fetch(parameters);
                writeResult(response, result);
            }
        } catch (Exception e) {
            handleException(e, response);
        }
    }

    private class QueryJSONWriterToCSV extends DefaultJsonDataService.QueryResultWriter {

        Writer writer;
        String fieldSeparator;
        String decimalSeparator;
        String prefDecimalSeparator;
        List<String> fieldProperties;
        Map<String, String> niceFieldProperties = new HashMap<String, String>();
        boolean propertiesWritten = false;
        Map<String, Map<String, String>> refLists = new HashMap<String, Map<String, String>>();
        List<String> refListCols = new ArrayList<String>();
        List<String> dateCols = new ArrayList<String>();
        List<String> dateTimeCols = new ArrayList<String>();
        List<String> timeCols = new ArrayList<String>();
        List<String> numericCols = new ArrayList<String>();
        Map<String, DecimalFormat> formats = new HashMap<String, DecimalFormat>();
        int clientUTCOffsetMiliseconds;
        TimeZone clientTimeZone;

        public QueryJSONWriterToCSV(HttpServletRequest request, HttpServletResponse response,
                Map<String, String> parameters, Entity entity) {
            try {
                OBContext.setAdminMode();
                response.setHeader("Content-Disposition", "attachment; filename=ExportedData.csv");
                writer = response.getWriter();
                VariablesSecureApp vars = new VariablesSecureApp(request);
                Window window = parameters.get("tab") == null || parameters.get("tab").equals("undefined") ? null
                        : OBDal.getInstance().get(Tab.class, parameters.get("tab")).getWindow();
                try {
                    prefDecimalSeparator = Preferences.getPreferenceValue("OBSERDS_CSVDecimalSeparator", true,
                            OBContext.getOBContext().getCurrentClient(),
                            OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(),
                            OBContext.getOBContext().getRole(), window);
                } catch (PropertyNotFoundException e) {
                    // There is no preference for the decimal separator.
                }
                decimalSeparator = vars.getSessionValue("#DecimalSeparator|generalQtyEdition").substring(0, 1);
                try {
                    fieldSeparator = Preferences.getPreferenceValue("OBSERDS_CSVFieldSeparator", true,
                            OBContext.getOBContext().getCurrentClient(),
                            OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(),
                            OBContext.getOBContext().getRole(), window);
                } catch (PropertyNotFoundException e) {
                    // There is no preference for the field separator. Using the default one.
                    fieldSeparator = ",";
                }
                if ((prefDecimalSeparator != null && prefDecimalSeparator.equals(fieldSeparator))
                        || (prefDecimalSeparator == null && decimalSeparator.equals(fieldSeparator))) {
                    if (!fieldSeparator.equals(";")) {
                        fieldSeparator = ";";
                    } else {
                        fieldSeparator = ",";
                    }
                    log.warn(
                            "Warning: CSV Field separator is identical to the decimal separator. Changing the field separator to "
                                    + fieldSeparator + " to avoid generating a wrong CSV file");
                }
                if (parameters.get("_UTCOffsetMiliseconds").length() > 0) {
                    clientUTCOffsetMiliseconds = Integer.parseInt(parameters.get("_UTCOffsetMiliseconds"));
                } else {
                    clientUTCOffsetMiliseconds = 0;
                }

                clientTimeZone = null;
                try {
                    String clientTimeZoneId = Preferences.getPreferenceValue("localTimeZoneID", true,
                            OBContext.getOBContext().getCurrentClient(),
                            OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(),
                            OBContext.getOBContext().getRole(), null);
                    List<String> validTimeZoneIDs = Arrays.asList(TimeZone.getAvailableIDs());
                    if (validTimeZoneIDs.contains(clientTimeZoneId)) {
                        clientTimeZone = TimeZone.getTimeZone(clientTimeZoneId);
                    } else {
                        log4j.error(clientTimeZoneId
                                + " is not a valid time zone identifier. For a list of all accepted identifiers check http://www.java2s.com/Tutorial/Java/0120__Development/GettingallthetimezonesIDs.htm");
                    }
                } catch (PropertyException pe) {
                    log4j.warn(
                            "The local Local Timezone ID property is not defined. It can be defined in a preference. For a list of all accepted values check http://www.java2s.com/Tutorial/Java/0120__Development/GettingallthetimezonesIDs.htm");
                }

                fieldProperties = new ArrayList<String>();
                if (parameters.get("viewState") != null
                        && !parameters.get("viewState").toString().equals("undefined")) {
                    String viewStateO = parameters.get("viewState");
                    String viewStateWithoutParenthesis = viewStateO.substring(1, viewStateO.length() - 1);
                    JSONObject viewState = new JSONObject(viewStateWithoutParenthesis);
                    String fieldA = viewState.getString("field");
                    JSONArray fields = new JSONArray(fieldA);
                    for (int i = 0; i < fields.length(); i++) {
                        JSONObject field = fields.getJSONObject(i);
                        if (field.has("visible") && !field.getBoolean("visible")) {
                            // The field is not visible. We should not export it
                            continue;
                        }
                        if (field.getString("name").equals("_checkboxField")
                                || field.getString("name").equals("_editLink")) {
                            continue;
                        }
                        fieldProperties.add(field.getString("name"));
                    }
                }

                // Now we calculate ref lists and nice property names
                final String userLanguageId = OBContext.getOBContext().getLanguage().getId();
                if (entity != null) {
                    final Map<String, Property> properties = new HashMap<String, Property>();
                    for (Property prop : entity.getProperties()) {
                        if (!fieldProperties.contains(prop.getName())) {
                            continue;
                        }
                        properties.put(prop.getName(), prop);
                    }
                    for (String fieldProperty : fieldProperties) {
                        if (fieldProperty.contains(DalUtil.FIELDSEPARATOR)) {
                            properties.put(fieldProperty, DalUtil.getPropertyFromPath(entity, fieldProperty));
                        }
                    }

                    for (String propKey : properties.keySet()) {
                        final Property prop = properties.get(propKey);
                        Column col = OBDal.getInstance().get(Column.class, prop.getColumnId());

                        if (prop.isAuditInfo()) {
                            Element element = null;
                            if ("creationDate".equals(propKey)) {
                                element = OBViewUtil.createdElement;
                            } else if ("createdBy".equals(propKey)) {
                                element = OBViewUtil.createdByElement;
                            } else if ("updated".equals(propKey)) {
                                element = OBViewUtil.updatedElement;
                            } else if ("updatedBy".equals(propKey)) {
                                element = OBViewUtil.updatedByElement;
                            }
                            if (element != null) {
                                niceFieldProperties.put(propKey,
                                        OBViewUtil.getLabel(element, element.getADElementTrlList()));
                            } else {
                                niceFieldProperties.put(propKey, col.getName());
                            }
                        } else if (parameters.get("tab") != null && !parameters.get("tab").equals("")) {
                            Tab tab = OBDal.getInstance().get(Tab.class, parameters.get("tab"));
                            for (Field field : tab.getADFieldList()) {
                                if (field.getColumn() == null || !field.getColumn().getId().equals(col.getId())) {
                                    continue;
                                }
                                niceFieldProperties.put(propKey, field.getName());
                                for (FieldTrl fieldTrl : field.getADFieldTrlList()) {
                                    if (fieldTrl.getLanguage().getId().equals(userLanguageId)) {
                                        niceFieldProperties.put(propKey, fieldTrl.getName());
                                    }
                                }
                            }
                        } else {
                            niceFieldProperties.put(propKey, col.getName());
                        }
                        UIDefinition uiDef = UIDefinitionController.getInstance().getUIDefinition(col.getId());
                        if (uiDef instanceof NumberUIDefinition) {
                            formats.put(propKey, Utility.getFormat(vars, ((NumberUIDefinition) uiDef).getFormat()));
                        }

                        // We also store the date properties
                        if (prop.isDate()) {
                            dateCols.add(propKey);
                        } else if (prop.isDatetime()) {
                            dateTimeCols.add(propKey);
                        } else if (prop.isTime()) {
                            timeCols.add(propKey);
                        } else if (prop.isPrimitive() && prop.isNumericType()) {
                            numericCols.add(propKey);
                        }

                        if (!(prop.getDomainType() instanceof EnumerateDomainType)) {
                            continue;
                        }
                        String referenceId = col.getReferenceSearchKey().getId();
                        Map<String, String> reflists = new HashMap<String, String>();
                        final String hql = "select al.searchKey, al.name from ADList al where "
                                + " al.reference.id=? and al.active=true";
                        final Query qry = OBDal.getInstance().getSession().createQuery(hql);
                        qry.setString(0, referenceId);
                        for (Object o : qry.list()) {
                            final Object[] row = (Object[]) o;
                            reflists.put(row[0].toString(), row[1].toString());
                        }
                        final String hqltrl = "select al.searchKey, trl.name from ADList al, ADListTrl trl where "
                                + " al.reference.id=? and trl.listReference=al and trl.language.id=?"
                                + " and al.active=true and trl.active=true";
                        final Query qrytrl = OBDal.getInstance().getSession().createQuery(hqltrl);
                        qrytrl.setString(0, referenceId);
                        qrytrl.setString(1, userLanguageId);
                        for (Object o : qrytrl.list()) {
                            final Object[] row = (Object[]) o;
                            reflists.put(row[0].toString(), row[1].toString());
                        }
                        refListCols.add(propKey);
                        refLists.put(propKey, reflists);
                    }
                }

                writeCSVHeaderNote(parameters);
                if (fieldProperties.size() > 0) {
                    // If the request came with the view state information, we get the properties from there
                    for (int i = 0; i < fieldProperties.size(); i++) {
                        if (i > 0) {
                            writer.append(fieldSeparator);
                        }
                        if (niceFieldProperties.get(fieldProperties.get(i)) != null) {
                            writer.append("\"").append(niceFieldProperties.get(fieldProperties.get(i)))
                                    .append("\"");
                        }
                    }
                    propertiesWritten = true;
                }
            } catch (Exception e) {
                throw new OBException("Error while exporting a CSV file", e);
            } finally {
                OBContext.restorePreviousMode();
            }
        }

        private void writeJSONProperties(JSONObject row) {
            final Iterator<?> itKeysF = row.keys();
            Vector<String> keys = new Vector<String>();
            boolean isFirst = true;
            try {
                while (itKeysF.hasNext()) {
                    String key = (String) itKeysF.next();
                    if (key.endsWith(JsonConstants.IDENTIFIER)) {
                        continue;
                    }
                    if (fieldProperties.size() > 0 && !fieldProperties.contains(key)) {
                        // Field is not visible. We don't show it
                        continue;
                    }
                    if (isFirst) {
                        isFirst = false;
                    } else {
                        writer.append(fieldSeparator);
                    }
                    keys.add(key);
                    writer.append("\"").append(key).append("\"");
                }
                propertiesWritten = true;
            } catch (Exception e) {
                throw new OBException("Error while writing column names when exporting a CSV file", e);
            }
        }

        public void write(JSONObject json) {
            try {
                if (!propertiesWritten) {
                    writeJSONProperties(json);
                }
                writer.append("\n");
                final Iterator<?> itKeys;
                if (fieldProperties.size() > 0) {
                    itKeys = fieldProperties.iterator();
                } else {
                    itKeys = json.keys();
                }

                boolean isFirst = true;
                while (itKeys.hasNext()) {
                    String key = (String) itKeys.next();
                    if (key.endsWith(JsonConstants.IDENTIFIER)) {
                        continue;
                    }
                    if (fieldProperties.size() > 0 && !fieldProperties.contains(key)) {
                        // Field is not visible. We don't show it
                        continue;
                    }
                    if (isFirst) {
                        isFirst = false;
                    } else {
                        writer.append(fieldSeparator);
                    }
                    if (!json.has(key)) {
                        continue;
                    }
                    Object keyValue = json.has(key + DalUtil.FIELDSEPARATOR + JsonConstants.IDENTIFIER)
                            ? json.get(key + DalUtil.FIELDSEPARATOR + JsonConstants.IDENTIFIER)
                            : json.get(key);
                    if (refListCols.contains(key)) {
                        keyValue = refLists.get(key).get(keyValue);
                    } else if (keyValue instanceof Number && keyValue != null) {
                        DecimalFormat format = formats.get(key);
                        if (format == null) {
                            // if the CSV decimal separator property is defined, used it over the character
                            // defined in Format.xml
                            keyValue = keyValue.toString().replace(".",
                                    prefDecimalSeparator != null ? prefDecimalSeparator : decimalSeparator);
                        } else {
                            keyValue = format.format(new BigDecimal(keyValue.toString()));
                            if (prefDecimalSeparator != null) {
                                keyValue = keyValue.toString()
                                        .replace(new Character(
                                                format.getDecimalFormatSymbols().getDecimalSeparator()).toString(),
                                                prefDecimalSeparator);
                            }

                        }
                    } else if (dateCols.contains(key) && keyValue != null && !keyValue.toString().equals("null")) {
                        Date date = JsonUtils.createDateFormat().parse(keyValue.toString());
                        String pattern = RequestContext.get().getSessionAttribute("#AD_JAVADATEFORMAT").toString();
                        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
                        dateFormat.setLenient(true);
                        keyValue = dateFormat.format(date);
                    } else if (dateTimeCols.contains(key) && keyValue != null
                            && !keyValue.toString().equals("null")) {
                        final String repairedString = JsonUtils.convertFromXSDToJavaFormat(keyValue.toString());
                        Date localDate = JsonUtils.createDateTimeFormat().parse(repairedString);
                        Date clientTimezoneDate = null;
                        clientTimezoneDate = convertFromLocalToClientTimezone(localDate);
                        String pattern = RequestContext.get().getSessionAttribute("#AD_JAVADATETIMEFORMAT")
                                .toString();
                        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
                        dateFormat.setLenient(true);
                        keyValue = dateFormat.format(clientTimezoneDate);
                    } else if (timeCols.contains(key) && keyValue != null && !keyValue.toString().equals("null")) {
                        Date UTCdate = JsonUtils.createTimeFormatWithoutGMTOffset().parse(keyValue.toString());
                        Date clientTimezoneDate = null;
                        clientTimezoneDate = convertFromUTCToClientTimezone(UTCdate);
                        SimpleDateFormat timeFormat = JsonUtils.createTimeFormatWithoutGMTOffset();
                        timeFormat.setLenient(true);
                        keyValue = timeFormat.format(clientTimezoneDate);
                    }

                    if (keyValue != null && !keyValue.toString().equals("null")) {
                        keyValue = Replace.replace(keyValue.toString(), "\"", "\"\"");
                    } else {
                        keyValue = "";
                    }
                    if (!numericCols.contains(key)) {
                        keyValue = "\"" + keyValue + "\"";
                    }
                    writer.append(keyValue.toString());
                }
            } catch (Exception e) {
                throw new OBException("Error while exporting CSV information", e);
            }
        }

        private Date convertFromLocalToClientTimezone(Date localDate) {

            Date UTCDate = convertFromLocalToUTCTimezone(localDate);
            Date clientDate = convertFromUTCToClientTimezone(UTCDate);

            return clientDate;
        }

        private Date convertFromUTCToClientTimezone(Date UTCdate) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(UTCdate);
            if (clientTimeZone != null) {
                calendar = Calendar.getInstance(clientTimeZone);
                calendar.setTime(UTCdate);
                int gmtMillisecondOffset = (calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET));
                calendar.add(Calendar.MILLISECOND, gmtMillisecondOffset);
            } else {
                calendar = Calendar.getInstance();
                calendar.setTime(UTCdate);
                calendar.add(Calendar.MILLISECOND, clientUTCOffsetMiliseconds);
            }
            return calendar.getTime();
        }

        private Date convertFromLocalToUTCTimezone(Date localDate) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(localDate);

            int gmtMillisecondOffset = (calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET));
            calendar.add(Calendar.MILLISECOND, -gmtMillisecondOffset);

            return calendar.getTime();
        }

        private void writeCSVHeaderNote(Map<String, String> parameters) throws IOException, PropertyException {
            final String csvHeaderMsg = getMessage(parameters, "OBSERDS_CSVHeaderMessage");

            if (StringUtils.isNotBlank(csvHeaderMsg)) {
                writer.append("\"").append(csvHeaderMsg).append("\"");
                fillEmptyColumns();
                writer.append("\n");
            }
        }

        private void writeCSVFooterNote(Map<String, String> parameters) throws IOException, PropertyException {
            final String csvFooterMsg = getMessage(parameters, "OBSERDS_CSVFooterMessage");

            if (StringUtils.isNotBlank(csvFooterMsg)) {
                writer.append("\n").append("\"").append(csvFooterMsg).append("\"");
                fillEmptyColumns();
            }
        }

        private String getMessage(final Map<String, String> parameters, final String property)
                throws PropertyException {
            OBContext.setAdminMode(true);
            try {
                String csvMessage = null;
                try {
                    Window window = parameters.get("tab") == null || parameters.get("tab").equals("undefined")
                            ? null
                            : OBDal.getInstance().get(Tab.class, parameters.get("tab")).getWindow();
                    csvMessage = Preferences.getPreferenceValue(property, true,
                            OBContext.getOBContext().getCurrentClient(),
                            OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(),
                            OBContext.getOBContext().getRole(), window);
                } catch (PropertyNotFoundException e) {
                    // There is no preference defined
                    csvMessage = null;
                }

                if (StringUtils.isNotBlank(csvMessage)) {
                    csvMessage = Replace.replace(
                            Replace.replace(Replace.replace(OBMessageUtils.messageBD(csvMessage), "\\n", "\n"),
                                    "&quot;", "\""),
                            "\"", "\"\"");
                }

                return csvMessage;
            } finally {
                OBContext.restorePreviousMode();
            }
        }

        private void fillEmptyColumns() throws IOException {
            for (int i = 1; i < fieldProperties.size(); i++) {
                writer.append(fieldSeparator);
            }
        }
    }

    private void handleException(Exception e, HttpServletResponse response) throws IOException {
        log4j.error(e.getMessage(), e);
        if (e instanceof SQLGrammarException) {
            log.error(((SQLGrammarException) e).getSQL());
        }
        if (!response.isCommitted()) {
            final JSONObject jsonResult = new JSONObject();
            final JSONObject jsonResponse = new JSONObject();
            String result = "";
            try {
                jsonResponse.put(JsonConstants.RESPONSE_STATUS, JsonConstants.RPCREQUEST_STATUS_VALIDATION_ERROR);
                jsonResponse.put("error", KernelUtils.getInstance().createErrorJSON(e));
                jsonResult.put(JsonConstants.RESPONSE_RESPONSE, jsonResponse);
                result = jsonResult.toString();
            } catch (JSONException e1) {
                log.error("Error genearating JSON error", e1);
            }
            writeResult(response, result);
        }
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        final Map<String, String> parameters = getParameterMap(request);

        setSessionInfo();

        try {
            if (!hasAccess(request, parameters.get("tabId"))) {
                throw new OBUserException("AccessTableNoView");
            }

            if (DataSourceConstants.FETCH_OPERATION
                    .equals(parameters.get(DataSourceConstants.OPERATION_TYPE_PARAM))) {
                doFetch(request, response, parameters);
                return;
            }

            // note if clause updates parameter map
            if (checkSetIDDataSourceName(request, response, parameters)) {
                final String result = getDataSource(request).add(parameters, getRequestContent(request));
                writeResult(response, result);
            }
        } catch (Exception e) {
            handleException(e, response);
        }
    }

    @Override
    public void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        final Map<String, String> parameters = getParameterMap(request);
        setSessionInfo();
        try {
            // checks and set parameters, if not valid then go away
            if (!checkSetParameters(request, response, parameters)) {
                return;
            }
            if (!hasAccess(request, parameters.get("tabId"))) {
                throw new OBUserException("AccessTableNoView");
            }

            final String id = parameters.get(JsonConstants.ID);
            if (id == null) {
                throw new InvalidRequestException("No id parameter");
            }

            final String result = getDataSource(request).remove(parameters);
            writeResult(response, result);
        } catch (Exception e) {
            handleException(e, response);
        }
    }

    private String getDataSourceNameFromRequest(HttpServletRequest request) {
        final String url = request.getRequestURI();
        if (url.indexOf(getServletPathPart()) == -1) {
            throw new OBException("Request url " + url + " is not valid");
        }
        final int startIndex = 1 + url.indexOf(getServletPathPart()) + getServletPathPart().length();
        final int endIndex = url.indexOf("/", startIndex + 1);
        final String dsName = (endIndex == -1 ? url.substring(startIndex) : url.substring(startIndex, endIndex));

        if (dsName.length() == 0) {
            throw new ResourceNotFoundException("Data source not found using url " + url);
        }
        return dsName;
    }

    @Override
    public void doPut(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        final Map<String, String> parameters = getParameterMap(request);
        setSessionInfo();
        try {
            if (!hasAccess(request, parameters.get("tabId"))) {
                throw new OBUserException("AccessTableNoView");
            }

            // note if clause updates parameter map
            if (checkSetIDDataSourceName(request, response, parameters)) {
                final String result = getDataSource(request).update(parameters, getRequestContent(request));
                writeResult(response, result);
            }

        } catch (Exception e) {
            handleException(e, response);
        }
    }

    private void setSessionInfo() {
        // FIXME: Because of issue #15331 connection is initialized with temporary audit table before
        // setting session info
        // Reset Session Info in DB manually as it was set in the service but actual information is not
        // available till now.
        SessionInfo.setDBSessionInfo(OBDal.getInstance().getConnection(),
                OBPropertiesProvider.getInstance().getOpenbravoProperties().getProperty("bbdd.rdbms"));
    }

    private boolean checkSetParameters(HttpServletRequest request, HttpServletResponse response,
            Map<String, String> parameters) throws IOException {
        if (!request.getRequestURI().contains("/" + servletPathPart)) {
            writeResult(response, JsonUtils.convertExceptionToJson(new InvalidRequestException(
                    "Invalid url, the path should contain the service name: " + servletPathPart)));
            return false;
        }
        final int nameIndex = request.getRequestURI().indexOf(servletPathPart);
        final String servicePart = request.getRequestURI().substring(nameIndex);
        final String[] pathParts = WebServiceUtil.getInstance().getSegments(servicePart);
        if (pathParts.length == 0 || !pathParts[0].equals(servletPathPart)) {
            writeResult(response, JsonUtils.convertExceptionToJson(
                    new InvalidRequestException("Invalid url: " + request.getRequestURI())));
            return false;
        }
        if (pathParts.length == 1) {
            writeResult(response, JsonUtils.convertExceptionToJson(
                    new InvalidRequestException("Invalid url, no datasource name: " + request.getRequestURI())));
            return false;
        }
        final String dsName = pathParts[1];
        parameters.put(DataSourceConstants.DS_NAME_PARAM, dsName);
        if (pathParts.length > 2) {
            // search on the exact id
            parameters.put(JsonConstants.ID, pathParts[2]);
            if (!parameters.containsKey(JsonConstants.TEXTMATCH_PARAMETER)) {
                parameters.put(JsonConstants.TEXTMATCH_PARAMETER, JsonConstants.TEXTMATCH_EXACT);
                parameters.put(JsonConstants.TEXTMATCH_PARAMETER_OVERRIDE, JsonConstants.TEXTMATCH_EXACT);
            }
        }
        return true;
    }

    private Map<String, String> getParameterMap(HttpServletRequest request) {
        final Map<String, String> parameterMap = new HashMap<String, String>();
        for (@SuppressWarnings("rawtypes")
        Enumeration keys = request.getParameterNames(); keys.hasMoreElements();) {
            final String key = (String) keys.nextElement();

            // do simple conversion of array of values to a string
            // TODO: replace when advancedcriteria are supported
            final String[] values = request.getParameterValues(key);
            if (values.length == 1) {
                parameterMap.put(key, values[0]);
            } else {
                final StringBuilder sb = new StringBuilder();
                for (String value : values) {
                    if (sb.length() > 0) {
                        sb.append(JsonConstants.IN_PARAMETER_SEPARATOR);
                    }
                    sb.append(value);
                }
                parameterMap.put(key, sb.toString());
            }
        }
        return parameterMap;
    }

    // NOTE: parameters parameter is updated inside this method
    private boolean checkSetIDDataSourceName(HttpServletRequest request, HttpServletResponse response,
            Map<String, String> parameters) throws IOException {
        if (!request.getRequestURI().contains("/" + servletPathPart)) {
            writeResult(response, JsonUtils.convertExceptionToJson(new InvalidRequestException(
                    "Invalid url, the path should contain the service name: " + servletPathPart)));
            return false;
        }
        final int nameIndex = request.getRequestURI().indexOf(servletPathPart);
        final String servicePart = request.getRequestURI().substring(nameIndex);
        final String[] pathParts = WebServiceUtil.getInstance().getSegments(servicePart);
        if (pathParts.length == 0 || !pathParts[0].equals(servletPathPart)) {
            writeResult(response, JsonUtils.convertExceptionToJson(
                    new InvalidRequestException("Invalid url: " + request.getRequestURI())));
            return false;
        }
        if (pathParts.length == 1) {
            return true;
        }

        final String dsName = pathParts[1];
        parameters.put(DataSourceConstants.DS_NAME_PARAM, dsName);

        if (pathParts.length > 2) {
            // search on the exact id
            parameters.put(JsonConstants.ID, pathParts[2]);
            if (!parameters.containsKey(JsonConstants.TEXTMATCH_PARAMETER)) {
                parameters.put(JsonConstants.TEXTMATCH_PARAMETER, JsonConstants.TEXTMATCH_EXACT);
                parameters.put(JsonConstants.TEXTMATCH_PARAMETER_OVERRIDE, JsonConstants.TEXTMATCH_EXACT);
            }
        }
        return true;
    }

    private DataSourceService getDataSource(HttpServletRequest request) {
        final String dsName = getDataSourceNameFromRequest(request);
        final DataSourceService dataSource = dataSourceServiceProvider.getDataSource(dsName);
        return dataSource;
    }

    private void writeResult(HttpServletResponse response, String result) throws IOException {
        response.setContentType(JsonConstants.JSON_CONTENT_TYPE);
        response.setHeader("Content-Type", JsonConstants.JSON_CONTENT_TYPE);

        final Writer w = response.getWriter();
        w.write(result);
        w.close();
    }

    private String getRequestContent(HttpServletRequest request) throws IOException {
        final BufferedReader reader = request.getReader();
        if (reader == null) {
            return "";
        }
        String line;
        final StringBuilder sb = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            if (sb.length() > 0) {
                sb.append("\n");
            }
            sb.append(line);
        }
        log.debug("REQUEST CONTENT>>>>");
        for (Enumeration<?> enumeration = request.getParameterNames(); enumeration.hasMoreElements();) {
            final Object key = enumeration.nextElement();
            log.debug(key + ": " + request.getParameter((String) key));
        }
        return sb.toString();
    }

    /**
     * Checks access to the current tab, it reuses infrastructure in HttpSecureAppServlet
     */
    private boolean hasAccess(HttpServletRequest req, String tabId) {
        try {
            if (tabId == null || tabId.isEmpty()) {
                return true;
            }
            VariablesSecureApp vars = new VariablesSecureApp(req);
            boolean hasAccess = hasGeneralAccess(vars, "W", tabId);
            if (hasAccess) {
                return true;
            }
            // Here is checked process definition that containing parameters defined as "window",
            // automatically inherit permissions. See issue #29035
            try {
                OBContext.setAdminMode(true);
                Tab tab = OBDal.getInstance().get(Tab.class, tabId);
                if (tab == null) {
                    return false;
                }
                // Search window references that linked with the window of the provided tab.
                OBCriteria<RefWindow> obcRefWindow = OBDal.getInstance().createCriteria(RefWindow.class);
                obcRefWindow.add(Restrictions.eq(RefWindow.PROPERTY_WINDOW, tab.getWindow()));
                if (obcRefWindow.list().size() == 0) {
                    return false;
                }
                final List<Reference> references = new ArrayList<Reference>();
                for (RefWindow refWindow : obcRefWindow.list()) {
                    references.add(refWindow.getReference());
                }

                // Then search parameters that linked with references and get theirs processes.
                OBCriteria<Parameter> obParameters = OBDal.getInstance().createCriteria(Parameter.class);
                obParameters.add(Restrictions.in(Parameter.PROPERTY_REFERENCESEARCHKEY, references));
                if (obParameters.list().size() == 0) {
                    return false;
                }
                final List<Process> obuiapProcesses = new ArrayList<Process>();
                for (Parameter parameter : obParameters.list()) {
                    obuiapProcesses.add(parameter.getObuiappProcess());
                }

                // Finally select all columns that linked with selected processes and get their fields.
                OBCriteria<Column> columns = OBDal.getInstance().createCriteria(Column.class);
                columns.add(Restrictions.in(Column.PROPERTY_OBUIAPPPROCESS, obuiapProcesses));
                if (columns.list().size() == 0) {
                    return false;
                }
                final List<Field> fields = new ArrayList<Field>();
                for (Column col : columns.list()) {
                    for (Field field : col.getADFieldList()) {
                        fields.add(field);
                    }
                }

                Map<String, Object> parameters = new HashMap<String, Object>();
                // Avoid to invoke hasAccess() method with same window.
                final List<String> windowCheck = new ArrayList<String>();
                for (Field f : fields) {
                    // Check access with OBUIAPPProcess & windowId for each field.
                    String windowId = (String) DalUtil.getId(f.getTab().getWindow());
                    if (!windowCheck.contains(windowId)) {
                        windowCheck.add(windowId);
                        parameters.put("windowId", windowId);
                        boolean hasAccessEntity = BaseProcessActionHandler
                                .hasAccess(f.getColumn().getOBUIAPPProcess(), parameters);
                        if (hasAccessEntity) {
                            return true;
                        }
                    }
                }
                // here there is no access to current tabId
                return false;
            } catch (final Exception e) {
                log4j.error("Error checking access: ", e);
                return false;
            } finally {
                OBContext.restorePreviousMode();
            }
        } catch (final Exception e) {
            log4j.error("Error checking access: ", e);
            return false;
        }
    }
}