Google’s love/hate relationship with JS and love of flutter

I for one think it’s absolutely hilarious how google devs despise js as an enterprise development tool. But can’t stand to develop in it. I remember when angular1 came out. And thought to my self “Wow google actually made a library in pure js?” and recommends you to develop in js – impressive. Some years back I worked in a project called GWT – you may have heard of it. It compiles Java down into JS and provides a somewhat awesome way to dev apps – outside of compile times and crappy looking widgets.

Anyway – So when angular1 came out I was like “Whoa awesome – pure js”. Used it for a project to build a fully functional phone with WebRTC and flash. Worked great – I really enjoyed the thoroughness that google put into it. But I couldnt get over the fact – having done a prior project in GWT/GXT that google was just laying it all out and doing a pure js library. I mean “I thought google despised js as a pure dev language.”

Now, years later – Google releases angular 2 through 5 – “AHHHHH Im right – they ditched js in favor of TypeScript” haha. Now we’re using TypeScript. So I guess I was right. Google cant stand js.

So now – I see flutter and Im like – wow, that’s pretty cool – FINALLY a competitor to react-native. But wait there’s more! https://flutter.io/. Google revamped the dart language and basically said screw js – lets use Dart! Honestly Im kind of excited about flutter as it uses a REAL language to build mobile apps. Although I think they should have used Groovy but that’s fine. We got a REAL language.

Google’s UI development frameworks (that could have been done in js but google said f’ that):

  • GWT version 1.0 RC 1 was released on May 16, 2006 (Java)
  • Angular JS 1.0 release October 20, 2010 (JS – But not for long!) 
  • Angular 2 May 2016 (Typescript preferential treatment – (remove JS from title))
  • Flutter May 2017 (Dart Lang) 

Point here is. Google doesnt want to build enterprise applications with JS. And I kind of agree with them here. React-Native comes out using JS some years back. Google could have been like “Oh yea, lets destroy that with our own js library to take out react-native”. Instead they implement something completely different using the Dart Lang. Interesting. Lets see if it gains traction. The Dart lang has gotten NO love from the community. Lets hope in time that google can bring a viable competitor to react-native for the pour developer souls that do not want to write everything in js.

Here’s some Flutter Code:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
 
class AnimatedListSample extends StatefulWidget {
  @override
  _AnimatedListSampleState createState() => new _AnimatedListSampleState();
}
 
class _AnimatedListSampleState extends State {
  final GlobalKey _listKey = new GlobalKey();
  ListModel _list;
  int _selectedItem;
  int _nextItem; // The next item inserted when the user presses the '+' button.
 
  @override
  void initState() {
    super.initState();
    _list = new ListModel(
      listKey: _listKey,
      initialItems: [0, 1, 2],
      removedItemBuilder: _buildRemovedItem,
    );
    _nextItem = 3;
  }
}

Anyway – pretty cool eh? I think its got potential.

Note to Dart Team: Make an actual layout language and ide! Xcode using Storyboard’s did it right. Building components inline in the code is not abstracting the design enough. The design should be separate. Layout and code not a good mix. Designers need jobs too!

-Austin

J2EE Filter that modifies a Stripes response for Velocity rendering

I combined Stripes and Velocity into a J2EE filter that came out to be extremely useful, so I could have Stripes/JSPs and have Velocity rendering with every request.

I love the toolbox.xml concept of velocity so I couldnt live without it, but wanted to use stripes for its amazing action controller functionality. Here is a filter which modifies the stripes response and then parses the response through the Velocity Template Engine.

 
package org.adk.java.viewfilter;
 
import org.adk.java.locale.LocaleTool;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.adk.java.store.StoreWebApplication;
import org.adk.java.store.WebSessionBean;
import org.adk.java.log.KLogger;
import org.adk.java.secure.SecureRedirectorActionBean;
import org.adk.java.stripes.ActionBeanUrlManager;
import org.adk.java.viewfilter.tools.BaseTool;
import org.adk.java.viewfilter.tools.BreadCrumbTool;
import org.adk.java.viewfilter.tools.PropertyTool;
import org.adk.java.viewfilter.tools.RequestTool;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.tools.view.XMLToolboxManager;
 
/********************************************************************************************
 * <p>
 * Our basic filter, all it does is take our WebSessionBean which is a wrapper around
 * the current session and passes it in the request, so that all jsps and other objects
 * can access the data, such as the current users shopping cart, his user info, and
 * other pertinent details to the session for this user.
 * <p>
 * In addition to basic session addition, we also take the content and
 * add in the velocity template engine so the session can be accessed from
 * velocity, and all content, after stripes is done with it is modified
 * with velocity.
 * <p>
 * We perform the following steps:
 * -------------------------------
 * <ol>
 * <li>  Load in the velocity toolbox so these can be exposed and used throughout
 *    the web app.
 * 
 * <li> Add the session to the queue and attributes
 *
 * <li>  Perform the filter chain and take the modified content in the form
 *    of a PrintWriter
 *
 * <li>  Modify this content with the Velocity Engine.
 *
 * <li>  Using the localeTool, we can then render all translations loaded in
 *    the appropriate strings.xml file.
 *</ol>
 *
 * @author Austin Kottke
 ********************************************************************************************/
public class ViewFilterEngine implements Filter {
 
    private StoreWebApplication webApp = StoreWebApplication.getInstance();
    private KLogger logger = KLogger.getLogger(this.toString());
    private VelocityContext velocityContext = new VelocityContext();
    private static ViewFilterEngine instance ;
    private FilterConfig filterConfig;
    private ServletContext servletContext;
 
    // Security redirector
    private SecureRedirectorActionBean secureRedir = new SecureRedirectorActionBean();
 
    // The actual path of the primary servlet. From here we can load
    // in all other files based on this path.
    private String currentPath;
    private String toolboxPath;
    private XMLToolboxManager toolboxManager;
    private Map toolBox;
 
    // A reference to the locale tool.
    private LocaleTool localeTool;
    private RequestTool requestTool;
    private BreadCrumbTool breadCrumbTool;
 
    private boolean initialized = false;
 
    // Action url manager
    private ActionBeanUrlManager actionUrlManager ;
 
    private void ViewFilterEngine(){
 
    }
 
    /**
     *
     * @return
     */
    public static ViewFilterEngine getInstance(){
        if (instance == null ){
            return new ViewFilterEngine();
        }
        return instance;
    }
 
 
 
    /**
     *
     * @param aConfig
     * @throws ServletException
     */
    public void init(FilterConfig aConfig) throws ServletException {
        this.filterConfig=aConfig;
        this.currentPath = filterConfig.getServletContext().getRealPath("");
        this.toolboxPath = currentPath + filterConfig.getInitParameter("ToolBoxPath");
        this.servletContext = filterConfig.getServletContext() ;
 
        logger.initFilters( currentPath );
        instance = this;
        try {
 
            toolboxManager = new XMLToolboxManager();
            Velocity.init(currentPath + "/WEB-INF/classes/velocity.properties");
            logger.debug("Loading velocity toolbox: " );
            toolboxManager.load( toolboxPath );
 
            // Initialize the velocity tools and add them to the
            // velocity context so they can be used in the app
            initVelocityTools();
 
        } catch (Exception ex) {
            Logger.getLogger(ViewFilterEngine.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
 
    /**
     * Propery loads and initializes all of the velocity tools so they can be used
     * throughout the application.
     */
    public void initVelocityTools()
    {
         // Retrieve the toolbox
         toolBox = toolboxManager.getToolbox(null);
         Set keys = toolBox.keySet();
         if( keys.size() <= 0 ) return;
 
         for( Object key : keys )
         {
            String k  = key.toString();
            Object obj = toolBox.get(k);
            logger.debug("Tool object ["+k+"] = " + obj);
 
            velocityContext.put(k, obj);
 
            try
            {
                if( obj.getClass().getSuperclass().toString().indexOf("BaseTool") >= 0  )
                {
                    logger.debug("Setting tool properties: " + obj);
                    BaseTool t = (BaseTool ) obj;
                    logger.debug("Property set!");
                    t.setServletFilePath(currentPath);
                    t.setContext(velocityContext);
                    t.setServletContext(servletContext);
                }
            } catch(Exception e){}
        }
         try
         {
 
             if( toolBox.get("breadcrumbTool") != null )
             breadCrumbTool = (BreadCrumbTool) toolBox.get("breadcrumbTool");
 
             if( toolBox.get("locale") != null)
             localeTool = (LocaleTool) toolBox.get("locale");
 
             if( toolBox.get("requestTool") != null)
             requestTool = (RequestTool) toolBox.get("requestTool");
 
         } catch( Exception e )
         {
             logger.error("Error - missing locale tool and / or request tool in the toolbox.xml");
         }
    }
 
    /**========================================================================
     * Here we add the session to the webApp singleton and add the user to the
     * session queue if he is not already there. We also modify the content
     * with VTL.
     * 
     * @param request
     * @param response
     * @param chain
     * @throws java.io.IOException
     * @throws javax.servlet.ServletException
     *========================================================================*/
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        HttpServletRequest req =  (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        PropertyTool p  = (PropertyTool) toolBox.get("propertyTool");
        /*********************************************************************
         * Initalize the tools that need the current request object.
         *********************************************************************/
 
        doRequestInitialization(p,req,resp);
        p.initProperties(req.getContextPath(),  req.getRequestURI() );
        if( ! initialized )
        {
                webApp.doInitialization();
                actionUrlManager = ActionBeanUrlManager.getActionUrlManager();
                initialized = true;
        }
 
        /*********************************************************************
         * Take the current session, and if it is not already added to
         * to the current request, add this to the current session.
         *********************************************************************/
        HttpSession s = addSessionToQueue( req, resp);
 
        /*********************************************************************
         * Check if the page should be redirected to Https.
         *********************************************************************/
       if( doHttpsCheck(p, req,resp)  )
           return;
 
        /*********************************************************************
         * Return the html stream.
         *********************************************************************/
        CharArrayWriter caw = new CharArrayWriter();
 
        logger.debug("Retrieving html stream... ["+req.getRequestURI()+"]");
 
        try
        {
 
         PrintWriter out = getHtmlStream(chain, req, resp, caw );
 
        /*********************************************************************
         * render the html stream with velocity and persist the session
         * so velocity can access this.
         *********************************************************************/
 
        velocityContext.put( "velocitySession", s );
 
        out.write(  renderVelocity( caw.toString() ).toString() );
        } catch(Exception e )
        {
            logger.error("Error: " + e.toString() );
        }
 
    }
 
    /***
     * Initializes the tools and the property tool for requests that need the
     * information. It also initializes all of the tools that are instances
     * of base tools, including setting the current request and setting the
     * current locale for the localeTool.
     * 
     * @param p
     * @param request
     * @param response
     */
    public void doRequestInitialization(PropertyTool p, HttpServletRequest request, HttpServletResponse response)
    {
        try
        {
            breadCrumbTool.setServletContextPath( request.getContextPath() );
            String curLocale = request.getParameter("locale");
            if( curLocale != null ) {
                Locale l = null;
                String lang = curLocale.split("_")[0];
                if( curLocale.indexOf("_") >= 0)
                {
                    String country = curLocale.split("_")[1];
                    l = new Locale(lang, country);
                }
                else
                {
                    if( lang.indexOf("en") >=0 )
                    l = new Locale(lang, "US");
                    else
                    l = new Locale(lang,lang);
 
                }
                localeTool.setCurrentLocale( l );
            }
 
 
 
            Collection c = toolBox.values();
            Iterator i = c.iterator();
            while( i.hasNext() )
            {
                try
                {
                     Object o = i.next();
 
                     if( o.getClass().getSuperclass().toString().indexOf("BaseTool") >= 0  )
                     {
                         //logger.debug("Base tool! " + o);
                         BaseTool t = (BaseTool) o;
                         t.setRequest(request);
                     }
 
                }catch(Exception e ){}
            }
 
 
 
        } catch(Exception e ){
            logger.error("Did you not load in the propertyTool ? - Check your velocity toolbox.xml file.");
            logger.error("Error: you might be missing a /global.properties in the site root! " + e.toString());
        }
    }
    /***
     * Modifys the request if this page should be secured or not. If it is
     * supposed to be secured, based on the global properties 'securePage' we
     * then forward to Https. If it is not supposed to be secured, then
     * we redirect to a regular url.
     *
     * @param p The property tool for lookup of the securePage property.
     * @param request The current request
     * @param response The response request.
     * @return
     */
 
    public boolean doHttpsCheck(PropertyTool p, HttpServletRequest request, HttpServletResponse response)
    {
 
        logger.debug("Secure page: " + p.get("securePage") );
 
        try
        {
            if( p.get("securePage").equals("1") && request.isSecure() == false)
            {
                logger.debug("This page should be secured...");
                String securedUrl = secureRedir.returnSecureUrl(request, request.getRequestURI() );
                logger.debug("Secure url: "  + securedUrl );
                try {
                    response.sendRedirect(securedUrl);
                    return true;
                } catch (Exception ex) {
                    logger.error("Error redirecting to url...");
                    return false;
                }
            }
 
            // Do check for if this should not be a secure page, but is in https mode.
 
 
            if( p.get("securePage").equals("0") && request.isSecure() == true)
            {
                logger.debug("This page should be NOT be secured!");
                 String regularUrl = secureRedir.returnRegularUrl(request, request.getRequestURI() );
                 try {
                    logger.debug("Redirecting to: " + regularUrl);
                    response.sendRedirect(regularUrl);
                    return true;
                } catch (Exception ex) {
                    logger.error("Error redirecting to url... " +ex.toString());
                    return false;
                }
            }
        }
        catch(Exception e ){
            return false;
        }
        return false;
    }
 
    /*
     *
     * Adds the session bean to our web app session queue, so this can be
     * referred to throughout the application
     *
     * @param request
     * @param response
     * @return Returns the HttpSession instance
     **/
 
    public HttpSession addSessionToQueue(HttpServletRequest request, HttpServletResponse response )
    {
        HttpSession currentSession = (HttpSession) request.getSession();
        WebSessionBean cartSession = null;
 
        logger.debug("Total sessions: " + webApp.getActiveSessions());
 
        boolean isSessioninQueue = webApp.isSessionInQueue(currentSession.getId() );
 
        if( ! isSessioninQueue ) {
            cartSession = new WebSessionBean ( currentSession );
            webApp.addSession( cartSession );
        }
 
        if( isSessioninQueue ) cartSession = (WebSessionBean) webApp.getSession(currentSession.getId());
 
        // on the request for this particular user, make sure the session is included
        // with the shopping info, etc.
        request.setAttribute( "sessionBean", cartSession );
 
        return cartSession;
 
    }
 
 
 
    /**
     * Returns a PrintWriter with the current Html stream, after it has gone
     * through all redirects and filters.
     *
     * We do this by making a wrapper around the current html response and then
     * writing the current content after the  filters have been performed.
     * Once we have a writer we can then modify the response with whatever is
     * needed. Currently we modify the current response with the
     * velocity template engine, so we can also use VTL in a jsp or html.
     *
     * @param chain
     * @param request
     * @param response
     * @param writer
     * @return A PrintWriter with the modified html content.
     **/
 
    public PrintWriter getHtmlStream(FilterChain chain, HttpServletRequest request, HttpServletResponse response, CharArrayWriter writer ){
        /****************************************************************
         * Add in our custom response wrapper, so all filters and content
         * are written to, then we can add in our own content once we
         * have the outputted content in string form.
         ****************************************************************/
        PrintWriter out = null;
 
        try
        {
            out = response.getWriter();
            CharRequestWrapper reqWrapper = new CharRequestWrapper( request, request.getRequestURI(), request.getRequestURL() );
            CharResponseWrapper respWrapper = new CharResponseWrapper( response );
 
            /****************************************************************
             * If what was requested was an html page, then we need to
             * still retrieve the content based on the request, since we use
             * jsp as our presentation technology we still want to serve
             * up the content based on an html string.
             ****************************************************************/
 
            chain.doFilter(reqWrapper, respWrapper);
 
            /****************************************************************
             * If we have a 404, then we forward to the error pages...
             ****************************************************************/
            boolean sendErrorPage = false;
            if( respWrapper.getErrorCode() == HttpServletResponse.SC_NOT_FOUND ) sendErrorPage = true;
            if( respWrapper.getErrorCode() == HttpServletResponse.SC_INTERNAL_SERVER_ERROR ) sendErrorPage = true;
 
            if( sendErrorPage ) respWrapper.sendRedirect(reqWrapper.getContextPath() + "/engine/error/error.jsp");
 
 
            writer.write( respWrapper.getContent());
 
 
        } catch (Throwable ex) {
            Logger.getLogger(ViewFilterEngine.class.getName()).log(Level.SEVERE, null, ex);
        } 
 
        return out;
    }
 
    /**
     *
     * @param htmlContent
     * @return
     */
    public CharArrayWriter renderVelocity(String htmlContent)
    {
 
        CharArrayWriter outputVelocity = new CharArrayWriter();
        try {
 
 
            Velocity.evaluate(velocityContext, outputVelocity, "log", htmlContent);
        } catch (Throwable ex) {
            Logger.getLogger(ViewFilterEngine.class.getName()).log(Level.SEVERE, null, ex);
        }
 
        return outputVelocity;
    }
 
    /**
     *
     * @return
     */
    public XMLToolboxManager getToolboxManager() {
        return toolboxManager;
    }
 
    /**
     *
     * @param t
     */
    public void setToolboxManager(XMLToolboxManager t) {
         toolboxManager = t;
    }
 
    /**
     *
     */
    public void destroy() {
        webApp.clearSessions();
    }
 
    /**
     *
     * @return
     */
    public Map getToolBox() {
        return toolBox;
    }
 
    /**
     *
     * @param toolBox
     */
    public void setToolBox(Map toolBox) {
        this.toolBox = toolBox;
    }
 
 
}

A bread crumb tool, for creating breadcrumbs from a site map:

 
package org.adk.java.viewfilter.tools;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.adk.java.log.KLogger;
import org.adk.java.web.sitemap.SiteDirectory;
import org.adk.java.web.sitemap.SiteMapBuilder;
import org.adk.java.web.sitemap.SitePage;
import org.jdom.Attribute;
 
/***===============================================================================
 * The BreadCrumbTool generates a tree structure based on a site map xml file
 * located in the WEB-INF directory.
 * <p>
 * The way we do this, is through first using the {@link SiteMapBuilder} class which
 * generates a tree structure full of SiteDirectory and SitePage classes including
 * sub directories.
 * <p>
 * Then using the {@link SiteMapBuilder} class, we can retrieve the entire structure
 * in a TreeMap.
 * <p>
 * We then use velocity to create the breadcrumb as follows:<br>
 * =========================================================
 * <p>
 *   #set( $requestUri =  "$requestTool.getRequest().getRequestURI()" )
 *   #set ( $pageTitle = "")
 *   #set ( $dirs = $breadcrumbTool.getDirectory( $requestUri  ) )
 *
 *    <span  style="font-size: 10px; font-family: verdana">
 *    #foreach ($dir in $dirs )
 *       #set ( $isDisplayed = "$!dir.isDisplayed()")
 *
 *       <a href="$dir.getFinalTreeUrl()">$locale.get( "$dir.getId()" )</a>
 *    #end
 *
 *<p>
 * What this does:<br>
 * ===============
 *<p>
 * It retrieves the entire directory object and puts it in $dirs.
 *<p>
 * We then loop through each directory and generate a link and we pull the
 * title of the directory using the $locale tool.
 *<br>
 * As a note - this class needs major refactoring. I recommend another
 * version that handles dynamic directory creation.
 *
 * @see SiteMapBuilder
 * 
 * @version 0.1
 * @author Austin Kottke
 * ===============================================================================*/
 
public class BreadCrumbTool extends BaseTool {
 
    private KLogger logger = KLogger.getLogger(this.toString());
    private SiteMapBuilder primarySiteMap  = new SiteMapBuilder();
    private String siteMapXmlPath;
 
    /**
     *
     */
    public void BreadCrumbTool(){
    }
 
    /**
     *
     * @param params
     */
    public void configure(Map params)
    {
        logger.debug("configure() called.");
        siteMapXmlPath = params.get("siteMapXmlPath").toString() ;
 
    }
 
    /**
     *
     * @param requestUri
     */
    public void get( String requestUri ){
        if( ! primarySiteMap.isInitialized() ) {
            primarySiteMap.buildSiteMap( this.getServletFilePath() + siteMapXmlPath );
        }
 
        generateBreadcrumb(requestUri);
    }
 
    /**===============================================================================
     * This is the primary entry point to retrieve the breadcrumb directory.
     *
     * It returns an arraylist based on where the page request is coming
     * from.
     *
     * @param requestUri
     * @return
     *===============================================================================*/
 
    public ArrayList getDirectory( String requestUri ){
        logger.debug("getDirectory() called.");
 
        if( ! primarySiteMap.isInitialized() )
            primarySiteMap.buildSiteMap( this.getServletFilePath() + siteMapXmlPath );
 
        logger.debug("Servlet context name: " + getServletContext().getServletContextName() );
 
        String uriContextPath = this.getServletContextPath();
        String requestPath = requestUri.replace(uriContextPath, "");
        ArrayList b = generateBreadcrumb(requestPath);
 
        // Build urls...
        String url = "";
 
        for( int i=0; i<b.size(); i++ )
        {
 
            try
            {
                SiteDirectory dir = (SiteDirectory) b.get(i);
                if( dir.getDynamicPageId() != null) continue;
 
 
                url += dir.getUrl();
                SitePage p = null;
 
                if( dir.getPages().size() >= 1 )
                {
                    String pageKey = dir.getDefaultPage();
                    p = dir.getPages().get(pageKey);
                    url += "/"+p.getUrl();
                }
 
                dir.setFinalTreeUrl(url.replace(".jsp", ".html"));
 
                if( dir.getPages().size() >= 1) {
 
                    url = url.replace("/"+p.getUrl(), "");
                   // logger.debug("Modified url: " + url );
                }
 
 
            } catch (Exception e ) { }
 
        }
 
        List newbreadcrumb = (List) getRequest().getAttribute("additionalbreadcrumb");
        if( newbreadcrumb != null )
        {
            for( int j=0; j<newbreadcrumb.size(); j++)
                b.add( newbreadcrumb.get(j));
        }
 
        return b;
    }
 
    /**===============================================================================
     * Let us say that you are adding in dynamic directories and do not want to
     * use the site map, you can add urls at request time so that the bread crumb
     * shows up properly.
     *
     * This is recommended to use in case of very dynamic cases, but otherwise you
     * should configure the SiteMap.xml file.
     *
     * @param localeId The current page locale id to be displayed to the end user
     * @param urlPath the Path to the service or url being generated.
     *===============================================================================*/
 
    public void addDirectory(String localeId, String urlPath){
        logger.debug("addUrl() called.");
 
        List breadcrumb = getAdditionalBreadcrumb();
 
        SiteDirectory dir = new SiteDirectory();
        dir.setId(localeId);
        dir.setFinalTreeUrl( urlPath );
        dir.setDynamicPageId( "dynamic" );
        breadcrumb.add(dir);
 
        getRequest().setAttribute("additionalbreadcrumb", breadcrumb);
    }
 
    /***
     * Adds a page using the localeId as the name, this is primarily
     * used for static pages that do not change.
     * 
     * @param localeId
     */
 
    public void addPage(String localeId ){
        List breadcrumb = getAdditionalBreadcrumb();
 
        SitePage p = new SitePage();
        p.setId( localeId );
 
        breadcrumb.add(p);
    }
 
    /***
     * Used for dynamic pages that change the title based on a specific
     * attribute passed.
     * 
     * @param localeId
     * @param dynamicPageTitleAttribute
     */
 
     public void addPage(String localeId, String dynamicPageTitleAttribute ){
        List breadcrumb = getAdditionalBreadcrumb();
 
        SitePage p = new SitePage();
        p.setId( localeId );
        p.setDynamicPageTitle( dynamicPageTitleAttribute );
 
        breadcrumb.add(p);
    }
    /***
     * Stores a bread crumb in the request attributes. Used primarily for
     * creating directories that are marked as dynamic. We then can
     * add on additional directories and subdirectories at will. Particularly
     * useful for pages that have very dynamic page structures.
     * 
     * @return List A breadcrumb list that will be added on to the end of the breadcrumb
     */
    public List getAdditionalBreadcrumb(){
        List breadcrumb = null ;
        try {
            if( getRequest().getAttribute("additionalbreadcrumb") != null )
                breadcrumb = (List)getRequest().getAttribute("additionalbreadcrumb");
            else
                breadcrumb = new ArrayList();
 
        } catch(Exception e ){}
        return breadcrumb;
    }
 
 
    /**
     *
     * @return
     */
    public SitePage getPage(){
 
        return null;
    }
 
    /****************************************************************************
     * Creates the bread crumb.
     * 
     * @param requestUri
     * @return
     ****************************************************************************/
    public ArrayList generateBreadcrumb(String requestUri){
 
        logger.debug("generateBreadcrumb for: " + requestUri);
        TreeMap<String,SiteDirectory>  map = this.primarySiteMap.getSiteMap();
        Set directorySet = map.entrySet();
        String reqUri = requestUri;
 
        // Check if the action is in the request uri, which needs to be taken
        // out as this is not a top level directory
        if( requestUri.indexOf("action") >= 0 )
            reqUri = requestUri.split("/action")[1];
 
        String [] directories = reqUri.split("/");
 
        SiteDirectory parentDir = null;
        ArrayList breadcrumb = new ArrayList();
 
        breadcrumb.add(map.get("dir_home"));
 
        logger.debug("Directory size: " + directorySet.size());
 
        for( int i=0; i<directorySet.size(); i++ )  {
            String key = directorySet.toArray()[i].toString().split("=")[0];
            SiteDirectory d = map.get(key);
            String topLeveldirectoryName = d.getUrl().replace("/", "");
 
            for( int k=0; k<directories.length; k++ )
            {
                String directoryToFind = directories[1].toString().trim();
 
                if( directoryToFind.length() > 1 ) {
 
                    if( topLeveldirectoryName.equals(directoryToFind))
                    {
                        logger.debug("Matching top level directory: " + topLeveldirectoryName );
                        getFinalDirectoryStructure(d, directories, breadcrumb, 0);
                        return breadcrumb;
                    }
                }
            }
 
        }
 
 
 
        return breadcrumb;
    }
 
    /***===============================================================================
     * Generates the final directory used for the breadcrumb, this can include
     * any number of directories and a single page to send the url to.
     * 
     * @param dir 
     * @param requestPath
     * @param intDirIndex
     * @param breadcrumb
     * ===============================================================================*/
 
    public void getFinalDirectoryStructure(SiteDirectory dir, String[] requestPath, ArrayList breadcrumb, int intDirIndex)
    {
        breadcrumb.add( dir );
 
        for( int k=0; k<requestPath.length; k++ )
        {
            String pathToFind = requestPath[k].toString().trim();
 
            int dirLength = dir.getSubcategories().size();
            int pageLength = dir.getPages().size();
 
            Set pages = dir.getPages().entrySet();
            Set directories = dir.getSubcategories().entrySet();
 
            //logger.debug("Current dir index: " + dir.getId() );
            // Check directories and then check pages.
            for( int i=0; i<dirLength; i++ )
            {
                String dirKey = directories.toArray()[i].toString().split("=")[0];
                SiteDirectory d = dir.getSubcategories().get(dirKey);
 
                if( pathToFind.equals( d.getUrl().replace("/", "") ) )  {
                    logger.debug("Directory: " + d.getDynamicPageId() );
                    getFinalDirectoryStructure(d, requestPath, breadcrumb, i);
                    return;
                }
 
            }
 
            // Now go through the last few pages...
            for( int m=0; m<pageLength; m++ )
            {
                String pageKey = pages.toArray()[m].toString().split("=")[0];
                SitePage p =  dir.getPages().get(pageKey);
 
                String url = p.getUrl();
 
                if( url.equals( requestPath[k]) || url.equals( requestPath[k].replace(".html", ".jsp") ) ){
                    logger.debug("Found a page match: ["+p.getUrl()+"] in directory: ["+dir.getId()+"]");
 
                    breadcrumb.add(p);
                    return;
                }
            }
        }
 
    }
 
    /****************************************************************************
     * Builds a dynamic directory based on the requestUri. A store has
     * many categories and sub categories which are generated dynamically
     * and are not in the site map.
     * <p>
     * As a result of this, it is impossible if not tedious to create an entire
     * directory structure based on a possible directory structure that has
     * many infinite possibilities of shopping options.
     *<p>
     * So to generate the dynamic directory we simply build the structure based
     * on the uri and let the action handler correctly go to the correct directory
     * structure.
     *<p>
     * Example of a Directory:
     * ------------------------
     * <p>
     *
     * Home > Store > Books > Fiction > New and Used > Foundation Series
     * <p>
     * As you can see the directory structure is very long and the categories
     * are built within Books to Fiction to New and Used.
     *
     * @deprecated Not used, use the addDirectory and addPage methods as these
     *             are less messy.
     ****************************************************************************/
 
    public void addDynamicDirectoryToBreadCrumb( ArrayList breadcrumb, SiteDirectory d, Object[] requestDirectories)
    {
        logger.debug("addDynamicDirectoryToBreadCrumb() called: " + d );
 
        // ==============================================================================
        // We need to first find what the directory name is and in the current path array
        // contains the requestUri. We find the index into this path array so we can then
        // parse the remaining part of the array and build a dynamic directory with all
        // path info taken into account.
        // ==============================================================================
        String currentDirPathName = d.getUrl().split("/")[1];
        int currentDirectoryIndex = 0;
        for( int k=0; k<requestDirectories.length; k++ ) {
            if( requestDirectories[k].toString().equals( currentDirPathName ) ) currentDirectoryIndex=k;
        }
        // ==============================================================================
        // Now we have the current directory index, we now need to build the correct
        // dynamic directory.
        //
        // The remaining directory names correspond to localeIds and refer to
        // an action service.
        // ==============================================================================
        Attribute e = d.getAttributeById("dynamicUrl");
        String previousUrl = d.getAttributeById("previousUrl").getValue();
 
        SiteDirectory dynamicDir = new SiteDirectory();
        if( previousUrl != null )
        dynamicDir.setFinalTreeUrl( getServletContextPath() + previousUrl + requestDirectories[currentDirectoryIndex+1] );
        dynamicDir.setId( requestDirectories[currentDirectoryIndex+1].toString() );
        dynamicDir.setDynamicPageId("dynamic");
        breadcrumb.add( dynamicDir );
 
        for( int i=currentDirectoryIndex+2; i<requestDirectories.length; i++)
        {
            SiteDirectory newDynamicDir = new SiteDirectory();
 
            if( requestDirectories[i].toString().indexOf(".html") <= -1 )
            {
                newDynamicDir.setId( requestDirectories[i].toString() );
                newDynamicDir.setFinalTreeUrl( getServletContextPath() + e.getValue() + "/" + requestDirectories[i] );
                newDynamicDir.setDynamicPageId("dynamic");
                logger.debug("Directories: " +requestDirectories[i] );
                breadcrumb.add( newDynamicDir );
            }
 
            // This is a page, add a site page to the breadcrumb.
            if( requestDirectories[i].toString().indexOf(".html") >= 0 )
            {
                SitePage p = new SitePage();
                p.setId(requestDirectories[i].toString().split(".html")[0]);
                breadcrumb.add(p);
            }
 
        }
 
        logger.debug("Index: " + currentDirectoryIndex );
    }
}

If anyone wants the source let me know =)

Austin

JAR like use in Flash? Use mxmlc -load-externs

For the last few years I’ve been developing in flex. One of the very nice features is RSLs to save on file space, but no matter how much optimization work you end up with a huge SWF right? Which is why developers end up using flash for web development.

How many times have you wanted to use the RPC components but ended up scrapping them because they bloat your SWFs? I’ve had to resort to custom AS3 soap libraries, or even have had to hack an existing AS3 web service implementation because it wasn’t compatible with ALL SOAP specs, as in each server has little nuances which can potentially break an AS3 implementation. The joys =)

Ive been doing a lot of flash dev recently, and one option which I think a lot of flash developers miss over is the fact that they compile assets within assets that are shared across multiple SWF files. E.g. you have the Pure MVC framework or cairngorm classes compiled into 5 swfs, when really, all you need to load in a single shell SWF and all future SWF files can reference the classes normally WITHOUT compiling in the entire framework.

So how do you do this? How do you make your other SWFs not compile the entire framework such as ui components and buttons and flash?

Well, instead of doing all the runtime instantiation and non strong typing – you know like var myClass:* – which is prone to errors, use mxmlc -load-externs.

So the steps are as follows:

  1. Create main loader SWF that uses the majority of the framework, ui components etc.
  2. Generate link-report XML using ant:  <mxmlc … link-report=”shell-link-report.xml”
  3. In your ant build file, when compiling all sub-module swfs, compile all module swfs using load-externs=shell-link-report.xml.

By doing these very simple steps you can reduce legacy swf projects by half sometimes simply because when using the Flash IDE it does not natively create this for you.

Tutorial: Build Twitter with Groovy Grails in 90 Minutes

I found an awesome video that came out last month from one of the Spring Source engineers on building an entire application in Grails.

I think if anything this video shows the true simplicity of grails. It outlines security, ajax, authentication, jdbc, validation and how fast it is to develop with grails. You see how robust and quick to develop a data-base driven twitter clone.

Stripes/Velocity – Finally a web development architecture I like

I tell you it’s been a while since I last posted, but one thing that I have been spending a lot of time is looking at web frameworks. I looked at wicket, web macro, jsp, velocity, and I have to say that the most awesome framework I can see is stripes.

I was first reading a book on hibernate – harnessing hibernate and in the end of the chapter it mentions about Stripes. One of the things I can’t stand is the auto-wiring of Java bean objects and a web form. It’s not a big deal with flex because you have amf, but in a web site it’s a pain.

So take stripes, I just take an entity bean and I can specify the bean on the page and stripes will auto-validate, and send errors if a field is not required. It’s pretty nice.

See below:

*/
@ValidateNestedProperties({
@Validate(field=”firstName”, required=true, minlength=3, maxlength=50),
@Validate(field=”lastName”, required=true, minlength=5, maxlength=50),
@Validate(field=”email”, required=true, minlength=5, converter=EmailTypeConverter.class ),
@Validate(field=”userName”, required=true, minlength=5 ),
@Validate(field=”password”, required=true, minlength=8 )
})
@Validate(encrypted=true)
public UserVO getUser() {
return this.beanContext.getUser();
}

Using annotations the validation occurs and its just a simple matter of inserting a field and binding directly:

Doing this will automatically validate the field in java.

Cool huh?

Well that’s one of the major things I like about it. Besides flex, it’s pretty smoothe. I like wicket, but for some reason I just cant get over writing all my UI logic in java…. I feel like im doing swing again!

Anyway, check out stripes – its pretty cool: www.stripesframework.org

Mobile development, best way to start?

If someone were interested in developing for the android or a sidekick, what is the best way to learn. I know the iphone is objective-c, but does anyone have any advice for this? Im trying to get perspectives from different developers as the best way to learn this field, coming from an as2 – as3 – heavy oop background with some java apps built but not proficient in J2ME. Obviously each unit requires different skills, but what is the best recommended learning environment for this? This is a very general question i realize this, but it would be good to know from pro developers any feedback on this.