/* * � Copyright IBM Corp. 2010 * * 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.rpc; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.faces.component.UIComponentBase; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ibm.commons.util.StringUtil; import com.ibm.commons.util.io.json.JsonFactory; import com.ibm.commons.util.profiler.Profiler; import com.ibm.commons.util.profiler.ProfilerAggregator; import com.ibm.commons.util.profiler.ProfilerType; import com.ibm.domino.services.ServiceException; import com.ibm.domino.services.rpc.RpcMethod; import com.ibm.domino.services.rpc.RpcServiceEngine; import com.ibm.jscript.json.JsonJavaScriptFactory; import com.ibm.xsp.ajax.AjaxUtil; import com.ibm.xsp.application.UniqueViewIdManager; import com.ibm.xsp.component.FacesAjaxComponent; import com.ibm.xsp.extlib.util.ExtLibUtil; import com.ibm.xsp.util.JavaScriptUtil; import com.ibm.xsp.util.StateHolderUtil; import com.ibm.xsp.webapp.XspHttpServletResponse; /** * Component that handles JSON RPC services. * * @author priand */ public class UIJsonRpcService extends UIComponentBase implements FacesAjaxComponent { private static final ProfilerType profilerRemoteService = new ProfilerType("XSP JSON-RPC Service"); //$NON-NLS-1$ public static final String COMPONENT_TYPE = "com.ibm.xsp.extlib.rpc.JsonRpcService"; //$NON-NLS-1$ public static final String COMPONENT_FAMILY = "com.ibm.xsp.extlib.JsonRpcService"; //$NON-NLS-1$ public static final String RENDERER_TYPE = "com.ibm.xsp.extlib.rpc.JsonRpcService"; //$NON-NLS-1$ private String pathInfo; private String serviceName; private List<RemoteMethod> methods; private Boolean state; public UIJsonRpcService() { setRendererType(RENDERER_TYPE); //$NON-NLS-1$ } @Override public String getFamily() { return COMPONENT_FAMILY; } public String getPathInfo() { if (null != this.pathInfo) { return this.pathInfo; } ValueBinding _vb = getValueBinding("pathInfo"); //$NON-NLS-1$ if (_vb != null) { return (java.lang.String) _vb.getValue(getFacesContext()); } else { return null; } } public void setPathInfo(String pathInfo) { this.pathInfo = pathInfo; } public String getServiceName() { if (null != this.serviceName) { return this.serviceName; } ValueBinding _vb = getValueBinding("serviceName"); //$NON-NLS-1$ if (_vb != null) { return (java.lang.String) _vb.getValue(getFacesContext()); } else { return null; } } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public List<RemoteMethod> getMethods() { return methods; } public void addMethod(RemoteMethod arg) { if (methods == null) { methods = new ArrayList<RemoteMethod>(); } methods.add(arg); } public boolean isState() { if (null != this.state) { return this.state; } ValueBinding _vb = getValueBinding("state"); //$NON-NLS-1$ if (_vb != null) { Boolean val = (java.lang.Boolean) _vb.getValue(FacesContext.getCurrentInstance()); if (val != null) { return val; } } return false; } public void setState(boolean state) { this.state = state; } @Override public void restoreState(FacesContext _context, Object _state) { Object _values[] = (Object[]) _state; super.restoreState(_context, _values[0]); this.pathInfo = (java.lang.String) _values[1]; this.serviceName = (String) _values[2]; this.methods = StateHolderUtil.restoreList(_context, this, _values[3]); this.state = (Boolean) _values[4]; } @Override public Object saveState(FacesContext _context) { Object _values[] = new Object[5]; _values[0] = super.saveState(_context); _values[1] = pathInfo; _values[2] = serviceName; _values[3] = StateHolderUtil.saveList(_context, methods); _values[4] = state; return _values; } // ================================================================= // Access to the service URL // ================================================================= public String getUrl(FacesContext context, String extraPathInfo) { String pathInfo = getPathInfo(); String url = getUrlPath(context, pathInfo, extraPathInfo); boolean hasQ = false; // Compose the query string String vid = getAjaxViewid(context); if (StringUtil.isNotEmpty(vid)) { url += (hasQ ? "&" : "?") + AjaxUtil.AJAX_VIEWID + "=" + vid; hasQ = true; } // If not path info was specified,use the component ajax id String axTarget = getAjaxTarget(context, pathInfo); if (StringUtil.isNotEmpty(axTarget)) { url += (hasQ ? "&" : "?") + AjaxUtil.AJAX_AXTARGET + "=" + axTarget; hasQ = true; } return url; } public String getUrlPath(FacesContext context, String pathInfo, String extraPathInfo) { ExternalContext externalContext = context.getExternalContext(); String contextPath = externalContext.getRequestContextPath(); String servletPath = externalContext.getRequestServletPath(); StringBuilder b = new StringBuilder(); b.append(contextPath); b.append(servletPath); // If there is a path info, use it as part of the URL if (StringUtil.isNotEmpty(pathInfo)) { b.append("/"); b.append(pathInfo); // the extra path info is only valid in this case if (StringUtil.isNotEmpty(extraPathInfo)) { b.append("/"); b.append(extraPathInfo); } } return b.toString(); } public String getAjaxViewid(FacesContext context) { String vid = UniqueViewIdManager.getUniqueViewId(context.getViewRoot()); if (StringUtil.isNotEmpty(vid)) { return vid; } return null; } public String getAjaxTarget(FacesContext context, String pathInfo) { if (StringUtil.isEmpty(pathInfo)) { return getClientId(context); } return null; } // public String getUrl(FacesContext context) { // ExternalContext externalContext = context.getExternalContext(); // String contextPath = externalContext.getRequestContextPath(); // String servletPath = externalContext.getRequestServletPath(); // // StringBuilder b = new StringBuilder(); // b.append(contextPath); // b.append(servletPath); // b.append("/"); // b.append(pathInfo); // // String vid = UniqueViewIdManager.getUniqueViewId(context.getViewRoot()); // if(StringUtil.isNotEmpty(vid)) { // b.append("?"); // b.append(AjaxUtil.AJAX_VIEWID); // b.append("="); // b.append(vid); // } // return b.toString(); // } // ================================================================= // Ajax service implementation // ================================================================= public boolean handles(FacesContext context) { String reqPathInfo = context.getExternalContext().getRequestPathInfo(); if (StringUtil.isNotEmpty(reqPathInfo)) { String pathInfo = getPathInfo(); if (StringUtil.isEmpty(pathInfo)) { return false; } if (!pathInfo.startsWith("/")) { pathInfo = "/" + pathInfo; } if (!pathInfo.endsWith("/")) { pathInfo += "/"; } if (!reqPathInfo.startsWith("/")) { reqPathInfo = "/" + reqPathInfo; } if (!reqPathInfo.endsWith("/")) { reqPathInfo += "/"; } if (reqPathInfo.startsWith(pathInfo)) { return true; } } return false; } public void processAjaxRequest(FacesContext context) throws IOException { if (Profiler.isEnabled()) { String svc = context.getExternalContext().getRequestServletPath() + context.getExternalContext().getRequestPathInfo(); ProfilerAggregator agg = Profiler.startProfileBlock(profilerRemoteService, svc); long ts = Profiler.getCurrentTime(); try { _processAjaxRequest(context); } finally { Profiler.endProfileBlock(agg, ts); } } else { _processAjaxRequest(context); } } public void _processAjaxRequest(FacesContext context) throws IOException { HttpServletRequest httpRequest = (HttpServletRequest) context.getExternalContext().getRequest(); HttpServletResponse httpResponse = (HttpServletResponse) context.getExternalContext().getResponse(); // Disable the XPages response buffer as this will collide with the engine one // We mark it as committed and use its delegate instead if (httpResponse instanceof XspHttpServletResponse) { XspHttpServletResponse r = (XspHttpServletResponse) httpResponse; r.setCommitted(true); httpResponse = r.getDelegate(); } Engine engine = new Engine(httpRequest, httpResponse); engine.processRequest(); // Save the view state... boolean saveState = isState(); if (saveState) { ExtLibUtil.saveViewState(context); } } protected class Engine extends RpcServiceEngine { protected Engine(HttpServletRequest httpRequest, HttpServletResponse httpResponse) { super(httpRequest, httpResponse); } @Override protected JsonFactory getJsonFactory() throws ServiceException { JsonFactory factory = new JsonJavaScriptFactory(JavaScriptUtil.getJSContext()); return factory; } @Override protected RpcMethod findMethod(String methodName) throws ServiceException { // NTF changed this so it calls the outer classes getter, so it can be overridden if (getMethods() != null) { for (int i = 0; i < getMethods().size(); i++) { RpcMethod m = getMethods().get(i); if (StringUtil.equals(methodName, m.getName())) { return m; } } } return null; } } }