Tuesday, July 20, 2010

Core Data or Sqlite - iPhone Local Persistence

This post is after a while. So, what have I been up to. Nothing new in Java world, but with my third iPhone / iPad application underway, I think I have made some serious progress in iPhone development.

So, what is today's topic of discussion? Recently I was faced with an option to choose between SQLite or Core Data for data storage on the device. I was all for SQLite until I found out that all the examples provided by Apple using SQLite were gone.. Not really gone, but switched to Core Data. So, that's how quest to choose began.

So, what do you look into making choices. My first reaction was to see what people out there are doing. What are they using and what choices are they making. Of course, not to follow them, but just to gain insight into their thoughts.

And I found arguments going both ways. Instead of pointing them out, as a quick google search will lead you there, I want to mention my decision. Am I glad I went with Core Data. Let me explain.

Before I made the call to use Core Data I decided that I needed to clearly identify my 'need' and find a solution for it, and which-ever solution fit, I'd go for it. I could quantify my 'need' in the following:

1. invoke a web service and get data and store it on device.
2. query device for listing of saved data with basic querying.
3. update of individual records of saved data.
4. access related entities when reading one entity.

Could I do all of above with SQLite? Yes. What about Core Data? Yes. So, still not clear which one to choose. Next I though of what other 'needs' may arise?

1. update multiple records based on criteria
2. generate detailed, grouped reports

These are possible 'needs' an application may have, but did I foresee this in my app? No. Would SQLite be suitable? Yes. What about Core Data? Its do-able, but not elegant. (such as, looping and updating each record instead of issuing 'update xyz where abc')

This settled one question. If I had these needs, then I would have used SQLite, for performance, ease of use. But, since I do not have this need, which one should I go with? That question still was not answered. Next thought: What does Core Data give me that SQLite does not?

1. ORM 'style' working. (Note: Core Data is not ORM. It does not map a Database Schema, actually it is DB agnostic in a way.. But it works just like ORM models, such as Hibernate models, in many respects).
2. Lazy loading of related entities, reverse relations
3. API for querying, ordering & filtering
4. Session based updates.

Basically, all the goodies that I know and love about Hibernate. (not really all, such as, I cannot bypass the Core Data request and query objects by SQL as in Hibernate). What else would Core Data provide?

1. A new skill.
2. A better understanding of Core Data, so that if and when I have to decide again, I would make a more informed decision.

So, you can see.. I am leaning towards Core Data. So, what is there to convince me? Apple. There is a reason (with Apple knows better) why they switched their examples from SQLite to Core Data. Though it is unlikely that they will drop support for SQLite (Core data itself uses SQLite as one of data store options), but there is going to be lesser and lesser support in Apple documentation for SQLite.

So, there you have it. My reasoning to switch to Core Data. Having said that, Am I glad that I chose it. (Did I already say that?). Accessing and writing data is a breeze. In summary, I would say that if you do not have a 'need' that overwhelmingly 'requires' a SQLite solution, go for Core Data.

Wednesday, February 17, 2010

Extending UsernamePasswordAuthenticationFilter in Spring Security

So, you are using Spring security, and you want to process some information after a user has logged in (say, load something in session) or after failed login attempts (such as lock an account after a set number of failed logins.. ), here's how it can be done.. Hope this helps.

Before I go on to describe what goes into it, thanks to the following. These links are a bit dated, but they did help me along.

Custom AuthenticationProcessingFilter for spring security to perform actions on login
An example of the security config for a custom auth filter (see last post)
Spring's documentation on adding own filters

Now, here we go. What is described below works for Spring Security 3.0.0.RELEASE.

1. We need to extend UsernamePasswordAuthenticationFilter

It used to be AuthenticationProcessingFilter, which is depricated.

In this example, we just write to the console. You would do something more important.

public class CustomUsernamePasswordAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {

protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, Authentication authResult)
throws IOException, ServletException {
super.successfulAuthentication(request, response, authResult);

System.out.println("==successful login==");

protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
super.unsuccessfulAuthentication(request, response, failed);

System.out.println("==failed login==");


2. In spring security context, we will be overriding the form login filter FORM_LOGIN_FILTER.

This used to be AUTHENTICATION_PROCESSING_FILTER, which is not used now.

<http ...
<custom-filter position="FORM_LOGIN_FILTER" ref="customUsernamePasswordAuthenticationFilter">

Note: Since we are replacing the default FORM_LOGIN_FILTER, we should not use <form-login login-page... , as that will try to create the default filter and you would get an exception (that there are two filters defined at the same position).

3. Since we are using the custom FORM_LOGIN_FILTER, we need to set the following property on <http ..

<http auto-config="false"

4. Another thing to set at <http is entry-point-ref

Again, since we are altering the default behavior, we need to tell what is the entry point for the form login.

<http entry-point-ref="loginUrlAuthenticationEntryPoint"

And, correspondingly, define the loginUrlAuthenticationEntryPoint bean. Note that LoginUrlAuthenticationEntryPoint used to be AuthenticationProcessingFilterEntry which is depricated.

<beans:bean id="loginUrlAuthenticationEntryPoint"
<beans:property name="loginFormUrl" value="/login.html">

5. Remember the filter 'customUsernamePasswordAuthenticationFilter ' we used in in step 2 and extended in step 1, we have to define it.
<beans:bean id="customUsernamePasswordAuthenticationFilter"
class="com.yourapp.web.security.CustomUsernamePasswordAuthenticationFilter" >
<beans:property name="authenticationManager" ref="authenticationManager">
<beans:property name="authenticationFailureHandler" ref="failureHandler">
<beans:property name="authenticationSuccessHandler" ref="successHandler">

<beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/login.html">
<beans:bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.html?login_error=true">

6. In the definition of 'customUsernamePasswordAuthenticationFilter' we are identifying the 'authenticationManager', so when you define your authentication manager provide an 'alias' for it:

<authentication-manager alias="authenticationManager">

There you have it. Share and enjoy.

Saturday, February 13, 2010

Flash Scope alternative in Spring MVC

Flash scope in Spring MVC has been a topic of discussion, and even though Spring provides infrastructure to create custom scopes, I went with another solution. Hey.. as long as it works.. (for your requirements and within your constraints)...

The goal:
Set a flash error or flash message before redirecting to another view. The next view consumes the flash, displays it and clears it (so that on further redirects or refreshes the message does not appear).

Here we go:

1. Create a SessionHandler (or any other dependency you can inject into your Controller).

public class SessionHandler {

private transient final Log log = LogFactory.getLog(SessionHandler.class);

public static final String FLASH_MESSAGE = "FLASH_MESSAGE";
public static final String FLASH_ERROR = "FLASH_ERROR";
public static final String FLASH_TYPE = "flashType";

public void flashError(HttpServletRequest request, String message){
flash(request, FLASH_ERROR, message);

public void flashMessage(HttpServletRequest request, String message){
flash(request, FLASH_MESSAGE, message);

public String consumeFlash(HttpServletRequest request, String flashType){
String flash = null;
try {
flash = (String) request.getSession().getAttribute(flashType);
} catch (Exception e){
log.warn("Error retrieving flash value of type:" + flashType);
return flash;

* helper method that sets the flash message of the type provided in session scope
private void flash(HttpServletRequest request, String flashType, String message){
request.getSession().setAttribute(flashType, message);

This handler accepts the flash error/message and sets in session. When requested for it (using consumeFlash), it gets it from the session and clears it. It returns null if it does not find it in session.Next,

2. Use it in your controller.

if(<error condition>){
sessionHandler.flashError(request, "user.error.user.id.invalid");
return new ModelAndView(cancelView);
or on a successful operation,
sessionHandler.flashMessage(request, "user.message.saved.successfully");
return new ModelAndView(getSuccessView());
As of this point we can flash an error or a message. Next step is to show it. Here is where my solution gets a bit different. I created another controller (FlashController) that creates a view of type MappingJacksonJsonView and returns that along with flash messages as redereredAttributes.

3. Create FlashController (which is ResourceLoaderAware) and map it to a url (say /flash.html)

public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {

MappingJacksonJsonView view = new MappingJacksonJsonView();
Map model = new HashMap();
Resource resource = null;
PropertyResourceBundle propertyResourceBundle = null;

resource = resourceLoader.getResource(resourceLocation);
propertyResourceBundle = new PropertyResourceBundle(resource.getInputStream());
}catch (Exception e){
log.warn("Error loading resource bundle");

String flashError = sessionHandler.consumeFlash(request, SessionHandler.FLASH_ERROR);
String resolvedFlashError = flashError;
if (!(propertyResourceBundle==null)){
resolvedFlashError = propertyResourceBundle.getString(flashError);
}catch(Exception e){}
model.put(SessionHandler.FLASH_ERROR, resolvedFlashError);

String flashMessage = sessionHandler.consumeFlash(request, SessionHandler.FLASH_MESSAGE);
String resolvedFlashMessage = flashMessage;
if (!(propertyResourceBundle==null)){
resolvedFlashMessage = propertyResourceBundle.getString(flashMessage);
}catch(Exception e){}
model.put(SessionHandler.FLASH_MESSAGE, resolvedFlashMessage);

Set renderedAttributes = new HashSet();

return(new ModelAndView(view, model));

Couple of things to note here. Since I've added support for key-value from a resource properties file, if the flash message sent is found in the resource file (classpath:ApplicationResources.properties by default), it uses that, or else it just sends the message sent.

Now the question is, we have a way and place to set the flash error/message and now a way to retrieve the message. How do we show it on a view. That comes next.

4. Invoke FlashController from your view using ajax call.

$.each(data, function(i,item){
var curDiv = (i=='FLASH_ERROR')?"#flashError":"#flashMessage";
<div id="flashError" style="display:none;"></div&gt;
<div id="flashMessage" style="display:none;"></div>

That's it. using jQuery getJSON method, we get the messages, and if received show them.

I'm sure there are limitations and constraints in this method, such as using javascript, ajax may be a constraint. I'm sure workarounds can be found. And if not, then using Spring's custom flash scope is always an option. I would like to hear from you if you find any security issue in this solution. n'joy.

Additional reading:
Custom Scope for Flash Scope discussion
Spring by example Custom ThreadScope

Friday, February 5, 2010

JasperReports in Spring with runtime report and format

This post is after a while. I recently finished a phpCake project, which I want to write about a bit, but in an another post. This post is about something I recently was challenged with and came up with a pretty decent solution. (I think..) hope it helps.

My current project is a Spring based solution, built on Spring MVC, spring Security, Sitemesh, JPA with Hibernate and all other bells and whistles. It also integrates JasperReports. Spring has built in support for JasperReports. You can see it documented (that can be debated) here. It has built in View implementations for different renderings of the JasperReports. (csv, html, pdf, xls).

It works, with a little bit of digging.

But it was not sufficient for my requirement, which was:
Allow for rendering of a jasper report picked at runtime in a format chosen at runtime.

The View resolvers pretty much expect that the report url be defined at configuration time. Spring has another View implementation (JasperReportsMultiFormatView) that allows run time format choice, but the JasperReport url still needs to be defined at configuration time.

I understand where this behavior comes from, as majority of the time the reports are built and packaged with source code and the rendering is wired. But it fell short of allowing picking JasperReport at runtime. So, determined to get it to work within the framework of Spring Jasper View implementations (with its nifty support for both .jrxml and compiled .jasper) , I came up with this solution. Of course, this is one implementation, and can be optimized.. If you do, please drop in a note in comments.

1. Create a JasperReportsViewFactory

protected static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";

public AbstractJasperReportsSingleFormatView getJasperReportsView(HttpServletRequest httpServletRequest,
DataSource dataSource, String url, String format, String fileName){
String viewFormat = format==null?"pdf":format;

// set possible content headers
Properties availableHeaders = new Properties();
availableHeaders.put("html", "inline; filename="+fileName+".html");
availableHeaders.put("csv", "inline; filename="+fileName+".csv");
availableHeaders.put("pdf", "inline; filename="+fileName+".pdf");
availableHeaders.put("xls", "inline; filename="+fileName+".xls");

// get jasperView class based on the format supplied
// defaults to pdf
AbstractJasperReportsSingleFormatView jasperView = null;
if(viewFormat.equals("csv")) {
jasperView = new JasperReportsCsvView();
}else if(viewFormat.equals("html")){
jasperView = new JasperReportsHtmlView();
}else if(viewFormat.equals("xls")){
jasperView = new JasperReportsXlsView();
jasperView = new JasperReportsPdfView();

// get appContext. required by the view
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(

// set the appropriate content disposition header.
Properties headers = new Properties();
headers.put(HEADER_CONTENT_DISPOSITION, availableHeaders.get(viewFormat));

// set the relevant jasperView properties

// return view
return jasperView;

As you can see, the return type is a super class of all the Spring's Jasper View implementations. Another feature in this method is to set content disposition and supply an alternate file name for the downloaded file.

2. Use It
Once you have this , make it a dependency in your controller responsible for launching the report, and provide the url, format and other required elements for the view resolver, and execute your report.
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws Exception {

Map model = new HashMap();

String format = httpServletRequest.getParameter("format");
//Default format to pdf
if (StringUtils.hasText(format)){
if (!(format.equalsIgnoreCase("pdf") || format.equalsIgnoreCase("html")
|| format.equalsIgnoreCase("csv") || format.equalsIgnoreCase("xls"))){
format = "pdf";
format = "pdf";

// get the View that will render the report.
// in actual controller, based on report id rquestd, get the report name and build URL and use
AbstractJasperReportsSingleFormatView jasperView = jasperReportsViewFactory.getJasperReportsView(
httpServletRequest, dataSource, "/WEB-INF/jasper/sample.jrxml",format,"DownloadFileName");

// add parameters used by the report
model.put("Company","Sample Company");
// more here...

return new ModelAndView(jasperView, model);

I'm sure there are some.. One that I know of is Jasper's sub-reports. For me it is not an issue right now as we are not supporting Jasper sub-Reports in our application.

There you have it.. short and sweet. Here are some other links if this is not what you're looking for.

Matt Raible's blog on JasperReports with Appfuse and Spring (dated)
A springOne presentation of Spring and JasperReports
Filename discussion using JasperReportsMultiFormatView using configuration

Thursday, September 17, 2009

Baking with cakePHP

Its been a while..

So lately I've been baking with cakePHP. Since I'm new to php and new to this framework, my thoughts might overlap between the two. A comment for the cake might be due to php and vice-versa. Anyway, here it goes. Instead of pro's and con's, I'll write as likes and don't-likes.. in no particular order. As I don't think I can judge php and related technology being a newbie..

First thoughts of using phpcake reminded me of Grails. very simple, less code and code generator tools.

Don't Like:
Too much reliance on convention over configuration. I'm still a bit 'old-school' if you will on this. My gripe with this is that if you live a framework (meaning that is all that you do), then convention is all ok. But if you switch between projects using different frameworks, configuration is a lifesaver. You know exactly what goes where and why. Personally I fall in the second camp and don't like to rely on too many 'automagic' methods.

convention overrides. It gives ways to override conventions. Such as /controllers/opportunities_controller.php expects and uses /models/opportunity.php model and will error out if model not present. But if you want to use /models/client.php model in /controllers/opportunities_controller.php, just declare a member:

$uses = array('Client');

or if you want to use other models, declare:

$uses = array('Opportunity','Client');

bake tool. It generates the models, views, controllers with staple code. The generated code is very workable to customize.

Don't Like:
ACL. Nothing wrong with ACL, but the heavy reliance on ACL for Authorization. I wish they support url pattern for authorization as an alternative. (maybe they do.. but I didn't find a good example). As a comparison see aceigi ( http://www.acegisecurity.org/petclinic-tutorial.html and scroll to block of code just before section 'Start petclinic's database)

Also, there is not much support for 'rapid development' with authorization using ACL. Everyone seems to create a different way to implement the actual authorization permission checking, especially from the views. So, I did the same. I even had to create my own session managed permission map, so that the system does not make tons of db queries on each page request.

And if you need to see at a later date which resource is authorized to whom, you need to be a SQL guru to run Tree/Hierarchical queries to find that out.

debugging. It helps a lot, and shows what framework is doing internally to get stuff done.

Don't Like:
Lack to true OO. a quick example. It allows you to create a Model class. When you invoke for a model from the Model's find() method, you don't get an instance of the model. You get an array with data?!

Lets say controllerA is using ModelB, you can say:
$model = $this->ModelB->find(...)
This is OO. But any call on
fails, as $model is not an instance of a class. It is an array.

components for controllers and helpers for views concept. This helps create reusable functionality and integration into controllers and views is seamless.

Don't Like:
models with data access methods. I had this gripe with Grails as well. There is a big debate on this issue about should models be aware of loading and fetching themselves or should that functionality be separated in DAO's. I don't want to judge which is better, but I like DAO's.. This helps models to behave as command or transport objects.

dynamic methods: findBy... and findAllBy.. on models.. that's a life saver.

$conditions for making complex queries. Build your complex query in an array, and supply it to the find method. This addresses one issue I had talked about earlier. See ORM and search/filtering In effect it uses the method I had talked about called 'search...(criteria)', where a criteria object is built and sent to the search method.

Don't Like:
->, => .. fingers hurt typing. This is php issue though..

There are features that I've not tried yet. Such as behaviors, elements etc. I may write again when I have a fuller grasp of the framework.

So finally, would I use php cake in future? Yes, for a quick prototype or where php is required in a project. But for more enterprise solutions I'd fall back on a java solution.

Friday, May 29, 2009

Designing Correct Solutions

Recently I was asked to describe my experience with solutioning. Fristly, I'm not sure if solutioning is a word, but, assuming that it means 'my experience with creating solutions', I said:

"Each opportunity presents a unique set of challenges and requires a unique approach for arriving at a solution.
I have come to realize that the best solution comes by consulting the team, explaining the solution to stakeholders, prototyping and building on the results, learning from past experiences and never shunning an advice.
Technology plays a role in making a right solution, but is not the driver for a solution. A customer's need should drive a solution.
A good solution is a correct solution. A solution can be enhanced by making it efficient, robust, cost effective and scalable. "

Well, I wrote this, that is why I have the original quote. And I thought, elaborate it in my post. So, instead of repeating what I said, I'll just highlight a thought and expound on it.

Each unique opportunity requires a unique approach
Meaning, one cannot take a cookie cutter approach to any challenge presented, how so much ever it is similar to what he/she has done before. Of course, one can take from one's experience, but each new task should be taken on its own merits, analysed, designed, solved and tested. Identify the challenges, find parallels in your experience and extract what is new and what can be reused, solve the problem as a whole independent problem with a whole independent solution.

Consult with the team
Ever so often, in this every changing and tele-commuting world, with vertical job descriptions, we forget that we are a part of a team. It is a job of a team to provide a solution. An architect, an analyst, a tech lead, a developer, a tester, a project manager, a designer, an information architect etc. etc. comprises a team. These people cannot, should not work in a vacuum. If an architect comes with a design, consult with your tech lead, your PM, your stakeholder before you set it in stone. If a developer comes up with re-usable components, consult with team lead, testers, folks who will maintain the codebase before you refactor. If an IA comes up with a new work flow, consult with who-so-ever it will affect before you put it in your wireframes. Increased communication brings increased clarity, increased productivity, decreases re-works and every team member appreciates the other.

Explain your solution to stakeholders
One of the reasons so many solutions fail to live up to their promise is that the end result is not what you were hired to build. There exists a flurry of agile methodologies that address this problem, be it SCRUM, XP, RUP or their variations. Among many things these methodologies try to solve is to make sure the solution is presented to stakeholders so that they can see the results sooner, and they can control the direction. Which is good. For me, it is as simple as honoring a contract. If you were contracted to build a wall, you build a wall. You do not build a picket fence and deliver at the end with no budget to correct it.

Prototype and build on results
As mentioned earlier, every problem presents a unique set of challenges and require a unique set of solutions. From them are those solutions that one has no experience with, or just a better way of achieving the results. I say, go for it. But prototype first, on a small scale. If it works, then refactor and build. If not, you haven't lost much, rather learned which path not to take in future.

Learning from past experiences
Experience is a valuable asset and one's seniority is defined by his/her experience. In my humble opinion, a lot of repetition does not qualify for equivalent time of experience, rather the exposure to different tasks or variations of a task amount to experience. For example, there is difference in the quality of experience of a person who has installed 100 bath tubs in peoples homes versus a person who has installed 10 bath tubs, but each in a different setting, such as a residential complex, a business, a public facility, a government institution etc. etc.

Having said that, experience is a valuable asset and lessons should be extracted from it, and applied in your work. If you find an urge to try someting fanciful and it cannot be justified within the context of a project, try it on a personal pet project. (of course, I would not say do not try it.. just don't try it on a project at the expense of your business sponsor)

Never shun an advice
Even if it is from a person not in your descipline. Be grateful that someone cares about you and wants to help you achieve your goals. If you disagree, disagree on its merits or demerits, not because 'who is he/she to say this.' All members of a team come from varied backgrounds and have had unique experiences in their past work or jobs, and if someone offers an advice, hear it. This does not mean, you do not excercise your judgment. No. Rather, what I am saying is, use that judgment to analyze the advice. If there is something valueable in it, acknowledge it and use it. If not, then you have heard.

Technology is not a driver of a solution
Rather, the business owner, stakeholders are the driver. One should not forget that someone owns the project and has funded it. They need something out of it which brings some business value to them. That should be the goal in designing a solution. Then, use the technology to achieve it. Technology is a means to the solution, not the goal.

Yes, sometimes you may be bound by technology choices, which are made by someone sitting in a board room somewhere and has paid enormous license fees. And sometimes, you are the boss, you can choose your latest and greatest tool available out there. So, in the first case, make the best of what is available to you. If you want change, get every concerned party on board before changing. Be prepared to explain that the technology change is supprotable, maintainable, affordable and necessary.In the second case, do not get carried away. Use the technology that is just right, affordable and maintainable for your client, even if it means not using your favorite tool that was beta-released a day before.

A good solution is a correct solution
How often do we see emphasis put on the scalablity of the solution, or configurability or how cost efficient the solution is. But we loose sight of the bigger picture, is the solution put in place the 'correct' solution? Yes we can build a scissor that can cut thru 100 pages in 2 seconds and build it very cheap, but can a surgeon finely cut muscle tissue with it in an operation, the reason for builing this scissor?

So, the first thing one should focus on is that the solution is the correct solution for the problem. Once you have a correct solution, you can work day and night to make it most efficient, cost effective, robust, scalable or any other enhancement if you like.


Of course much more could be said, and I'm sure has been said. I welcome critique, corrections or additions.

Monday, April 27, 2009

Why Nassi-Schniederman a flowchart?

If you can use a flowchart synonymously with a Nassi Schniederman diagram (NSD), why would you want to use it? Flow-charts are widely supported in many tools and any algorithm can be possibly drawn with it, why would you need a NSD? I felt the need for it. Here is why.

While working on an SMS solution where the system responds to individual SMS text messages, each controlled by a single keyword. Each SMS has a response based on conditions such as, which phone was used to text, if the keyword was sent before from that phone, if a user had access to request a resource, etc. etc. I had to make sure that each and every possible scenario is handled by the system resulting in a successful or an error response. A logical approach would be to draw a flowchart outlining each possible path that ends in a response. While technically correct, I felt it did not deliver what I was looking for, which is a complete snapshot of all possible scenarios laid out, guaranteeing that no scenario was missing.

I recalled a good example in the book 'Groovy in Action', they used a NSD to identify how a method is invoked in a Groovy Class, depending on if the class instance is a Groovy Object, a Closure or an instance of Groovy interceptable. It identifies at the bottom of the chart which method gets invoked based on the path taken from top to bottom. I felt, if I laid out all possible scenarios in a diagram like this, it would 'shout' at me if I was missing a scenario/path a SMS message could take.

Next step was to draw one. Of course I could draw by hand, but I needed to document it as well. That resulted in a google search, and I did find couple of commercial products, and with little further search I found Structorizer (GPL), an open source product. Though rough on edges, it does the job. Very satisfied with the tool, outputs the diagram in a range of formats, including .png and .pdf.

As an comparison, I thought post an example NSD. Below you will find a NSD equivalent of a flowchart example found on wikipedia's flowchart page.

Flow Chart example (from wikipedia)

Equivalent NSD drawn using Structorizer.

So, experiment with NSD in addition to a flowchart. You never know why one diagram may add value over the other, even if technically they have the same information.