Package org.pentaho.platform.engine.services.solution

Source Code of org.pentaho.platform.engine.services.solution.PojoComponent

/*
* 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() + "\" takes a " + value.getClass() ); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
  }

  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;
  }

}
TOP

Related Classes of org.pentaho.platform.engine.services.solution.PojoComponent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.