/* * � Copyright IBM Corp. 2011 * * Licensed 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.ibm.xsp.extlib.component.rest; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import javax.faces.context.FacesContext; import javax.faces.el.MethodBinding; import javax.faces.el.ValueBinding; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.w3c.dom.Document; import com.ibm.commons.util.FastStringBuffer; import com.ibm.commons.util.StringUtil; import com.ibm.commons.util.io.StreamUtil; import com.ibm.commons.util.io.json.JsonGenerator; import com.ibm.commons.util.io.json.JsonJavaFactory; import com.ibm.commons.util.io.json.JsonParser; import com.ibm.commons.xml.DOMUtil; import com.ibm.designer.runtime.DesignerRuntime; import com.ibm.domino.services.HttpServiceConstants; import com.ibm.domino.services.ServiceException; import com.ibm.domino.services.rest.RestServiceEngine; import com.ibm.domino.services.rest.RestServiceParameters; import com.ibm.jscript.json.JsonJavaScriptFactory; import com.ibm.jscript.types.FBSValue; import com.ibm.xsp.FacesExceptionEx; import com.ibm.xsp.resource.DojoModuleResource; import com.ibm.xsp.util.ManagedBeanUtil; import com.ibm.xsp.util.StateHolderUtil; import com.ibm.xsp.util.TypedUtil; /** * Custom service. * @author Philippe Riand */ public class CustomService extends AbstractRestService { public static final String CONTENTTYPE_DEFAULT = HttpServiceConstants.CONTENTTYPE_BINARY; private String contentType; private String contentDisposition; private MethodBinding doGet; private MethodBinding doPost; private MethodBinding doPut; private MethodBinding doDelete; private String requestVar; private String requestContentType; private String serviceBean; //private String storeDojoType; //private String storeDojoModule; public CustomService() { } public boolean isCompact() { return false; } @Override public String getStoreDojoType() { return null; } @Override public DojoModuleResource getStoreDojoModule() { return null; } public String getContentType() { if (contentType != null) { return contentType; } ValueBinding vb = getValueBinding("contentType"); //$NON-NLS-1$ if (vb != null) { return (String)vb.getValue(getFacesContext()); } return null; } public void setContentType(String contentType) { this.contentType = contentType; } public String getContentDisposition() { if (contentDisposition != null) { return contentDisposition; } ValueBinding vb = getValueBinding("contentDisposition"); //$NON-NLS-1$ if (vb != null) { return (String)vb.getValue(getFacesContext()); } return null; } public void setContentDisposition(String contentDisposition) { this.contentDisposition = contentDisposition; } public MethodBinding getDoGet() { return doGet; } public void setDoGet(MethodBinding doGet) { this.doGet = doGet; } public MethodBinding getDoPost() { return doPost; } public void setDoPost(MethodBinding doPost) { this.doPost = doPost; } public MethodBinding getDoPut() { return doPut; } public void setDoPut(MethodBinding doPut) { this.doPut = doPut; } public MethodBinding getDoDelete() { return doDelete; } public void setDoDelete(MethodBinding doDelete) { this.doDelete = doDelete; } public String getServiceBean() { if (serviceBean != null) { return serviceBean; } ValueBinding vb = getValueBinding("serviceBean"); //$NON-NLS-1$ if (vb != null) { return (String)vb.getValue(getFacesContext()); } return null; } public void setServiceBean(String serviceBean) { this.serviceBean = serviceBean; } public String getRequestVar() { return requestVar; } public void setRequestVar(String requestVar) { this.requestVar = requestVar; } public String getRequestContentType() { if (requestContentType != null) { return requestContentType; } ValueBinding vb = getValueBinding("requestContentType"); //$NON-NLS-1$ if (vb != null) { return (String)vb.getValue(getFacesContext()); } return null; } public void setRequestContentType(String requestContentType) { this.requestContentType = requestContentType; } @Override public Object saveState(FacesContext context) { Object[] state = new Object[10]; state[0] = super.saveState(context); state[1] = contentType; state[2] = contentDisposition; state[3] = requestVar; state[4] = requestContentType; state[5] = StateHolderUtil.saveMethodBinding(context, doGet); state[6] = StateHolderUtil.saveMethodBinding(context, doPost); state[7] = StateHolderUtil.saveMethodBinding(context, doPut); state[8] = StateHolderUtil.saveMethodBinding(context, doDelete); state[9] = serviceBean; return state; } @Override public void restoreState(FacesContext context, Object value) { Object[] state = (Object[])value; super.restoreState(context, state[0]); contentType = (String) state[1]; contentDisposition = (String) state[2]; requestVar = (String) state[3]; requestContentType = (String) state[4]; doGet = StateHolderUtil.restoreMethodBinding(context, getComponent(), state[5]); doPost = StateHolderUtil.restoreMethodBinding(context, getComponent(), state[6]); doPut = StateHolderUtil.restoreMethodBinding(context, getComponent(), state[7]); doDelete = StateHolderUtil.restoreMethodBinding(context, getComponent(), state[8]); serviceBean = (String) state[9]; } public RestServiceEngine createEngine(FacesContext context, UIBaseRestService parent, HttpServletRequest httpRequest, HttpServletResponse httpResponse) { return new ScriptServiceEngine(context,httpRequest,httpResponse); } protected CustomServiceBean findBeanInstance() { String beanName = getServiceBean(); Object b = ManagedBeanUtil.getBean(FacesContext.getCurrentInstance(), beanName); if(b!=null) { if(!(b instanceof CustomServiceBean)) { throw new FacesExceptionEx(null,"Bean {0} is not a CustomServiceBean",beanName); // $NLX-BeanTreeNode.Bean0isnotaCustomServiceBean-1$ } return (CustomServiceBean)b; } return null; } protected class ScriptServiceEngine extends RestServiceEngine { private FacesContext context; public ScriptServiceEngine(FacesContext context, HttpServletRequest httpRequest, HttpServletResponse httpResponse) { super(httpRequest, httpResponse); this.context = context; } public FacesContext getFacesContext() { return context; } @Override public RestServiceParameters getParameters() { return null; } @Override public void renderService() throws ServiceException { try { // Look for a bean to delegate to CustomServiceBean bean = findBeanInstance(); if(bean!=null) { bean.renderService(CustomService.this, this); return; } // Else, look for some script if ("GET".equalsIgnoreCase(getHttpRequest().getMethod())) { // $NON-NLS-1$ renderServiceGet(); } else if ("POST".equalsIgnoreCase(getHttpRequest().getMethod())) { // $NON-NLS-1$ String var = getRequestVar(); if(StringUtil.isNotEmpty(var)) { Object oldValue = getFacesContext().getExternalContext().getRequestMap().get(var); try { pushContent(var); renderServicePost(); } finally { if(oldValue!=null) { TypedUtil.getRequestMap(getFacesContext().getExternalContext()).put(var,oldValue); } else { getFacesContext().getExternalContext().getRequestMap().remove(var); } } } else { renderServicePost(); } } else if ("PUT".equalsIgnoreCase(getHttpRequest().getMethod())) { // $NON-NLS-1$ String var = getRequestVar(); if(StringUtil.isNotEmpty(var)) { Object oldValue = getFacesContext().getExternalContext().getRequestMap().get(var); try { pushContent(var); renderServicePut(); } finally { if(oldValue!=null) { TypedUtil.getRequestMap(getFacesContext().getExternalContext()).put(var,oldValue); } else { getFacesContext().getExternalContext().getRequestMap().remove(var); } } } else { renderServicePut(); } } else if ("DELETE".equalsIgnoreCase(getHttpRequest().getMethod())) { // $NON-NLS-1$ renderServiceDelete(); } else { throw new ServiceException(null,"Unsupported method {0}",getHttpRequest().getMethod()); // $NLX-CustomService_UnsupportedHTTPMethod-1$ } } catch(Exception ex) { throw new ServiceException(ex,"Error while rendering service"); // $NLX-CustomService_ErrorRenderingService-1$ } } public void renderServiceGet() throws Exception { MethodBinding m = getDoGet(); Object result = null; if (m != null) { result = m.invoke(getFacesContext(), null); } renderResult(result); } public void renderServicePost() throws Exception { MethodBinding m = getDoPost(); Object result = null; if (m != null) { result = m.invoke(getFacesContext(), null); } renderResult(result); } public void renderServicePut() throws Exception { MethodBinding m = getDoPut(); Object result = null; if (m != null) { result = m.invoke(getFacesContext(), null); } renderResult(result); } public void renderServiceDelete() throws Exception { MethodBinding m = getDoDelete(); Object result = null; if (m != null) { result = m.invoke(getFacesContext(), null); } renderResult(result); } public void renderError() throws ServiceException { } protected void renderResult(Object result) throws Exception { // Empty content if(result==null) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_TEXT_PLAIN); addHeaders(ct); return; } // Render a piece of text if(result instanceof String) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_TEXT_PLAIN_UTF8); addHeaders(ct); getHttpResponse().setCharacterEncoding(HttpServiceConstants.ENCODING_UTF8); Writer os = new OutputStreamWriter(getHttpResponse().getOutputStream(),HttpServiceConstants.ENCODING_UTF8); os.write((String)result); os.close(); return; } // Render an XML document if(result instanceof Document) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_TEXT_XML_UTF8); addHeaders(ct); getHttpResponse().setCharacterEncoding(HttpServiceConstants.ENCODING_UTF8); OutputStream os = getHttpResponse().getOutputStream(); DOMUtil.serialize(os, (Document)result, isCompact(), true); os.close(); return; } // Render a Json Object if(result instanceof FBSValue) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_APPLICATION_JSON_UTF8); addHeaders(ct); getHttpResponse().setCharacterEncoding(HttpServiceConstants.ENCODING_UTF8); Writer os = new OutputStreamWriter(getHttpResponse().getOutputStream(),HttpServiceConstants.ENCODING_UTF8); JsonGenerator.toJson(new JsonJavaScriptFactory(DesignerRuntime.getJSContext()), os, result, isCompact()); os.close(); return; } if(JsonJavaFactory.instanceEx.isObject(result) || JsonJavaFactory.instanceEx.isArray(result)) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_APPLICATION_JSON_UTF8); addHeaders(ct); getHttpResponse().setCharacterEncoding(HttpServiceConstants.ENCODING_UTF8); Writer os = new OutputStreamWriter(getHttpResponse().getOutputStream(),HttpServiceConstants.ENCODING_UTF8); JsonGenerator.toJson(JsonJavaFactory.instanceEx, os, result, isCompact()); DOMUtil.serialize(os, (Document)result, isCompact(), true); os.close(); return; } // Render a binaty piece if(result instanceof byte[]) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_BINARY); addHeaders(ct); OutputStream os = getHttpResponse().getOutputStream(); os.write((byte[])result); os.close(); return; } if(result instanceof InputStream) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_BINARY); addHeaders(ct); OutputStream os = getHttpResponse().getOutputStream(); InputStream is = (InputStream)result; try { StreamUtil.copyStream(is, os); os.close(); } finally { is.close(); } return; } if(result instanceof File) { String ct = getContentType(HttpServiceConstants.CONTENTTYPE_BINARY); addHeaders(ct); OutputStream os = getHttpResponse().getOutputStream(); InputStream is = new FileInputStream((File)result); try { StreamUtil.copyStream(is, os); os.close(); } finally { is.close(); } return; } throw new ServiceException(null,"Cannot process object of class {0}",result.getClass().getName()); // $NLX-CustomService_UnhandledObjectClass-1$ } protected String getContentType(String def) { String ct = CustomService.this.getContentType(); if(StringUtil.isNotEmpty(ct)) { return ct; } return def; } protected void pushContent(String var) throws Exception { String ct = getRequestContentType(); Object value = null; if(StringUtil.isEmpty(ct) || ct.indexOf(HttpServiceConstants.CONTENTTYPE_TEXT_PLAIN)>=0) { value = readText( ct); } else if(ct.indexOf(HttpServiceConstants.CONTENTTYPE_TEXT_XML)>=0) { Reader r = ((HttpServletRequest)getFacesContext().getExternalContext().getRequest()).getReader(); value = DOMUtil.createDocument(r); } else if(ct.indexOf(HttpServiceConstants.CONTENTTYPE_APPLICATION_JSON)>=0) { Reader r = ((HttpServletRequest)getFacesContext().getExternalContext().getRequest()).getReader(); value = JsonParser.fromJson(new JsonJavaScriptFactory(DesignerRuntime.getJSContext()), r); } else { throw new ServiceException(null,"Cannot process content type {0}",// $NLX-CustomService_CannotProcessContentType-1$ StringUtil.isNotEmpty(ct)?ct:"<empty>");// $NLX-CustomService_ContentTypeEmpty-1$ } TypedUtil.getRequestMap(getFacesContext().getExternalContext()).put(var,value); } protected String getRequestContentType() { String ct = CustomService.this.getRequestContentType(); if(StringUtil.isNotEmpty(ct)) { return ct; } ct = (String)getFacesContext().getExternalContext().getRequestHeaderMap().get("Content-type"); //$NON-NLS-1$ return ct; } protected String readText(String ct) throws Exception { FastStringBuffer b = new FastStringBuffer(); b.append(((HttpServletRequest)getFacesContext().getExternalContext().getRequest()).getReader()); return b.toString(); } protected void addHeaders(String contentType) { if(StringUtil.isNotEmpty(contentType)) { getHttpResponse().setContentType(contentType); } String contentDisposition = getContentDisposition(); if(StringUtil.isNotEmpty(contentDisposition)) { getHttpResponse().addHeader("Content-disposition",contentDisposition); //$NON-NLS-1$ } } } }