/* See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Esri Inc. licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.esri.gpt.catalog.gxe;
import com.esri.gpt.catalog.schema.MetadataDocument;
import com.esri.gpt.catalog.schema.Schema;
import com.esri.gpt.catalog.schema.SchemaException;
import com.esri.gpt.catalog.schema.Schemas;
import com.esri.gpt.catalog.schema.UnrecognizedSchemaException;
import com.esri.gpt.framework.context.BaseServlet;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.jsf.FacesContextBroker;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.util.Val;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang3.StringEscapeUtils;

/**
 * The Geoportal XML editor servlet.
 */
@SuppressWarnings("serial")
public class GxeServlet extends BaseServlet {
  
  /** class variables ========================================================= */
  
  /** The Logger. */
  private static Logger LOGGER = Logger.getLogger(GxeServlet.class.getName());
  
  /** methods ================================================================= */

  /**
   * Executes a request.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  @Override
  protected void execute(HttpServletRequest request,
                         HttpServletResponse response, 
                         RequestContext context) 
    throws Exception {
            
    // determine the request type and execute
    try {
      if (request.getRequestURI() != null) {
        String sLcUri = request.getRequestURI().toLowerCase();
        
        if (sLcUri.endsWith("/definition/type")) {
          this.executeDefinitionType(request,response,context);
        } else if (sLcUri.endsWith("/definition/types")) {
          this.executeDefinitionTypes(request,response,context);
        } else if (sLcUri.endsWith("/echo/attachment")) {
          this.executeEchoAttachment(request,response,context);          
        } else if (sLcUri.endsWith("/i18n")) {
          this.executeI18N(request,response,context);
        } else if (sLcUri.endsWith("/interrogate")) {
          this.executeInterrogate(request,response,context);
        } else if (sLcUri.endsWith("/interrogate/details")) {
          this.executeInterrogateDetails(request,response,context,true);
        } else if (sLcUri.endsWith("/interrogate/multipart")) {
          this.executeInterrogate(request,response,context);
          
        // this is for backward compatibility, 
        // should be using /definition/type
        } else if (sLcUri.endsWith("/definition")) {
          this._executeDefinitionRequest(request,response,context);
          
        // this is for backward compatibility, 
        // should be using /interrogate/details
        } else if (sLcUri.endsWith("/details")) {
          this.executeInterrogateDetails(request,response,context,false);
          
        // this is for backward compatibility, 
        // should be using /definition/types
        } else if (sLcUri.endsWith("/types")) {
          this.executeDefinitionTypes(request,response,context);
          
        }
      } 
    } finally {
      context.onExecutionPhaseCompleted();
    }
  }
  
  /**
   * Handles a request to return the editor definition for
   * a supplied schema key or location.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void executeDefinitionType(HttpServletRequest request,
                                     HttpServletResponse response, 
                                     RequestContext context) 
    throws Exception {
    
    String sMimeType = "application/json";
    String sResponse = "";
    String sCallback = "";
    MessageBroker msgBroker = null;
    try {
      
      // determine the response format
      String f = Val.chkStr(request.getParameter("f"));
      if (f.equalsIgnoreCase("pjson")) {
        sMimeType = "text/plain";
      }
      sCallback = Val.chkStr(request.getParameter("callback"));
      
      // determine the definition
      GxeDefinition definition = null;
      String key = Val.chkStr(request.getParameter("key"));
      String loc = Val.chkStr(request.getParameter("loc"));
      if (key.length() > 0) {
        Schemas schemas = context.getCatalogConfiguration().getConfiguredSchemas();
        Schema schema = schemas.get(key);
        if (schema == null) {
          throw new SchemaException("Unsupported schema key.");
        } else {
          definition = schema.getGxeEditorDefinition();
        }
      } else if (loc.length() > 0) {
        definition = new GxeDefinition();
        definition.setFileLocation(loc); 
      } 
      String sCfg = "";
      if (definition != null) {
        msgBroker = this.getMessageBroker(request,response,context,msgBroker);
        sCfg = this.generateDefinition(request,response,context,msgBroker,definition);
      }
      
      // set the response
      if ((sCfg != null) && (sCfg.length() > 0)) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\r\n\"cfgDefinition\":").append(sCfg);
        sb.append("\r\n}");
        sResponse = sb.toString();
      } else {
        throw new SchemaException("Unsupported schema type.");
      }

    } catch (Throwable t) {
      if (!(t instanceof SchemaException)) {
        LOGGER.log(Level.WARNING,"Error processing request.",t);
      }
      sResponse = this.generateJsonError(request,response,context,msgBroker,t);
    } 
    
    // write the response
    //LOGGER.finest("gxeResponse:\n"+sResponse);
    if ((sResponse != null) && (sResponse.length() > 0)) {
      if ((sCallback != null) && (sCallback.length() > 0)) {
        sResponse = sCallback+"("+sResponse+")";
      }
      writeCharacterResponse(response,sResponse,"UTF-8",sMimeType+";charset=UTF-8");
    }
  }

  /**
   * Handles a request for the list of defined editor types.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void executeDefinitionTypes(HttpServletRequest request,
                                      HttpServletResponse response, 
                                      RequestContext context) 
    throws Exception {
    
    String sMimeType = "application/json";
    String sResponse = "";
    String sCallback = "";
    MessageBroker msgBroker = null;
    try {
      sCallback = Val.chkStr(request.getParameter("callback"));
      
      // make the list of defined editor types
      StringBuilder sb = new StringBuilder();
      sb.append("{\"types\": [");
      Schemas schemas = context.getCatalogConfiguration().getConfiguredSchemas();
      if (schemas != null) {
        int n = 0;
        msgBroker = this.getMessageBroker(request,response,context,msgBroker);
        for (Schema schema: schemas.values()) {
          if (schema.getEditable()) {
            GxeDefinition definition = schema.getGxeEditorDefinition();
            if (definition != null) {
              String key = schema.getKey();
              String label = null;
              if (schema.getLabel() != null) {
                String resKey = schema.getLabel().getResourceKey();
                if ((resKey != null) && (resKey.length() > 0)) {
                  label = msgBroker.retrieveMessage(resKey);
                }
              }
              if ((label == null) || (label.length() == 0)) {
                label = key;
              }
              n++;
              if (n > 1) sb.append(",");
              sb.append("\r\n\t");
              sb.append("{\"key\": \"").append(Val.escapeStrForJson(key)).append("\"");
              sb.append(", \"label\": \"").append(Val.escapeStrForJson(label)).append("\"}");
            }
          }
        }
      }  
      sb.append("\r\n]}");
      sResponse = sb.toString();

    } catch (Throwable t) {
      LOGGER.log(Level.WARNING,"Error processing request.",t);
      sResponse = this.generateJsonError(request,response,context,msgBroker,t);
    } 
    
    // write the response
    //LOGGER.finest("gxeResponse:\n"+sResponse);
    if ((sResponse != null) && (sResponse.length() > 0)) {
      if ((sCallback != null) && (sCallback.length() > 0)) {
        sResponse = sCallback+"("+sResponse+")";
      }
      writeCharacterResponse(response,sResponse,"UTF-8",sMimeType+";charset=UTF-8");
    }
  }   
  
  /**
   * Handles a request to return a posted XML document as
   * a content disposition attachment.
   * <br/>The posted mime-type should be: application/x-www-form-urlencoded
   * <br/>The posted URL parameter name should be: xml
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void executeEchoAttachment(HttpServletRequest request,
                                     HttpServletResponse response, 
                                     RequestContext context) 
    throws Exception {
    
    String sResponse = "";
    String sFilename = "";
    try {
      
      // read the posted xml
      String sXml = this.readPostedXml(request,response,context);
      if ((sXml != null) && (sXml.length() > 0)) {
        sResponse = sXml;
        sFilename = Val.chkStr(request.getParameter("filename"));
        if (sFilename.length() == 0) sFilename = "metadata.xml";
      } else {
        sFilename = "error.txt";
        sResponse = "Error: The posted XML was empty.";
      }
    } catch (Throwable t) {
      LOGGER.log(Level.WARNING,"Error processing request.",t);
      String sMsg = Val.chkStr(t.getMessage());
      if (sMsg.length() == 0) sMsg = t.toString();
      sFilename = "error.xml";
      sResponse = "Error: "+sMsg;
    } 
    
    // write the response
    //LOGGER.finest("gxeResponse:\n"+sResponse);
    response.setContentType("APPLICATION/OCTET-STREAM; charset=UTF-8");
    response.setHeader("Content-Disposition","attachment; filename=\""+StringEscapeUtils.escapeHtml4(Val.stripControls(sFilename))+"\"");          
    writeCharacterResponse(response,sResponse,"UTF-8","APPLICATION/OCTET-STREAM; charset=UTF-8");
  }    
  

  
  /**
   * Handles a request to lookup a set of localized strings.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void executeI18N(HttpServletRequest request,
                           HttpServletResponse response, 
                           RequestContext context) 
    throws Exception {
    
    String sMimeType = "application/json";
    String sResponse = "";
    String sCallback = "";
    MessageBroker msgBroker = null;
    try {
      
      // determine the response format
      String f = Val.chkStr(request.getParameter("f"));
      if (f.equalsIgnoreCase("pjson")) {
        sMimeType = "text/plain";
      }
      sCallback = Val.chkStr(request.getParameter("callback"));
      msgBroker = this.getMessageBroker(request,response,context,msgBroker);
      
      // determine the property keys
      String sKeys = Val.chkStr(request.getParameter("keys"));
      String[] aKeys = sKeys.split(",");
      
      // lookup the strings
      StringBuilder sb = new StringBuilder();
      sb.append("{\"i18n\": {");
      int n = 0;
      if ((aKeys != null) && (aKeys.length > 0)) {
        for (String sKey: aKeys) {
          String sValue = msgBroker.retrieveMessage(sKey);
          n++;
          if (n > 1) sb.append(",");
          sb.append("\r\n\t");
          sb.append("\"").append(Val.escapeStrForJson(sKey)).append("\": ");
          sb.append("\"").append(Val.escapeStrForJson(sValue)).append("\"");
        }
      }
      sb.append("\r\n}}");
      sResponse = sb.toString();
    
    } catch (Throwable t) {
      LOGGER.log(Level.WARNING,"Error processing request.",t);
      sResponse = this.generateJsonError(request,response,context,msgBroker,t);
    } 
    
    // write the response
    //LOGGER.finest("gxeResponse:\n"+sResponse);
    if ((sResponse != null) && (sResponse.length() > 0)) {
      if ((sCallback != null) && (sCallback.length() > 0)) {
        sMimeType = "text/plain";
        sResponse = sCallback+"("+sResponse+")";
      }
      writeCharacterResponse(response,sResponse,"UTF-8",sMimeType+";charset=UTF-8");
    }
  }

  /**
   * Handles a request to interrogate the editor definition for
   * an uploaded XML document.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void executeInterrogate(HttpServletRequest request,
                                  HttpServletResponse response, 
                                  RequestContext context) 
    throws Exception {
    
    String sMimeType = "application/json";
    String sResponse = "";
    boolean isIE = false;
    boolean bWrap = false;
    MessageBroker msgBroker = null;
    try {
       isIE = Val.chkBool(request.getParameter("isIE"),false);
      
      // interrogate the posted XMl, generate the definition
      bWrap = Val.chkBool(request.getParameter("wrap"),false);
      String sXml = this.readPostedXml(request,response,context);
      Schema schema = this.interrogateSchema(context,sXml);
      String sCfg = "";
      GxeDefinition definition = schema.getGxeEditorDefinition();
      if (definition != null) {
        msgBroker = this.getMessageBroker(request,response,context,msgBroker);
        sCfg = this.generateDefinition(request,response,context,msgBroker,definition);
      }
      
      // set the response
      if ((sCfg != null) && (sCfg.length() > 0)) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\r\n\"cfgDefinition\":").append(sCfg);
        sb.append(",\r\n\"xml\":\"").append(
            Val.escapeStrForJson(schema.getActiveDocumentXml())).append("\"");
        sb.append("\r\n}");
        sResponse = sb.toString();
      } else {
        throw new SchemaException("Unsupported XML type.");
      }

    } catch (Throwable t) {
      if (!(t instanceof SchemaException)) {
        LOGGER.log(Level.WARNING,"Error processing request.",t);
      }
      sResponse = this.generateJsonError(request,response,context,msgBroker,t);
    } 
    
    // write the response
    //LOGGER.finest("gxeResponse:\n"+sResponse);
    if ((sResponse != null) && (sResponse.length() > 0)) {
    	if(isIE){
    		sMimeType = "text/html";
    		sResponse = "<script type=\"text/javascript\">var gxeImportFileResponse="+sResponse+";</script>";    		
    	}
      // responseObject= is required for the 
      // HTML5 based dojox.form.Uploader 1.6
    	else if (bWrap) sResponse = "responseObject="+sResponse;
      writeCharacterResponse(response,sResponse,"UTF-8",sMimeType+";charset=UTF-8");
    }
  }
  
  /**
   * Handles a request for a details view of an XML document.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void executeInterrogateDetails(HttpServletRequest request,
                                         HttpServletResponse response, 
                                         RequestContext context,
                                         boolean defaultToJson) 
    throws Exception {
    
    String sMimeType = "application/json";
    String sFormat = "json";
    String sResponse = "";
    MessageBroker msgBroker = null;
    try {

      // determine the response format
      if (!defaultToJson) {
        sFormat = "htmlFragment";
      }
      String f = Val.chkStr(request.getParameter("f"));
      if (f.equalsIgnoreCase("htmlFragment")) {
        sFormat = "htmlFragment";
      } else if (f.equalsIgnoreCase("json")) {
        sFormat = "json";
      } else if (f.equalsIgnoreCase("pjson")) {
        sFormat = "json";
      }
      if (!sFormat.equals("json")) {
        sMimeType = "text/plain";
      }
      
      // interrogate the posted XMl, generate the details
      String sXml = this.readPostedXml(request,response,context);
      Schema schema = this.interrogateSchema(context,sXml);
      msgBroker = this.getMessageBroker(request,response,context,msgBroker);
      String sDetails = this.generateDetails(request,response,context,msgBroker,schema);
      sDetails = Val.chkStr(sDetails);
      
      // set the response
      if (sFormat.equals("json")) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"details\": \"").append(Val.escapeStrForJson(sDetails)).append("\"");
        sb.append("}");
        sResponse = sb.toString();
      } else {
        sResponse = sDetails;
      }
      
    } catch (Throwable t) {
      if (!(t instanceof SchemaException)) {
        LOGGER.log(Level.WARNING,"Error processing request.",t);
      }
      if (sFormat.equals("json")) {
        sResponse = this.generateJsonError(request,response,context,msgBroker,t);
      } else {
        String sMsg = Val.chkStr(t.getMessage());
        if (sMsg.length() == 0) sMsg = t.toString();
        sResponse = Val.escapeXmlForBrowser(sMsg);
      }
    }
    
    // write the response
    // LOGGER.finest("gxeResponse:\n"+sResponse);
    if ((sResponse != null) && (sResponse.length() > 0)) {
      writeCharacterResponse(response,sResponse,"UTF-8",sMimeType+";charset=UTF-8");
    }
  }  
  
  /**
   * Generates the JSON editor definition.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @param msgBroker the message broker
   * @param definition the GXE definition
   * @return the JSON definition
   * @throws Exception if a processing exception occurs
   */
  private String generateDefinition(HttpServletRequest request,
                                    HttpServletResponse response,
                                    RequestContext context,
                                    MessageBroker msgBroker,
                                    GxeDefinition definition) 
    throws Exception {
    GxeContext gxeContext = new GxeContext();
    gxeContext.setMessageBroker(msgBroker);
    if (definition.getRootElement() == null) {
      GxeLoader loader = new GxeLoader();
      loader.loadDefinition(gxeContext,definition);
    }
    GxeJsonSerializer serializer = new GxeJsonSerializer();
    return serializer.asJson(gxeContext,definition);
  }
  
  /**
   * Generates a details view for an interrogated schema.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @param msgBroker the message broker
   * @param schema the interrogated schema
   * @return the details
   * @throws Exception if a processing exception occurs
   */
  private String generateDetails(HttpServletRequest request,
                                 HttpServletResponse response,
                                 RequestContext context,
                                 MessageBroker msgBroker,
                                 Schema schema) 
    throws Exception {
    String sXslt = Val.chkStr(schema.getDetailsXslt());
    if (sXslt.length() > 0) {
      MetadataDocument document = new MetadataDocument();
      return document.transformDetails(
          schema.getActiveDocumentXml(),sXslt,msgBroker);
    }
    return null;
  }
  
  /**
   * Generates a JSON based error object.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @param msgBroker the message broker
   * @param t the exception
   * @return the JSON string
   */
  private String generateJsonError(HttpServletRequest request,
                                   HttpServletResponse response,
                                   RequestContext context,
                                   MessageBroker msgBroker,
                                   Throwable t) {
    String sMsg = Val.chkStr(t.getMessage());
    if (sMsg.length() == 0) sMsg = t.toString();
    
    // TODO localized messages here
    if (sMsg.contains("Unrecognized metadata schema.")) {
    } else if (sMsg.contains("Unable to parse document.")) {
    } else if (sMsg.contains("Unsupported XML type.")) {  
    } 
    
    StringBuilder sb = new StringBuilder();
    sb.append("{\"error\":{");
    sb.append("\"message\": \"").append(Val.escapeStrForJson(sMsg)).append("\"");
    sb.append("}}");
    return sb.toString();
  }
  
  /**
   * Ensures the existence of a message broker.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @param msgBroker a message broker reference 
   *   (if null then a new message broker will be generated)
   * @return the message broker
   */
  private MessageBroker getMessageBroker(HttpServletRequest request,
                                         HttpServletResponse response,
                                         RequestContext context,
                                         MessageBroker msgBroker) {
    // TODO check for lang parameter
    if (msgBroker != null) {
      return msgBroker;
    } else {
      FacesContextBroker fcb = new FacesContextBroker(request,response);
      return fcb.extractMessageBroker();
    }
  }
  
  /**
   * Interrogates a schema based upon a posted XML document.
   * @param context the request context
   * @param xml the XML string
   * @return the corresponding schema
   * @throws Exception if a processing exception occurs
   */
  private Schema interrogateSchema(RequestContext context, String xml) 
    throws Exception {
    String sXml = Val.chkStr(Val.removeBOM(xml));
    if (sXml.length() == 0) {
      throw new UnrecognizedSchemaException("Unrecognized metadata schema.");
    } else {
      MetadataDocument document = new MetadataDocument();
      Schema schema = document.prepareForView(context,sXml);
      return schema;
    }
  }  
  
  /**
   * Reads a posted XML document.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private String readPostedXml(HttpServletRequest request,
                               HttpServletResponse response, 
                               RequestContext context) 
    throws Exception {
    String sXml = "";
    String sContentType = Val.chkStr(request.getContentType());
    if (sContentType.toLowerCase().startsWith("multipart/form-data;")) {
      String sMultipartAttribute = "uploadedfiles[]";
      Object oFile = request.getAttribute(sMultipartAttribute);
      if (oFile == null) {
      	sMultipartAttribute = "uploadedfile";
      	oFile = request.getAttribute(sMultipartAttribute);
      }
      if ((oFile != null) && (oFile instanceof FileItem)) {
        FileItem item = (FileItem)oFile;
        sXml = Val.chkStr(Val.removeBOM(item.getString("UTF-8")));
      }
    } else if (sContentType.toLowerCase().startsWith("application/x-www-form-urlencoded")) {
      String sFormUrlParamater = "xml";
      sXml = Val.chkStr(Val.removeBOM(request.getParameter(sFormUrlParamater)));
    } else {
      sXml = Val.chkStr(Val.removeBOM(this.readInputCharacters(request)));
    }
    
    /*
    // check for multipart/form-data
    String sMultipartAttribute = "uploadedfiles[]";
    Object oFile = request.getAttribute(sMultipartAttribute);
    if ((oFile != null) && (oFile instanceof FileItem)) {
      FileItem item = (FileItem)oFile;
      sXml = Val.chkStr(Val.removeBOM(item.getString("UTF-8")));
    } else {
      
      // check for application/x-www-form-urlencoded
      String sFormUrlParamater = "xml";
      sXml = Val.chkStr(request.getParameter(sFormUrlParamater));
      if (sXml == null) {
        
        // check the raw post body
        sXml = this.readInputCharacters(request);
      }
    }
    */
    return sXml;
  }  
  
  
  /**
   * Handles a request for an editor definition.
   * <br/>Original method, should be deprecated.
   * @param request the HTTP servlet request
   * @param response the HTTP servlet response
   * @param context the request context
   * @throws Exception if a processing exception occurs
   */
  private void _executeDefinitionRequest(HttpServletRequest request,
                                         HttpServletResponse response, 
                                         RequestContext context) 
    throws Exception {
            
    // initialize
    String mimeType = "text/plain";
    String sResponse = "";
    
    // handle a request for an editor definition
    try {
      GxeContext gxeContext = new GxeContext();
      FacesContextBroker fcb = new FacesContextBroker(request,response);
      gxeContext.setMessageBroker(fcb.extractMessageBroker());
      
      GxeDefinition definition = null;
      String key = Val.chkStr(request.getParameter("key"));
      String loc = Val.chkStr(request.getParameter("loc"));
      if (key.length() > 0) {
        Schemas schemas = context.getCatalogConfiguration().getConfiguredSchemas();
        Schema schema = schemas.get(key);
        if (schema != null) {
          definition = schema.getGxeEditorDefinition();
        }
      } else if (loc.length() > 0) {
        definition = new GxeDefinition();
        definition.setFileLocation(loc); 
      } else {
        
        String xml = null;
        try {
          xml = this.readInputCharacters(request);
        } catch (IOException e) {
          LOGGER.log(Level.WARNING,"Error processing request.",e);
          throw new ServletException("400: IOException while reading request body.");
        }
        xml = Val.chkStr(Val.removeBOM(xml));
        if (xml.length() > 0) {
          MetadataDocument document = new MetadataDocument();
          Schema schema = document.prepareForView(context,xml);
          if (schema != null) {
            definition = schema.getGxeEditorDefinition();
          }
        }
        
      }
      if (definition != null) {
        if (definition.getRootElement() == null) {
          GxeLoader loader = new GxeLoader();
          loader.loadDefinition(gxeContext,definition);
        }
        GxeJsonSerializer serializer = new GxeJsonSerializer();
        sResponse = serializer.asJson(gxeContext,definition);
        
        String sCallbackParam = Val.chkStr(request.getParameter("callback"));
        if (sCallbackParam.length() > 0) {
          sResponse = sCallbackParam+"("+sResponse+")";
        }
      }
      
    } catch (SchemaException e) {
      LOGGER.log(Level.WARNING,"Error processing request.",e);
      String sMsg = e.toString();
      if (sMsg.contains("Unrecognized metadata schema.")) {
        throw new ServletException("409: Unrecognized metadata schema.");
      } else if (sMsg.contains("Unable to parse document.")) {
        throw new ServletException("409: Unable to parse document as XML.");
      } else {
        throw new ServletException("409: Unable process request.");
      } 

    } catch (Exception e) {
      throw e;
    } finally {
      context.onExecutionPhaseCompleted();
    }
    
    // write the response
    LOGGER.finest("gxeResponse:\n"+sResponse);
    if ((sResponse != null) && (sResponse.length() > 0)) {
      writeCharacterResponse(response,sResponse,"UTF-8",mimeType+";charset=UTF-8");
    }
  }  

}
