Austin Kottke's Code Site

Thoughts about Architecture – Java, C/C++, JS, Objective-C, Swift, Groovy, Grails, (RIP Flash)

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

For some reason, companies like Microsoft, Google do not seriously believe javascript is a great programming language. I have constantly been working in javascript and see the pros and cons. I see that because of it’s amazingly dynamic nature you can do almost anything. However, because of this dynamic nature it makes it prone to errors almost immediately.

Most recently Microsoft unveiled it’s new language called Typescript, which converts an object oriented scripting language with Interfaces, Classes, constructors into javascript. Now look at Google Dart which by the way is in full swing and it does nearly the same thing except with an actual VM. Some people thought it died, but they are working on a milestone 1 release.

So the big question is, which one is going to DIE first??? Or is Apple going to come up with their own intermediary language and pose another type of intermediary language that converts to JS.

Ahh, I miss the days of Flash being King. Where you had one tool to do amazing stuff and never needed to worry about browser dependencies.

I was knee deep into an HTML5/CSS3 book about 8 months ago and the opening introduction said “We need to infer to our clients that the project will not look the same on multiple browsers.” Wow, seriously? It’s because of this problem and competing vendors that we have this issue. Why cant vendors just work together one on project and support it? There is very little money in open source tools. Wouldn’t it be nice if Mozilla, Microsoft and Apple all supported Dart?

Anyone who has done RIA development, back-end development and worked with a myriad of languages that include strong typing and compile time checking knows that Adobe’s announcement of the non-existence of a mobile flash player has set the entire flash development community backwards into a realm of using javascript and shitty compilation standards. HTML5 is cool, but for real – it comes down to javascript.

Don’t get me wrong I love javascript, for little things here and there and working on a page. But a usual flash application can be up to a 100,000 lines of front-end code.

I sought out to prove to myself that javascript could be a force to be reckoned with so I set myself up with Aptana again and downloaded all of the latest libraries, jquery, mootools etc. I then decided to create classes using mootools. To my dismay creating a class is at run time only so guess what:

1. If using new frameworks to create classes instead of the usual prototype BS, you will thoroughly disappointed that you will have no compile time checking.

2. Thus if you have an error in your class you will never know until you run the app.

3. console.log doesnt work in IE 8.

Thus from my deduction the only logical approach to this is the fact that you must use prototype to create your classes and not rely on strong typing.

Even then, if you use prototype to create a class AND then use another class within that class all compile time checking goes out the window.

This slows development time down tremendously and is the primary reason I think developers like Google are going with alternate solutions like Dart.

I really think Adobe should have waited for the announcement. I think Adobe should have made the announcement WHEN they had a solution. It makes developers now flounder as a whole. You dont announce the big issue when you have no alternative.

So I hope adobe builds in something into dreamweaver or eclipse that does INCREDIBLE javascript parsing. The dreamweaver parsing in 5.5 is ok, but come on. Can we get LIVE compile time checking with JS? If you are making this the alternative to flash development.

Can you get the error detection as awesome as flex builder?

The amount of millions of dollars wasted in no compile time checking is at stake here and I think that managers need to know the alternatives to development.

ECMAScript:

Seriously, rather than come to any standard about proper web development in a language they drop the ECMAScript 4 specification which is something that could help make javascript more readable and strong typing – lots of things. Companies like Microsoft and Yahoo say its too complex.

Seriously, who thinks javascript is amazing at Microsoft? Can they see the issues here?

It appears javascript is more about shorthand than it is about writing clean, documented code:

Just look at this article about js 1.8 whereby instead of implementing ECMAScript 4 enhancements that include packages, namespaces, classes. They implement CLOSURES – shorthand code for methods that make code a lot more unreadable.

https://developer.mozilla.org/en/New_in_JavaScript_1.8

I think it’s a backwards move and I think Google sees this too which is why Google Dart is being developed.

I would like to get feedback from developers from using Adobe’s Mobile Development toolset, particularly in flex sdk 4.5.

Can some developers provide some pros and cons in using it? Speed of performance, android phones, tablets. Also, Ive tried some apps on an Iphone 3GS and found them really slow.

How is the speed now? What can and cant be done?

So now all of us awesome developers who have been developing and programming for the adobe flash platform for years now have an opportunity to write some awesome apps for mobile platforms.

The big question that we as developers have and need answered are – what are the drawbacks of developing with Adobe’s tools as opposed to developing with something like the android SDK or the Iphone SDK. What challenges and problems are we going to encounter.

My questions for adobe:

    1. What is the roadmap and to-dos on the Air Mobile SDK

    2. Native controls – how do you access these using the Air Mobile SDK

    3. How do you program in 3D for IOS/Android using native GL graphics calls. Is this possible?

    4. Is there some way to leverage advanced APIs for IOS and Android

    5. Can you actually use APIS on the IOS platform via a “hack” or some way… what if a company invests millions of dollars and the adobe tools are selected as the platform of choice and we need to do an advanced task that is not in the AS3 Air Mobile SDK? What do we do?

I think that a lot of developers have these questions and it would be good to get some light on this before talking to the higher ups on the pros and cons.

Thanks =)

So, Im always digging into new frameworks and trying to understand the philosophies behind why developers chose to make new frameworks to help others.

After falling in love with the new [Inject] metatag and thinking it’s the coolest thing since Java has been doing this for years anyway with Annotations, I thought this framework must be rad =)

So after hours of looking at the documentation, the one thing I found out and is the bane of all robot legs development is CLASS NAMING. Lord have mercy, there is a base class called “Actor” – THATS THE NAME OF THE CLASS. Ok, so after reading GoF and all these development books over the years the one thing that java/flex/flash developers have a huge problem is is correctly conveying the names of classes. Conceptually representing something. With that said “ACTOR” is the worst name for a class ever – I mean, it communicates absolutely nothing! The concept is so ambiguous and it hurts just thinking about it. And it’s used EVERYWHERE. PureMVC atleast names the classes by common design patterns so it’s clear to everyone using it if you’ve studied these patterns.

Lord have mercy – can we just get a pure mvc framework with an [Inject] meta tag? Ill be happy then =)

Something that I think should be done for the flash player, now that HTML 5 is gaining ground, is an inline HTML renderer that should be packaged as part of the download. Potentially it could be an optional download. I mean the core code base is already a part of the AIR packager. How hard would it be to make an external RSL that gets cached or make it part of the flash player 11 installation with a checkbox to download this in addition.

If there is an update to the web kit renderer, it then flags this in the flashplayer download manager… Just a thought to make flash player 11 completely blow away. We’ve got 3d support, but how about a little HTML 5 renderer as part of the flash player?

So, the new flash player is out. And the one thing Im thinking is – AWESOME. So, I’ve been looking at the API docs and the program code and all that looks hot. Stencil Buffer, Depth Buffer, Vertex Buffer – they basically made it almost identical to NVidia’s C++ CG, CGFX. However I did not see profiles, but Im sure this is going to be a smart solution done.

So the wheels are turning and Im only thinking to myself, If Adobe, NVidia, and Microsoft can get together to develop some kind of HLSL (High Level Shader Language) that all three can use – a simple wrapper – MAN we’re going to be in heaven as 3d developers. Then, the shaders for say your favorite PC game can be loaded directly into a flash game, and the same shaders can apply to the model. Because you’ll be using a PC for all types of development.

The only issue this poses is when you export to mobile – a lot of the same shaders will have to be dummied down – so adobe will need to come up with some kind of profile mechanism – similar to how CGFX does it where it loads back up shaders if the hardware cannot support the shader.

Are you excited about this? I think this is going to kick ass!!!

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

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.