First Steps with Jakarta Struts

Just over a year ago, I started to learn how to use the Jakarta Struts J2EE Web application framework. During this journey, I realised that I found some aspects of Struts confusing at first, and that many other developers might well suffer the same difficulties. This article is an attempt to address that problem.

Many good books on the framework are now available, and the online documentation is quite mature for an open-source project; in spite of this, I have often had to refer to a wide variety of resources in order to build a good understanding of Struts. And there are still some areas in which the documentation is sparse.

However, this article isn’t an attempt to provide a definitive source of documentation. Rather, it’s intended to allow Struts newcomers to hit the ground running, to get a feel for what the framework is like, and understand what it can and can’t do.

Of the Struts example applications I’ve seen, most have been either too trivial or too complex, the complex ones having lots of external dependencies that must be resolved before the example can even be compiled and run. In this tutorial, I’ll attempt to hit a spot between these two extremes. I’ve deliberately created an application that has the minimum of external dependencies, so you should be able to actually run the code without too much difficulty! Everything you’ll need is open-source and can be downloaded for free — one of the advantages of J2EE.

The example application that we’re going to build is an online discussion forum, which I’ve given the rather generic name of "Web Forum". I’ve chosen an application of this type because it should be familiar, and because you can create quite a lot of functionality without the application becoming too complex. Our Web Forum is fully functional and reasonably well-debugged; hopefully, it should be fun to learn how it all fits together.

It’s important to note that this code shouldn’t be considered an example of a production-quality J2EE system. In particular, the exception handling is extremely — and deliberately — simplistic. This isn’t a tutorial on persistence strategies, object-oriented design or Web application GUI design; it’s simply a vehicle for learning Struts. Undoubtedly, some aspects of what I’ve created here could be improved upon, but that’s not important right now. Let’s get started!

Ingredients

To start, let’s talk about the ingredients you’ll need to create the application. I developed the Web Forum using Oracle JDeveloper because I’m familiar with that IDE. You can use your Java tool of choice for writing the code.

Apart from the source code itself, here’s a list of the other elements you’ll need:

I also used the MySQL Control Centre to create the database and control the database server, although the MySQL command line can be used if that’s what you prefer. Note that, because this isn’t a MySQL tutorial, some familiarity with MySQL is assumed. As a minimum, you’ll need to know how to start and stop the database server, how to create a new database, and you’ll need the ability to run a provided SQL script to create tables.

These are the files we’ll use through this tutorial.

To reduce the download sizes, I’ve split the Struts JAR files that go into WEB-INF/lib into a separate download. The pre-compiled WAR file is also available.

The Application

Let’s take a brief tour of the Web Forum’s functionality. I’d thought about some of this before I wrote a line of code, but other aspects became apparent as I was building the app. The fact that there are plenty of existing forums to serve as inspiration certainly helped!

Upon starting the application, the user is presented with a list of topics, and information about each topic, such as:

• the date and time it was posted
• the number of replies
• the topic’s author

The 16 most recent topics are displayed, ordered from the newest to the oldest:

At this point, the user can click on a topic to view it, along with any follow-up replies:

The date and time of the post are displayed, as well as the authors’ names, the dates on which they registered with the system, and the number of posts they’ve made. The user can register if he or she is a new user, or log in if they’re an existing user. A user must be logged in before he or she can create a new topic or a new reply. The user can elect to log in automatically during registration, or at the Login screen:

The auto-log in feature uses a cookie that expires after thirty days. During registration, the user must provide a unique user name, his or her real name (or screen alias) and a password. All of these fields are mandatory, apart from surname, and the user must confirm his or her chosen password by entering it twice:

Upon successful registration, the user is taken back to the topics screen, on which he or she can create a new topic:

Both the subject and message fields are mandatory. Users are allowed to use the HTML <b>, <i> or <u> tags for basic text formatting (bold, italic or underline) within the message field. Once the user creates a new topic, he or she is taken back to the Topics screen.

The topic hyperlinks ensure that users can see which topics they’ve already read. The hyperlinks to topics that the user has already read are displayed using the browser’s visited link colour. However, each topic link contains an encoding of the number of replies that have been made to that topic. Therefore, new replies to a topic cause that topic’s hyperlink to be displayed using the browser’s unvisited colour. Thanks to Joel Spolsky for this brilliantly simple idea.

When viewing a topic, users can compose a new reply; again, the message field is mandatory and accepts basic formatting tags:

The Persistence Layer

The persistence layer in the Web Forum application is very simple. There are two database tables: one for posts and another for users. The posts table stores topics and their replies. The users table stores the details of the registered users of the application. These tables are accessed using data access objects (DAOs), which are just Java objects that perform simple object-relational mapping. In other words, they take a Java object and persist its contents into a table, and vice-versa.

The DAOs have the appropriate SQL statements embedded into them, although this probably isn’t good practice: database administrators like such things to be externalised so they can be tuned. I have also created for all the DAOs an abstract superclass, which contains utility methods that obtain a JDBC connection from a data source and close database resources, etc.

This is the schema for the posts table:

The most interesting feature of this table is the ParentID column, which links replies back to their parent topic. The Subject column is null when a post is a reply and not a topic. I’m storing the number of replies to each topic in a ReplyCount column. Technically, this is redundant because it could be calculated, but I’m storing it because MySQL doesn’t currently support nested SQL SELECT statements.

The CreationDate column originally auto-updated, but I changed this because the date and time of the original topic were being updated to the current date and time whenever a reply was added to that topic.

Here’s the schema for the users table:

The MySQL SQL script that creates these tables is in src/sql/create_tables.sql. This script can be run from a query window in MySQL Control Centre. Note that it assumes the existence of a database named webforum. Also, because we’re not building the functionality to create new topics in this article, you’ll have to manually insert some test data so that you have some topics to display.

I didn’t have to do much thinking to come up with the business objects in the application: they’re pretty obvious. There are classes for individual posts and users, as well as collections of posts and users. The class model — excluding the Struts classes — is shown below:

Some of these classes, such as UserCookie, will be covered later in the series. As can be seen from the class model, a Posts class contains a collection of Post objects at runtime, and features methods for retrieving this collection and for adding a new post. The Post class itself has attributes that correspond to the columns in the posts table, and overloaded constructors that are invoked depending upon whether the post is a topic or a reply. Accessors and mutators (getters and setters) are not shown on the class model.

The Users class contains a collection of User objects at runtime, and features methods for retrieving this collection and for adding new users. In fact, I deprecated the getUsers method after I discovered that I’d coded it, but didn’t actually call it from anywhere! The User class has attributes that mirror the columns in the users table, as well as a convenient getDisplayName method that returns the user’s first names and surname with a space character in the middle.

Project Structure

The organisation of the source code folder tree is shown below:

Java source code files go under src, and the Java package hierarchy is rooted at com.johntopley.webforum. The public_html folder corresponds to the root of the Web application.

I always put JSPs under pages because this allows them to be protected by the Web container using J2EE declarative security. Anything in public_html and its sub-folders really should be regarded as public.

I also like to separate the Struts configuration files into a config folder, although usually you’ll see them stored directly under WEB-INF.

The application entry point is public_html/index.jsp, which is declared as a welcome file in the web.xml Web application deployment descriptor. Let’s take a look at index.jsp:

<%@ taglib uri=http://jakarta.apache.org/struts/tags-logic       prefix="logic" %>   <logic:redirect forward="ViewTopics" />


All this page does is transfer control to the Struts framework, because if we’re going to use a framework, we want to be using it as soon as possible. Struts ships with a number of JSP tag libraries (taglibs), and here we’re using the logic taglib. This handles the conditional generation of output text, looping over object collections and application flow management. In this case, the redirect tag performs an HTTP redirect to a Struts logical URL. More about that in a moment.

One thing to note about the Web Forum application is that we’re using the Servlet 2.3 specification syntax to reference the taglibs using URIs, rather than referring to TLD files in the web.xml file. This is documented in section 5.4.3 of The Struts User’s Guide.

The Heart of Struts

The heart of Struts is the config/struts-config.xml file. This file defines the flow of the application and tells Struts which classes to use for what. The Struts ActionServlet is a controller class that reads this file and receives all incoming requests for the Web application. The ActionServlet needs to be configured as a servlet in the web.xml file:

<servlet-name>action</servlet-name>     <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>     <init-param>       <param-name>config</param-name>       <param-value>/WEB-INF/config/struts-config.xml</param-value>     </init-param>     .     .     .


The main part of the struts-config.xml file covered by this article is:

<struts-config>     <global-forwards>       <forward name="ViewTopics" path="/ViewTopics.do"/>     </global-forwards>

<action-mappings>       <action         path="/ViewTopics"         type="com.johntopley.webforum.controller.action.ViewTopicsAction"         scope="request">         <forward           name="Topics"           path="/WEB-INF/pages/topics.jsp"         />       </action>     </action-mappings>   </struts-config>


Struts introduces a layer of indirection into Web applications because it uses logical URLs. This means that the address you see in the browser’s address bar does not correspond to the physical location of that resource on the Web server. This allows developers to move resources around easily without breaking things. The Struts name for the association of a logical name with a resource is an ActionForward, often just called a Forward. The Struts configuration file contains a global-forwards section that allows the configuration of Forwards that are available throughout a Struts application. These are effectively the application’s entry points.

Another key Struts concept is the Action class. Actions are simply Java servlets, so anything that a servlet can do, an Action class can do. Actions are used to process requests for specific URLs. Generally, they should act as a thin layer around the business objects layer, which does the real work.

Actions are referred to by ActionMappings, which, again, are logical URLs. The Struts convention is that ActionMappings end with .do. The web.xml file needs to be configured so that the Struts ActionServlet is used to process any URL matching the pattern *.do, as shown here:

<servlet-mapping>     <servlet-name>action</servlet-name>     <url-pattern>*.do</url-pattern>   </servlet-mapping>


Flow Of Events

Let’s recap where we’ve got to so far. The index.jsp page redirects to the ViewTopics global ActionForward, and this passes control to the /ViewTopics Action. Struts knows which Action class to invoke because the type attribute in the ActionMapping gives the fully-qualified name of the associated Java class, which must inherit from org.apache.struts.action.Action, and must have an execute method with the following signature:

public ActionForward execute(ActionMapping mapping,                                ActionForm form,                                HttpServletRequest request,                                HttpServletResponse response)     throws Exception


The mapping parameter is an ActionMapping object that represents the ActionMapping that invoked this Action. The form parameter is used if an HTML form is associated with the request. The request and response parameters highlight the fact that an Action class is just a specialisation of a servlet.

The Web Forum application uses a BaseAction abstract class that inherits from the org.apache.struts.action.Action class mentioned earlier. The other Action classes within the application inherit from BaseAction, so it serves as an extension point at which functionality common to all Action classes can be added if required. This should be considered good practice.

Important: Action classes should not use instance variables, as they are not thread-safe. State should be shared by storing values in the request, session, or servlet context objects, depending on their scope.

This is the execute method in ViewTopicsAction.java:

public ActionForward execute(ActionMapping mapping,                                ActionForm form,                                HttpServletRequest request,                                HttpServletResponse response)     throws Exception   {     request.setAttribute(KeyConstants.POST_LIST_KEY,       new PostsDAO().getTopics());

return mapping.findForward(ForwardConstants.TOPICS_PAGE);   }


A new PostsDAO object is instantiated and its getTopics method is called. This method uses the following SQL statement to query the posts table:

SELECT p.PostID, p.Subject, p.ReplyCount, p.UserID, p.CreationDate   FROM Posts p   WHERE p.ParentID = 0   ORDER BY p.CreationDate DESC


The SQL WHERE clause ensures that only topics are selected — not replies. The getTopics method returns an instance of the Posts class, i.e., an ordered collection of Post objects. This instance is stored in the HTTP request under the key referred to by the KeyConstants.POST_LIST_KEY constant. The JSP that displays the list of topics will use this Posts object stored in the request. Finally, the findForward method of the ActionMapping class is invoked. This takes, as a String parameter, the name of a Struts Forward to pass control to. The ForwardConstants class contains all of the Forward names used within the Web Forum.

As well as global ActionForwards, Struts also has local ActionForwards. These are simply Forwards whose scope is local to a single ActionMapping. In other words, they are not globally visible within the application.

Important: Struts gives precedence to local ActionForwards over global ActionForwards.

A local Forward is used within the /ViewTopics ActionMapping to hold a reference to the JSP that displays the list of topics:

<forward name="Topics" path="/WEB-INF/pages/topics.jsp"/>


We finally have a physical path to a page! topics.jsp is included with the source code downloads for this article (linked above), so you can see the list of topics.

Building the Application Using Apache Ant

If you’re not familiar with the Apache Ant build tool, spend a few moments reading Apache Ant Demystified, which should get you up to speed quickly. The build file for the Web Forum application shouldn’t look too unfamiliar, although it does use some more of Ant’s built-in tasks. Another key difference is that, because this application is rather more than a simple "Hello World" app, we have to deal with the classpath. The good news is that Ant makes this easy too!

To compile the application, we need to have the J2EE libraries on the classpath. Fortunately, the Apache Tomcat Web container already comes with these libraries, so the question becomes one of referencing. Rather than hard-code Tomcat’s installation folder into our build file, let’s set up an operating system environment variable called TOMCAT_HOME that refers to the root of the Tomcat installation folder, e.g. C:Program FilesApache Software FoundationTomcat 5.0. The procedure for doing this is exactly the same as the one we used to create the ANT_HOME and JAVA_HOME environment variables in Apache Ant Demystified.

With that done, we can add a couple of property declarations to the top of our build.xml file:

<project name="webforum" default="dist" basedir=".">      <description>        Build file for the Struts WebForum application.      </description>

<property environment="env" />      <property name="tomcat.dir" location="${env.TOMCAT_HOME}" />  The first property declaration simply says that we want to use the identifier env to refer to operating system environment variables. The second property sets the location of tomcat.dir to whatever env.TOMCAT_HOME is. Thus, from now on, when we want to refer to Tomcat’s installation folder, we can simply use ${tomcat.dir}.

Later, we set up a property named j2ee.lib.dir that points to the location at which Tomcat keeps its J2EE libraries:

<property name="j2ee.lib.dir" location="${tomcat.dir}/common/lib" />  We now need to deal with setting up the classpath. Ant provides several ways to do this; we’re going to set up a path structure which we’ll assign the unique ID of classpath: <path id="classpath"> <pathelement path="${j2ee.lib.dir}/servlet-api.jar" />      <fileset dir="${web.lib.dir}"> <include name="**/*.jar" /> </fileset> </path>  The snippet above shows that there are two elements within this path structure. The first element is an individual reference to the servlet-api.jar file, which is referenced using the pathelement tag. The second element is Ant’s fileset tag, which in our case has been set up to include all the JAR files within the folder represented by the ${web.lib.dir} property. To clarify, we’re setting up a classpath that includes Tomcat’s servlet-api.jar file and all the JAR files within public_html/WEB-INF/lib.

In order to use this classpath, the javac task within our build file’s compile target contains a nested classpath tag, which has a refid attribute set to the name of the path structure that we defined above:

<javac srcdir="${src.dir}" destdir="${classes.dir}">      <classpath refid="classpath" />    </javac>


That’s how we make sure that the Java compiler can find all the libraries it needs to compile our application. The javadocs target also makes use of this classpath, which we defined once and can then re-use anywhere within the build file.

The next task is to package the compiled code into a WAR file (Web ARchive) so that it can be run by Tomcat. We create a dist target that looks like this:

<target name="dist" depends="compile"                description="Create the binary distribution.">

<delete dir="${dist.dir}" /> <mkdir dir="${dist.dir}" />

<war basedir="${web.src.dir}" destfile="${dist.warfile}"              compress="true"              webxml="${web.src.dir}/WEB-INF/web.xml" excludes="**/web.xml"> <classes dir="${classes.dir}" />      </war>    </target>


Note that this target depends on the compile target, which makes sure that we always compile the code before we try to create a distribution in which it’s included! After deleting and recreating the output directory, we use Ant’s war task to create the WAR file. The basedir attribute points to a property that represents the public_html folder — the root of our application. The destfile attribute simply specifies the name of the output WAR file that’s to be created. We can choose whether or not to compress the file using — you guessed it — the compress attribute; the webxml attribute tells the war task where our Web application deployment descriptor is located. The use of this last attribute automatically ensures that the web.xml file is included in the WAR file. Thus, we use the excludes attribute to tell Ant not to generate a warning because it thinks we’re trying to include web.xml twice. Finally, a nested classes element tells the war task where our compiled classes are located.

If you run the dist target and then open /dist/WebForum.war using WinZip (or download the pre-compiled version from this site), you can see that the layout within the archive precisely mirrors the source code tree mentioned earlier. The compiled code has been placed within WEB-INF/classes and the Struts and MySQL JDBC JAR files are within WEB-INF/lib.

Running The Application Using Apache Tomcat

These instructions describe how to configure the Apache Tomcat Web container to use a MySQL data source. They’ve been tested using Tomcat 5.0.28 and MySQL 4.0.18, but should work with Tomcat 4 and other versions of MySQL, too. Note that TOMCAT_HOME refers to the root of the Tomcat installation folder, e.g. C:Program FilesApache Software FoundationTomcat 5.0.

Copy the MySQL JDBC driver JAR file to TOMCAT_HOME/common/lib.
Edit the TOMCAT_HOME/conf/server.xml file so that it includes a context definition for the Web Forum application. The main elements of the server.xml file are shown below:

<Server>      <Service>        <Engine>          <Host>            :            <Context>              <!-- Existing context definitions. -->            </Context>            :            <!--             | Paste Web Forum context definition (shown below) here.             +-->          </Host>        </Engine>      </Service>    </Server>


Add the following context definition for the Web Forum application to the appropriate place in the server.xml file, substituting your MySQL webforum database user name and password where the italics indicate:

<!--     | Begin WebForum context definition.     +-->    <Context path="/WebForum" docBase="WebForum"        reloadable="true">

<Logger className="org.apache.catalina.logger.FileLogger"          prefix="localhost_WebForum." suffix=".txt" timestamp="true"/>

<Resource name="jdbc/WebForumDS" auth="Container"          type="javax.sql.DataSource"/>

<ResourceParams name="jdbc/WebForumDS">        <parameter>          <name>factory</name>          <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>        </parameter>

<!--         | The JDBC connection URL for connecting to your MySQL DB.         | The autoReconnect=true argument to the URL makes sure that the         | MySQL JDBC Driver will automatically reconnect if mysqld closed         | the connection. mysqld by default closes idle connections after         | 8 hours.         +-->        <parameter>          <name>url</name>          <value>jdbc:mysql://localhost:3306/webforum?              autoReconnect=true</value>        </parameter>

<!--          | Class name for MySQL JDBC driver.          +-->        <parameter>          <name>driverClassName</name>          <value>com.mysql.jdbc.Driver</value>        </parameter>

<!--         | Maximum number of DB connections in pool. Make sure you         | configure your mysqld max_connections large enough to handle         | all of your DB connections. Set to 0 for no limit.         +-->        <parameter>          <name>maxActive</name>          <value>100</value>        </parameter>

<!--         | Maximum number of idle DB connections to retain in pool.         | Set to 0 for no limit.         +-->        <parameter>          <name>maxIdle</name>          <value>30</value>        </parameter>

<!--         | Maximum time to wait for a DB connection to become available         | in ms, in this example 10 seconds. An exception is thrown if         | this timeout is exceeded.         | Set to -1 to wait indefinitely.         +-->        <parameter>          <name>maxWait</name>          <value>10000</value>        </parameter>      </ResourceParams>    </Context>    <!--     | End WebForum context definition.     +-->


Next, copy the dist/WebForum.war we created earlier to TOMCAT_HOME/webapps. This archive will be automatically expanded and the Web Forum deployed the next time Tomcat is started.

After starting the MySQL and Tomcat servers, the application can be accessed in a Web browser using the URL: http://localhost:8080/WebForum/

We’ve covered a lot of important ground with this instalment. Next time, we’ll take a look at how the Topics page works, and move on from there. See you then!

Replay

Category: java Time: 2005-05-06 Views: 0
Tags:

Related post

• First Steps With Jakarta Struts, Part 2 2005-06-15

Last time we looked at Jakarta Struts, I explained the persistence and business object layers and we wrote code to retrieve the list of topics from the database and represent them as objects. We used a Struts ActionMapping and an ActionForward, to ge

• First steps with htlatex, I'm losing all the math format! 2015-07-09

I'm doing my first steps with htlatex, trying to get HTML documents. I have OS: Linux Mint, Tex Live, and I make my *.tex with Texmaker. I know how to use MathJax. I get this: This is how I wish it should look! So I managed this with Pandoc, I simply

• Only first step with multi step registration 2014-08-22

This question is an exact duplicate of: Multiple registration phases 1 answer I'm using the module Multi-step Registration for my user accounts. I have six steps in my registration proces. But now I want to show only the FIRST step when a user regist

• first steps with sockets [on hold] 2016-02-05

They are the first experience with networking and do not understand where I'm wrong. For the first exercise I simply connect to a port on my IP address, but gives me except when I use accept (); you know help me? Socket firstSocket = new Socket(Addre

• First steps with amd64 assembly 2012-04-23

I try to learn amd64 assembler. This is the first thing I tried. This piece of assembly should replicate the functionality of the following piece of C code, which turns a binary sha-256 hash into a human readable form. assembly code 1: .ascii "012345

• Recommended first steps with the BeagleBoard 2010-12-26

How can I easily start off professional development with the BeagleBoard XM? Which OS should I work with? .. my choices include: Ubuntu Android MeeGo WinCE QNX Angstrom (preinstalled) Symbian Gentoo How do I install an OS onto the board? Which IDE do

• First steps with LuaLaTeX and ttf fonts 2014-01-25

I am just starting to use LuaLaTeX. Mainly because I want to use a ttf font for a presentation. My MWE looks like \documentclass{article} \usepackage{fontspec} \setmainfont[]{HelveticaNeue/HelveticaNeueLTCom-Th.ttf} \begin{document} Test \end{documen

• first steps with ISPConfig 3... help please 2014-09-23

Ok, I have installed ISP3 on my Debian Lenny box. It is sitting on my home network at the address 192.168.0.100 Please consider that I am not familiar with either ISPconfig 2 or 3. I am a noob. I can login to ISP3 at http://192.168.0.100:8080 and php

• How to build ui router nesting where navigation is first state - with multiple "wizards" with 3 steps or more 2016-01-31

I'm trying to build a nested view using AngularJS and UI router. The solution should have multiple steps with different questionares. First step is a menu that leads to different wizards (each with 3 or more steps). Problem is the menu should dissape

• What is the "big picture" or "first step" to pointing a newly purchased domain to my localhost? 2010-02-21

I just purchased a domain name but I don't know what I'm doing yet. Before I get a hosting account, I want to test out some things on my own http://localhost/ How do I go about pointing the DNS to my IP address for my site? Please remember I'm just g

• I am not able to traverse from the last step to the first step in a multistep form 2011-04-21

I have created a multistep form with five steps using programatically. Is there any way to traverse to first step form the fifth step? I have created multistep form as given in example module. In the module, each step gives previous and next link onl

• First Steps of Making a Programming Language 2011-06-15

I am thorough with programming and have come across languages including BASIC, FORTRAN, COBOL, LISP, LOGO, Java, C++, C, MATLAB, Mathematica, Python, Ruby, Perl, JavaScript, Assembly and so on. I can't understand how people create programming languag

• Cannot start/stop Websphere from the First Steps Window 2011-06-23

When attempting to start or stop a Websphere 7 service from the First Steps window it fails with the following error; Cannot run program "C:\WebSphere\AppServer\bin\WASService.exe": CreateProcess error=740, The requested operation requires eleva

• first step in embedded system 2012-04-05

Possible Duplicate: How to become an embedded software developer? I'm third year undergraduate computer engineering I and I like embedded system I would like to be my specialize in engineering , but I found it a fast topic , so from your experience w

• What are the first steps a tester should perform when using combinational testing? 2012-05-29

Hypothetically, lets assume that I am testing the "Search and compare flights" functionality of this website*, and I want to apply a combinational testing technique. Where should a tester start this testing process? What should their first steps

• Software testing: a first step to development? 2012-08-15

I've been testing Ubuntu daily builds for QA since last month. I wasn't too confident in learning development yet, because I didn't feel familiar with encountering a bunch of code. But I want to write code and develop software one day. So far, I see

• What are the first steps for growing UX operations in an agency? 2012-09-25

I'm the first UX hire at an aggressively growing digital agency. I've been doing UX for corporations for a few years and before that was mainly doing dev and some minor design work. I'm working on tweaking the standard client workflow to incorporate

• Efficient way to perform elementary integration step with NDSolve internal method 2012-09-28

I'm trying to tweak the NDSolve function to perform one elementary integration step (using some explicitly selected stepping algorithm via Method option). Such a possibility is crucial for me, since after each performed step I'd like to check for som

• Second step of a funnel higher than first step in Google Analytics 2013-05-29

I have a problem with the funnels in Google Analytics. So I have a e-commerce website that I want to track the user path to a purchase. I want GA to track if a user goes trough these steps [Item page] → [Purchase] → [Checkout]. I thought this could b