Java tutorial
/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * * Copyright 2006 - 2013 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.engine.services.solution; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.Element; import org.pentaho.commons.connection.IPentahoResultSet; import org.pentaho.commons.connection.IPentahoStreamSource; import org.pentaho.platform.api.engine.IActionParameter; import org.pentaho.platform.api.engine.IActionSequenceResource; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IPluginManager; import org.pentaho.platform.api.engine.PluginBeanException; import org.pentaho.platform.api.repository.IContentItem; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.engine.core.output.SimpleContentItem; import org.pentaho.platform.engine.core.solution.SystemSettingsParameterProvider; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.messages.Messages; import org.pentaho.platform.util.messages.LocaleHelper; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.GenericSignatureFormatError; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This class interfaces with a plain old Java object and makes it available as a component within the Pentaho * platform. * * Resources and Input Parameters are set on a Pojo via setters. Any public setter is available to both, without * bias. The setters are called individually for Resources and Input Parameters and as such may be called for each * one should a parameter exist in both forms. Resources are processed first, followed by Input Parameters giving * Input Parameters the power to override. * * All public getters are exposed through the PojoComponent for consumption as Output Parameters within an Action * Sequence. * * There exist special methods which may be defined on a Pojo (No interface needed) in order to better facilitate * integration to the platform. They are as follows: configure validate execute done getOutputs setResources * setInputs setLogger setSession setOutputStream / getMimeType * * @author jamesdixon * @deprecated Pojo components are deprecated, use {@link IAction} * */ public class PojoComponent extends ComponentBase { private static final long serialVersionUID = 7064470160805918218L; protected Object pojo; Map<String, Method> getMethods = new HashMap<String, Method>(); Map<String, List<Method>> setMethods = new HashMap<String, List<Method>>(); Method executeMethod = null; Method validateMethod = null; Method doneMethod = null; Method resourcesMethod = null; Method runtimeInputsMethod = null; Method runtimeOutputsMethod = null; Method loggerMethod = null; Method sessionMethod = null; Method configureMethod = null; public Log getLogger() { return LogFactory.getLog(PojoComponent.class); } @Override public void done() { if (doneMethod != null && pojo != null) { try { doneMethod.invoke(pojo, (Object[]) null); } catch (Exception e) { e.printStackTrace(); } } } protected void callMethod(Method method, Object value) throws Throwable { List<Method> methods = Arrays.asList(new Method[] { method }); callMethods(methods, value); } protected void callMethods(List<Method> methods, Object value) throws Throwable { if (value instanceof String) { callMethodWithString(methods, value.toString()); return; } boolean done = false; for (Method method : methods) { Class<?>[] paramClasses = method.getParameterTypes(); if (paramClasses.length != 1) { // we don't know how to handle this throw new GenericSignatureFormatError(); } Class<?> paramclass = paramClasses[0]; // do some type safety. this would be the point to do automatic type conversions if (value instanceof IPentahoResultSet && paramclass.equals(IPentahoResultSet.class)) { done = true; method.invoke(pojo, new Object[] { (IPentahoResultSet) value }); break; } else if (value instanceof java.lang.Boolean && (paramclass.equals(Boolean.class) || paramclass.equals(boolean.class))) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof java.lang.Integer && (paramclass.equals(Integer.class) || paramclass.equals(int.class))) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof java.lang.Long && (paramclass.equals(Long.class) || paramclass.equals(long.class))) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof java.lang.Double && (paramclass.equals(Double.class) || paramclass.equals(double.class))) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof java.lang.Float && (paramclass.equals(Float.class) || paramclass.equals(float.class))) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof IPentahoStreamSource && paramclass.equals(IPentahoStreamSource.class)) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof Date && paramclass.equals(Date.class)) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof BigDecimal && paramclass.equals(BigDecimal.class)) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof IContentItem && paramclass.equals(IContentItem.class)) { done = true; method.invoke(pojo, new Object[] { value }); break; } else if (value instanceof IContentItem && paramclass.equals(String.class)) { done = true; method.invoke(pojo, new Object[] { value.toString() }); break; } else if (paramclass.equals(IPentahoSession.class)) { done = true; method.invoke(pojo, new Object[] { (IPentahoSession) value }); break; } else if (paramclass.equals(Log.class)) { done = true; method.invoke(pojo, new Object[] { (Log) value }); break; } } if (!done) { // Try invoking the first instance with what we have try { methods.get(0).invoke(pojo, new Object[] { value }); } catch (Exception ex) { throw new IllegalArgumentException("No implementation of method \"" + Method.class.getName() //$NON-NLS-1$ + "\" takes a " + value.getClass()); //$NON-NLS-1$ } } } protected void callMethodWithString(List<Method> methodList, String value) throws Throwable { boolean done = false; value = applyInputsToFormat(value); // Search ALL instances of a given method for an implementation // that takes a single string for (Method method : methodList) { Class<?>[] paramClasses = method.getParameterTypes(); if (paramClasses.length != 1) { // we don't know how to handle this throw new GenericSignatureFormatError(); } Class<?> paramclass = paramClasses[0]; if (paramclass.equals(String.class)) { done = true; method.invoke(pojo, new Object[] { value }); break; } } if (!done) { for (Method method : methodList) { Class<?>[] paramClasses = method.getParameterTypes(); if (paramClasses.length != 1) { // we don't know how to handle this throw new GenericSignatureFormatError(); } Class<?> paramclass = paramClasses[0]; if (paramclass.equals(Boolean.class) || paramclass.equals(boolean.class)) { done = true; method.invoke(pojo, new Object[] { new Boolean(value) }); break; } else if (paramclass.equals(Integer.class) || paramclass.equals(int.class)) { done = true; method.invoke(pojo, new Object[] { new Integer(value) }); break; } else if (paramclass.equals(Long.class) || paramclass.equals(long.class)) { done = true; method.invoke(pojo, new Object[] { new Long(value) }); break; } else if (paramclass.equals(Double.class) || paramclass.equals(double.class)) { done = true; method.invoke(pojo, new Object[] { new Double(value) }); break; } else if (paramclass.equals(Float.class) || paramclass.equals(float.class)) { done = true; method.invoke(pojo, new Object[] { new Float(value) }); break; } else if (paramclass.equals(BigDecimal.class)) { done = true; method.invoke(pojo, new Object[] { new BigDecimal(value) }); break; } } } if (!done) { throw new GenericSignatureFormatError(); } } @SuppressWarnings({ "unchecked" }) @Override protected boolean executeAction() throws Throwable { Set<?> inputNames = getInputNames(); Element defnNode = (Element) getComponentDefinition(); // first do the system settings so that component settings and inputs can override them if necessary // if( pojo instanceof IConfiguredPojo ) { if (getMethods.containsKey("CONFIGSETTINGSPATHS") && configureMethod != null) { //$NON-NLS-1$ Method method = getMethods.get("CONFIGSETTINGSPATHS"); //$NON-NLS-1$ Set<String> settingsPaths = (Set<String>) method.invoke(pojo, new Object[] {}); Iterator<String> keys = settingsPaths.iterator(); Map<String, String> settings = new HashMap<String, String>(); SystemSettingsParameterProvider params = new SystemSettingsParameterProvider(); while (keys.hasNext()) { String path = keys.next(); String value = params.getStringParameter(path, null); if (value != null) { settings.put(path, value); } } configureMethod.invoke(pojo, new Object[] { settings }); } // set the PentahoSession if (sessionMethod != null) { callMethods(Arrays.asList(new Method[] { sessionMethod }), getSession()); } // set the logger if (loggerMethod != null) { callMethods(Arrays.asList(new Method[] { loggerMethod }), getLogger()); } Map<String, Object> inputMap = new HashMap<String, Object>(); // look at the component settings List<?> nodes = defnNode.selectNodes("*"); //$NON-NLS-1$ for (int idx = 0; idx < nodes.size(); idx++) { Element node = (Element) nodes.get(idx); // inputs may typically contain a dash in them, such as // something like "report-definition" and we should expect // a setter as setReportDefinition, so we will remove the // dashes and everything should proceed as expected String name = node.getName().replace("-", "").toUpperCase(); //$NON-NLS-1$ //$NON-NLS-2$ if (!name.equals("CLASS") && !name.equals("OUTPUTSTREAM")) { //$NON-NLS-1$ //$NON-NLS-2$ String value = node.getText(); List<Method> method = setMethods.get(name); if (method != null) { callMethodWithString(method, value); } else if (runtimeInputsMethod != null) { inputMap.put(name, value); } else { // Supress error (For string/value replacement) getLogger().warn(Messages.getInstance().getString("PojoComponent.UNUSED_INPUT", name)); //$NON-NLS-1$ } } } Iterator<?> it = null; // now process all of the resources and see if we can call them as setters Set<?> resourceNames = getResourceNames(); Map<String, IActionSequenceResource> resourceMap = new HashMap<String, IActionSequenceResource>(); if (resourceNames != null && resourceNames.size() > 0) { it = resourceNames.iterator(); while (it.hasNext()) { String name = (String) it.next(); IActionSequenceResource resource = getResource(name); name = name.replace("-", ""); //$NON-NLS-1$ //$NON-NLS-2$ resourceMap.put(name, resource); List<Method> methods = setMethods.get(name.toUpperCase()); if (methods != null) { for (Method method : methods) { Class<?>[] paramTypes = method.getParameterTypes(); if (paramTypes.length == 1) { Object value = null; if (paramTypes[0] == InputStream.class) { value = resource.getInputStream(RepositoryFilePermission.READ, LocaleHelper.getLocale()); } else if (paramTypes[0] == IActionSequenceResource.class) { value = resource; } else if (paramTypes[0] == String.class) { value = getRuntimeContext().getResourceAsString(resource); } else if (paramTypes[0] == Document.class) { value = getRuntimeContext().getResourceAsDocument(resource); } callMethod(method, value); } } //CHECKSTYLE IGNORE EmptyBlock FOR NEXT 3 LINES } else { // BISERVER-2715 we should ignore this as the resource might be meant for another component } } } // now process all of the inputs, overriding the component settings it = inputNames.iterator(); while (it.hasNext()) { String name = (String) it.next(); Object value = getInputValue(name); // now that we have the value, we can fix the name name = name.replace("-", ""); //$NON-NLS-1$ //$NON-NLS-2$ List<Method> methods = setMethods.get(name.toUpperCase()); if (methods != null) { callMethods(methods, value); } else if (runtimeInputsMethod != null) { inputMap.put(name, value); } else { // Supress error (For string/value replacement) getLogger().warn(Messages.getInstance().getString("PojoComponent.UNUSED_INPUT", name)); //$NON-NLS-1$ } } if (resourceMap.size() > 0 && resourcesMethod != null) { // call the resources setter resourcesMethod.invoke(pojo, new Object[] { resourceMap }); } if (inputMap.size() > 0 && runtimeInputsMethod != null) { // call the generic input setter runtimeInputsMethod.invoke(pojo, new Object[] { inputMap }); } if (getOutputNames().contains("outputstream") && setMethods.containsKey("OUTPUTSTREAM") //$NON-NLS-1$ //$NON-NLS-2$ && getMethods.containsKey("MIMETYPE")) { //$NON-NLS-1$ // get the mime-type // Get the first method to match Method method = getMethods.get("MIMETYPE"); //$NON-NLS-1$ String mimeType = (String) method.invoke(pojo, new Object[] {}); String mappedOutputName = "outputstream"; //$NON-NLS-1$ if ((getActionDefinition() != null) && (getActionDefinition().getOutput("outputstream") != null)) { //$NON-NLS-1$ mappedOutputName = getActionDefinition().getOutput("outputstream").getPublicName(); //$NON-NLS-1$ } // this marks the HttpOutputHandler as contentDone=true, causing the MessageFormatter to not print an error IContentItem contentItem = getOutputContentItem(mappedOutputName, mimeType); if (!(contentItem instanceof SimpleContentItem)) { // SimpleContentItem can't handle being added to outputs because it // doesn't have a getInputStream(), and the path used to return // null. setOutputValue("outputstream", contentItem); //$NON-NLS-1$ } // set the output stream OutputStream out = contentItem.getOutputStream(getActionName()); method = setMethods.get("OUTPUTSTREAM").get(0); //$NON-NLS-1$ method.invoke(pojo, new Object[] { out }); } if (validateMethod != null) { Object obj = validateMethod.invoke(pojo, (Object[]) null); if (obj instanceof Boolean) { Boolean ok = (Boolean) obj; if (!ok) { return false; } } } // now execute the pojo Boolean result = Boolean.FALSE; if (executeMethod != null) { result = (Boolean) executeMethod.invoke(pojo, new Object[] {}); } else { // we can only assume we are ok so far result = Boolean.TRUE; } // now handle outputs Set<?> outputNames = getOutputNames(); // first get the runtime outputs Map<String, Object> outputMap = new HashMap<String, Object>(); if (runtimeOutputsMethod != null) { outputMap = (Map<String, Object>) runtimeOutputsMethod.invoke(pojo, new Object[] {}); } it = outputNames.iterator(); while (it.hasNext()) { String name = (String) it.next(); //CHECKSTYLE IGNORE EmptyBlock FOR NEXT 3 LINES if (name.equals("outputstream")) { //$NON-NLS-1$ // we should be done } else { IActionParameter param = getOutputItem(name); Method method = getMethods.get(name.toUpperCase()); if (method != null) { Object value = method.invoke(pojo, new Object[] {}); param.setValue(value); } else { Object value = outputMap.get(name); if (value != null) { param.setValue(value); } else { throw new NoSuchMethodException(name); } } } } return result.booleanValue(); } @Override public boolean init() { // nothing to do here return true; } @Override protected boolean validateAction() { boolean ok = false; if (pojo == null && isDefinedInput("class")) { //$NON-NLS-1$ String className = getInputStringValue("class"); //$NON-NLS-1$ // try to load the class from a plugin IPluginManager pluginMgr = PentahoSystem.get(IPluginManager.class, getSession()); if (pluginMgr != null && pluginMgr.isBeanRegistered(className)) { try { pojo = pluginMgr.getBean(className); // "className" is actually the plugin bean id in this case } catch (PluginBeanException e) { error("Could not load bean class from plugin", e); //$NON-NLS-1$ return false; } } // the bean class was not found in a plugin, so try the default classloader if (pojo == null) { try { // TODO support loading classes from the solution repository Class<?> aClass = getClass().getClassLoader().loadClass(className); pojo = aClass.newInstance(); } catch (Exception ex) { error("Could not load bean class", ex); //$NON-NLS-1$ return false; } } } if (pojo != null) { // By the time we get here, we've got our class try { Method[] methods = pojo.getClass().getMethods(); // create a method map for (Method method : methods) { String name = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); if (name.equals("getOutputs")) { //$NON-NLS-1$ runtimeOutputsMethod = method; } else if (name.equals("setInputs")) { //$NON-NLS-1$ runtimeInputsMethod = method; } else if (name.equals("setResources")) { //$NON-NLS-1$ resourcesMethod = method; } else if (name.equals("setLogger")) { //$NON-NLS-1$ if (paramTypes.length == 1 && paramTypes[0] == Log.class) { loggerMethod = method; } } else if (name.equals("setSession")) { //$NON-NLS-1$ if (paramTypes.length == 1 && paramTypes[0] == IPentahoSession.class) { sessionMethod = method; } } else if (name.equalsIgnoreCase("configure")) { //$NON-NLS-1$ configureMethod = method; } else if (name.startsWith("set")) { //$NON-NLS-1$ name = name.substring(3).toUpperCase(); if (name.equals("CLASS")) { //$NON-NLS-1$ warn(Messages.getInstance().getString("PojoComponent.CANNOT_USE_SETCLASS")); //$NON-NLS-1$ } else { if (!setMethods.containsKey(name)) { setMethods.put(name, new ArrayList<Method>()); } setMethods.get(name).add(method); } } else if (name.startsWith("get")) { //$NON-NLS-1$ name = name.substring(3).toUpperCase(); getMethods.put(name, method); } else if (name.equalsIgnoreCase("execute")) { //$NON-NLS-1$ executeMethod = method; } else if (name.equalsIgnoreCase("validate")) { //$NON-NLS-1$ validateMethod = method; } else if (name.equalsIgnoreCase("done")) { //$NON-NLS-1$ doneMethod = method; } } ok = true; } catch (Throwable ex) { error("Could not load object class", ex); //$NON-NLS-1$ } } return ok; } @Override protected boolean validateSystemSettings() { // nothing to do here, the pojo must do this during its init return true; } public void setPojo(Object pojo) { this.pojo = pojo; } }