Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    BI (Business Intelligence) like reporting using SmartGWT

    Hi,

    We're trying to delve deep now into ListGrids etc. We're trying to mimick a user-interface that we built in .net which more-or-less mimicked a BI (Business Inteligence) tool such as Business Objects -- that is, a user can select fields of interest and the underlying SQL is generated against the datawarehouse with appropraite aggregate expressions, group by statements, where clauses, and having clauses.

    So far, SmartGWT's filter builder seems unbeatable in terms of looks. However, I'm not sure the current version of SmartGWT that we have (the details of which are at end of this thread) provides pre-built support for basic BI reporting against a datawrehouse. That is, if our queries are going to use "aggregate expresions" for the metrics and therefore "group bys" for the attributes, how does SmartGWT handle it?

    Specifically, we looked at the sample "Dynamic Reporting" (http://www.smartclient.com/smartgwtee/showcase/#sql_dynamic_reporting) and then used it for our mock-up, but based on what we saw, we believe that we need *2* filter builders for any basic datawarehouse report -- one for the "where clause" and the other for the "having" clause (or, in the case of SmartGWT, we think they've subsituted a "having" syntax statement with another "where clause" that executes as part of an outer query, right?)

    So, our questions are:
    1) So, how can "insert" the criteria that's generated by the 2 filter builders into the ultimate SQL statment
    2) Is there a pre-built interface/component to allow a user to "select" fields of interest (ie this is similar to the hide/unhide feature in SmartGWT listgrid, except that when a user "hides" a field, it should not appear in the SQL select statement at all)

    *****3) Assuming that #2 is solved somehow (eg custom form etc.), is there an easy way to have the SQL statement be smart enough to put the "appropriate" fields in the group by clause (ie only the attributes shoudl be in the group-by clause; everything else, such as aggregate expressions, should not be in the group-by clause, though they will be in the "select" clause.) (This point #3 is the main question.)

    In general, we're wondering how SmartGWT handles the above? Or, if it's not in 2.4 or 2.5 SmartGWT EE, is it being worked on in the nighlty builds? (Our best educated guess is that if SmartGWT did not natively address the requirements of BI reporting, there's got to be a way that we could (easily?) customize SmartGWT as follows: write front-end javascript code (written originally in Java of course, but compiled using GWT) so that we could overide the variables "$defaultWhereClause" and "$defaultSelectClause" etc and maybe even create new session variables that we can stick in to the datasource definition, perhaps using Velocity? If we're on the right track, could you please point us in the right direction of how we can save a session variable/object through the front-end SmartGWT Java code...we couldn't find any such reference in the SmartGWT forums. We did though find referenes on how we can access session variables.)

    Thanks in advance.

    Regards,
    Gautam

    Additional infracstructure details are below:
    ==================================================

    OS: Windows XP Pro
    IDE: MyEclipse 9.0 with Google Plugin for Eclipse (2.3.1)
    SmartGWT EE 2.4
    Browwer: Mozilla Firefox 4.0.1
    GWT SDK: 2.2
    Sun JDK 1.6.0_13

    #2
    If you want the user to be able to have distinct "where" and "having" criteria on the same fields, then yes you'll need to two FilterBuilders.

    If you want SmartGWT to generate both the "where" and "having" clauses from arbitrary AdvancedCriteria, one way to do so is the new SQLDataSource.getSQLClause() API, which is new in 3.x nightlies. You can form one DSRequest with the "having" clause criteria, take the String from getSQLClause() and make it available in Velocity for a second dsRequest via dsRequest.addToTemplateContext().

    No there's not a pre-built component for field selection but this is really easy, just create one ListGridRecord per field and allow multiple selection. As far as separating attributes vs aggregates, only your user interface is going to know which fields the user is setting as aggregates, so showing only non-aggregated fields for grouping is up to you. But again, for this and other situations where you refer to "session variables", dsRequest.addToTemplateContext() is probably the API you're looking for.

    Note that a QueryDesigner component along these lines implemented as a new standard component would be a valid Feature Sponsorship if you'd just like the whole problem solved for you.

    Comment


      #3
      Hi,

      Thanks for the response.

      So, we downloaded the latest nightly build of SmartGWT (and have redone so again today), but we are wondering if there may be an issue with "passing objects" from the client to the server. Based on our readings, we should 1) *not* attempt to use DSRequest.setAttributes(), as these attributes will be DROPPED at the server side; instead, we should use setData. However, before even trying to go down that path, we tried the following very simple SERVER side code (which you may notice is 99% identical to sample code):

      ============

      public class SupplyItemDMI {

      Logger log = new Logger(SupplyItemDMI.class.getName());

      // By default, for a DSRequest of type "fetch", a method named "fetch" is invoked. You can
      // customize this via the <serverObject> declaration.
      public DSResponse fetch(DSRequest dsRequest)
      throws Exception
      {
      log.info("procesing DMI fetch operation");
      System.out.println("The where clause is: ***************************");
      System.out.println(SQLDataSource.getSQLClause(SQLClauseType.Where, dsRequest));

      com.isomorphic.rpc.RPCManager rpc = dsRequest.getRPCManager();
      Object data = rpc.getData(); // ******* LINE THAT GENERATED ERROR BELOW!!!!!!!!!!
      System.out.println("The data is " + data.toString());

      ============

      The logic "rpc.getData()" above gave the following error when running in "dev" mode in Eclipse. So our questions first are: why this error? And second, we presume that we can/should in the client code call the methode DSRequest.setData() and pass in a single custom object which is really composed of TWO objects (which represent the two filter buider objects), and then we can extract the needed info on the server side using "getData()" and then we can construct the needed dsRequests (as suggested in last week's response from Isomorphic)?

      =================
      ERROR OUTPUT-->
      =================

      === 2011-10-03 19:39:01,139 [0-17] DEBUG DataSourceDMI - Invocation threw exception
      java.lang.ClassCastException: com.isomorphic.datasource.DSRequest cannot be cast to com.isomorphic.rpc.RPCRequest
      at com.isomorphic.rpc.RPCManager.getRequest(RPCManager.java:444)
      at com.isomorphic.rpc.RPCManager.getData(RPCManager.java:398)
      at com.smartgwt.sample.server.SupplyItemDMI.fetch(SupplyItemDMI.java:34)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at com.isomorphic.base.Reflection.adaptArgsAndInvoke(Reflection.java:950)
      at com.isomorphic.datasource.DataSourceDMI.execute(DataSourceDMI.java:386)
      at com.isomorphic.datasource.DataSourceDMI.execute(DataSourceDMI.java:64)
      at com.isomorphic.datasource.DSRequest.execute(DSRequest.java:1832)
      at com.isomorphic.servlet.IDACall.handleDSRequest(IDACall.java:199)
      at com.isomorphic.servlet.IDACall.processRPCTransaction(IDACall.java:156)
      at com.isomorphic.servlet.IDACall.processRequest(IDACall.java:121)
      at com.isomorphic.servlet.IDACall.doPost(IDACall.java:73)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
      at com.isomorphic.servlet.BaseServlet.service(BaseServlet.java:152)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
      at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
      at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1097)
      at com.isomorphic.servlet.CompressionFilter.doFilter(CompressionFilter.java:259)
      at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088)
      at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
      at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
      at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
      at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
      at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
      at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
      at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
      at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
      at org.mortbay.jetty.Server.handle(Server.java:324)
      at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
      at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
      at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
      at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
      at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
      at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
      at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
      === 2011-10-03 19:39:03,389 [0-17] WARN RequestContext - dsRequest.execute() failed:
      java.lang.ClassCastException: com.isomorphic.datasource.DSRequest cannot be cast to com.isomorphic.rpc.RPCRequest
      at com.isomorphic.rpc.RPCManager.getRequest(RPCManager.java:444)
      at com.isomorphic.rpc.RPCManager.getData(RPCManager.java:398)
      at com.smartgwt.sample.server.SupplyItemDMI.fetch(SupplyItemDMI.java:34)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at com.isomorphic.base.Reflection.adaptArgsAndInvoke(Reflection.java:950)
      at com.isomorphic.datasource.DataSourceDMI.execute(DataSourceDMI.java:386)
      at com.isomorphic.datasource.DataSourceDMI.execute(DataSourceDMI.java:64)
      at com.isomorphic.datasource.DSRequest.execute(DSRequest.java:1832)
      at com.isomorphic.servlet.IDACall.handleDSRequest(IDACall.java:199)
      at com.isomorphic.servlet.IDACall.processRPCTransaction(IDACall.java:156)
      at com.isomorphic.servlet.IDACall.processRequest(IDACall.java:121)
      at com.isomorphic.servlet.IDACall.doPost(IDACall.java:73)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
      at com.isomorphic.servlet.BaseServlet.service(BaseServlet.java:152)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
      at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
      at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1097)
      at com.isomorphic.servlet.CompressionFilter.doFilter(CompressionFilter.java:259)
      at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088)
      at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
      at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
      at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
      at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
      at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
      at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
      at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
      at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
      at org.mortbay.jetty.Server.handle(Server.java:324)
      at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
      at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
      at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
      at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
      at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
      at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
      at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)

      Comment


        #4
        Did you read the docs for that API? :)

        Convenience method for getting the data of a single RPCRequest. Calling this method is equivalent to calling getRequest.getData()
        You've got a DSRequest coming and you're calling an API that only applies to RPCRequests.

        dsRequest.setAttributes() is for storing client-side information across the asynchronous processing of the request.

        Have you read the QuickStart Guide? That's the best place to start and provides critical context about how the system is meant to be used.

        Comment

        Working...
        X