Enterprise Content Management

Armedia Blog

Archive for the ‘Software Development’ Category

A pattern for extracting JavaScript from HTML

June 3rd, 2011 by dmiller

When I wrote about separation of concerns in webapps, I said I would consider how to apply separation of concerns in my project.  This post is a progress report!  I have tried this pattern on several pages and so far, all is well.

Step 1 is obvious: just extract all the JavaScript embedded in the page into its own .js file, and then import the new .js file into the old page.  This part is easy.  The only challenging part might be any hard-coded event handlers embedded in your HTML.  Anywhere you have, e.g., <a href=”…” onclick=”clickMe()”/>, you have to remove such explicit event handlers, and add code like this to the .js file: myAnchor.click=clickMe.

Luckily our code doesn’t have anything like this, so Step 1 is purely mechanical (so far, anyway).

Step 2 is more interesting.  Obviously the new JavaScript file has to be completely static: you want it to be cached by the browser; that’s the whole point of what we’re doing.  So you can’t have any page-specific data in the JavaScript file.

Well, in our code base, the JavaScript needs some page-specific stuff.  The JavaScript has to know about the form field values, the ID numbers of the objects displayed on the form, and other odds and ends of data that may be different each time the page is viewed.

The approach I came up with is to encode such needed data in the HTML page, and then have the JavaScript look up the data when the page is done loading.  jQuery makes this dead easy; with jQuery it is very easy to manipulate the browser DOM.

So the only question is where to put this data on the HTML page?  I use HTML5 custom data attributes: attributes whose name starts with “data-”, can be attached to any HTML element, and are ignored by the browser.  I chose to add these attributes to the HTML body element:


<body
data-contextPath=’<%=request.getContextPath()%>’
data-docId=’<c:out value=”${docId}” escapeXml=”false” />’
data-step=’<c:out value=”${step}” />’


Then, in the JavaScript file, it is very easy using jQuery to read these values… Obviously this code has to run after the DOM is ready:

$(document).ready(function()
{
    contextPath = $("body").attr("data-contextPath");
    docId = $("body").attr("data-docId");
    step = $("body").attr("data-step");
....
}

After applying these two steps, I have a clean separation. All my HTML is in an HTML page, and all my JavaScript is in a JavaScript file (and all my CSS is in a CSS file, but that was already true).

This approach may be old hat for you experienced web developers out there.  Hopefully this approach may help a few of you like me: an old server-side grunt tossed into the wild-and-wooly Web side of things!

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

HOW TO: Make file uploads work with WizardPro jQuery Library and Spring 3.0

May 31st, 2011 by jhsu

My web application front-end code is built using HTML5, CSS3, and jQuery, using the Spring MVC 3.0 Framework to get back-end data back to the browser. We use the WizardPro jQuery library to aid the user in creating objects in the webapp (WizardPro is a great little jQuery library that enables you to create an object in a few steps).

Here is where my heartache comes in – WizardPro’s default form submit uses XMLHttpRequest. Don’t get me wrong, most of the time the default behavior works great! Specifically, it doesn’t work is when you need file uploads in your web app, therefore requiring an input type of file as a form control. Luckily, Spring MVC 3.0 fileupload support was available in our toolkit already (it provides a MultipartResolver for use with Apache Commons FileUpload). To be able to make file uploads work with WizardPro and Spring MVC file upload, you must do the following:

1.  For the Spring MultipartHttpServletRequest to work, set WizardPro’s  defaultAjaxRequest attribute to false when the wizard is instantiated.

$(“#wizard”).wizardPro({
defaultAjaxRequest: false,
<other attributes>…
});

2.  WizardPro uses a default form class called .defaultRequest, this makes sure the plugin handles the default form ajax requests submits and validation.  Make a copy of the .defaultRequest CSS classes and name it uniquely, i.e. .defaultRequest2.  In the form tag, set the class to this new class name, so the class will not bind wizardPro to use default ajax submits.  Instead WizardPro will use normal HTML form submits with the file input form control for file uploads.

BAD:

<form class=”defaultRequest” action=”someUrl” method=”post” enctype=”application/x-www-form-urlencoded”>

GOOD:

<form class=”defaultRequest2” action=”someUrl” method=”post” enctype=”multipart/form-data“>

3.  Set the encode type to multipart/form-data in the form tag, MultipartHttpServletRequest filter expects this.  Example in number 2 above.

When you follow the above steps, along with setting up Spring MVC’s multipartResolver in the servlet context spring configuration file, ensuring the form action URL and the controller @RequestMapping match, etc, then WizardPro and file uploads work great together!

P.S.  You may ask me, “Why not use an HTML5/AJAX supported file upload library?”.  Uploadrr is a great little library that does this, and I’ve tested it using Firefox 3 and Chrome 10, with both @RequestParam using MultipartFile and also with the HttpServletRequest Input Stream to get the file byte data.  Unfortunately, it does not work for our primary browser platform, IE8, which is why (for now at least), we can’t integrate this into the webapp.

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

"Display Intranet Sites in Compatibility View", or, WTF happened to my webapp???

May 27th, 2011 by dmiller

My project uses a lot of HTML5/CSS3/JavaScript goodies.  It works well and looks awesome… in Chrome 10 and Firefox 4.  Our User Experience designer tells me it works well and looks awesome in Safari, too.

Say, which browser is missing from the lists I just mentioned?  Right!  The very one which is our official platform. Like my boss says, ”if it ain’t working on IE8, it ain’t working.”  If I was a real web designer, and not a poseur wanna-be services developer, I’d wake up in cold sweats at night with those words echoing in my ear!

Actually, it works well and looks pretty good in IE8, too… most of the time (doesn’t look quite as awesome as the other browsers, but good enough).

But some of the time it doesn’t work and looks awful.  Even on my own laptop it used to change; sometimes it would work well and look good, and sometimes it wouldn’t work and look awful.  WTF??

So I discovered the F12 key.  In IE, F12 brings up Developer Tools.  In the upper right, you can see which “compatibility view” IE8 is using.  Quickly I found the pattern:

  • If IE8 works well and looks pretty good, it has decided to use IE8 mode
  • If IE8 doesn’t work and looks awful, it has decided to use IE7 mode, or even “quirks mode”

Our user experience designer jumped right on the problem, and found the “X-UA-Compatible” setting.  If you add this setting to your page header, you can advise IE8 which mode to use:

<meta http-equiv="X-UA-Compatible" content="IE=edge">

The “edge” part tells IE to use the lastest mode; so our forward-looking users using IE9 get to see IE9 mode.

But even after this fix, sometimes I would see our development site in IE7 or quirks mode!  What is up with that?

Eventually I found an MSDN page purporting to explain compatibility modes in IE 8.  The real valuable part of this article was in one comment:

The “display intranet sites in compatibility view” check box will override any meta tag or http header you put in the server or on the web page. There is nothing you can do except uncheck that box. Yes, this is a dumb design and it runs completely contrary to all available documentation but according to MS tech support it is “by design”.

So I found that checkbox. It is under Compatibility View Settings:

Screen shot showing the checkbox to display intranet sites in compatibility view

And sure enough, our development web site is in the “Local Intranet” zone. I unchecked that box, and ever since, IE8 works well and looks pretty good!

Of course, from a delivery perspective, this is pretty bad news. We can put headers and meta tags in our webapp all day long, but if our users are stuck in the “local intranet” zone and haven’t unchecked this box, IE8 will probably not work and look awful! As far as I can tell, there is nothing we can do about it!

“If it’s not working in IE8, it’s not working”… My cold sweats are starting!

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

Q: Why should I test my CMIS application against multiple repositories?

May 26th, 2011 by dmiller

A: To uncover my hidden assumptions!

Let me tell you a story about SharePoint, Alfresco, design choices, and downloading files.

My Java-based application uses SharePoint 2010 to store files.  CMIS is the obvious tool for my app to talk to SP 2010, and in fact, CMIS and SP2010 work just fine together, as you may have gathered from my colleague Tim Lisko’s last few articles.

So it came time for me to write the download feature, thus allowing users to download the files stored in SP2010 from my application.  Chemistry makes it very easy to retrieve a file object by ID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public InputStream getFileContentStream(Session session, String objectId)
{
try
{
Document file = (Document) session.getObject(objectId);
ContentStream csFile = file.getContentStream();
InputStream retval = csFile.getStream();
return retval;
}
catch (CmisBaseException cbe)
{
throw new AcmRepositoryException("Could not get file contents",
cbe);
}
}

So I said to myself: OK, I’ll just construct a URL including the CMIS object ID, then I’ll pass that object ID straight through to my Java method.

All you guys that have used CMIS against both Alfresco and SharePoint (hi Colin!) know what’s coming next.

SP2010′s object ID looks like this: 42-512.  So my URL’s looked like this:

http://<host>/<context>/docs/download/2316/42-512

No problems here!  A nice valid URL, and my download code worked great!

So yesterday, I had an idea (actually, my boss had the idea). Let’s stand up the app against Alfresco!

Alfresco’s object IDs turn out to look like this:

workspace://SpacesStore/ecd60ff9-adbb-45fb-824d-9bc93aa61b0f

Which turned my download URL into this grim-looking thing:

http://<host>/<context>/docs/download/2316/workspace://SpacesStore/ecd60ff9-adbb-45fb-824d-9bc93aa61b0f

I’ll leave what happened when I clicked that link to your imagination (hint: it wasn’t pretty!).

Since I wanted to be up and running on Alfresco pretty quickly, I applied a short-term fix: I replace the colons and slashes in the object ID with strings that should not be part of any real object ID, and my download controller reverses the process.  Now my URLs look like this:

http://<host>/<context>/docs/download/2316/workspace-c!–s!–s!-SpacesStore-s!-ecd60ff9-adbb-45fb-824d-9bc93aa61b0f

The good: the download code works again.  The bad: I shouldn’t have to encode and decode my own download URL this way!  The ugly: as you can see, the aesthetics of the above URL leave a lot to be desired.

The long-term fix (by “long-term” I mean “by Monday”) is to not place the object ID in the URL in the first place, and use an opaque sequence-driven numeric database ID instead.  Then all my URLs will look like this, no matter what repository is in use at the moment:

http://<host>/<context>/docs/download/2316/42

Why didn’t I do this in the first place?  Well, I certainly should have.  For the most part I would never use a natural key such as the CMIS object ID in this situation.  I’m pretty sure I was trying to avoid a database lookup.  In the end I needed to do a database lookup anyway, but it never occurred to me to rethink my earlier design choices.

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

SharePoint 2010 – REST Atom Service and JAVA, part 2

May 26th, 2011 by Tim Lisko

This is the second of two blogs where I demonstrate how to use the openCMIS library from a JAVA client to connect to a CMIS enabled SharePoint site through the REST Atom service of SharePoint.

My previous blog “SharePoint 2010 – REST Atom Service.” provided information on how to connect to a SharePoint site and examined some of the SharePoint site’s capabilities. In this blog I will show how to use the openCMIS library and REST Atom Service to: 1) Access and Create Folders; 2) Iterate over SharePoint folders; and 3) Add documents to a SharePoint folder. This blog builds on the previous one, so reference it if you aren’t able to follow the code in this one.

Accessing and Creating SharePoint Folders

The following exmple will show you how to get and create folder objects. An additional class library is imported for accessing folders and another for getting a CMIS object by path.

 
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.CmisObject;

Folder folder = session.getRootFolder();
String folderprops=twolines + “ROOT FOLDER:” + newline +
tab + “ID: ” + folder.getId().toString() + newline +
tab + “name: ” + folder.getName() + newline +
tab + “path: ” + folder.getPath();//create new folder in the root folder
Map properties = new HashMap();
properties.put(PropertyIds.OBJECT_TYPE_ID, “cmis:folder”);
properties.put(PropertyIds.NAME, “mynewfolder”);
Folder newFolder = folder.createFolder(properties);//Check properties
String folderprops=twolines + “NEW FOLDER:” + newline +
tab + “ID: ” + newFolder.getId().toString() + newline +
tab + “name: ” + newFolder.getName() + newline +
tab + “path: ” + newFolder.getPath();

//Get another folder by path that already exists; CmisObject does not have a “getPath” method,
//so convert it to a CMIS folder
CmisObject cmisObj = session.getObjectByPath(“/2011″);
Folder fo = (Folder) cmisObj;

//Check properties
folderprops=twolines + “2011 FOLDER:” + newline +
tab + “ID: ” + fo.getId().toString() + newline +
tab + “name: ” + fo.getName() + newline +
tab + “path: ” + fo.getPath();

 

 

Results:

  ROOT FOLDER:
ID: -1
name: Shared Documents
path: /NEW FOLDER:
ID: 87
name: mynewfolder
path: /mynewfolder2011 FOLDER:
ID: 83
name: 2011
path: /2011

Things to note:

1) The path is relative to the Repository, so the “Shared Documents” library could be accessed at its top level with the path “/”.

Iterating over SharePoint Folders

The following exmple will show you how to iterate over the folder objects. An additional class library is imported iterating over CMIS objects.
 

 
import org.apache.chemistry.opencmis.client.api.ItemIterable;

FileList.setText(“FOLDER (Name; ID)” + newline + tab + “CHILDREN (Name; ID)”);folder = session.getRootFolder();
FileList.append(twolines+folder.getName()+”; ” + folder.getId());
tabs = tab;
this.getKids(folder);
}

private void getKids(Folder folder) {
//recursive method to go through library

ItemIterable children = folder.getChildren();
for ( CmisObject obj : children )
{
if(obj.getBaseTypeId().toString()!=”CMIS_FOLDER”)
{
FileList.append(newline+tabs+obj.getName()+”; ” + obj.getId());
}
}
children = folder.getChildren();
for ( CmisObject obj : children )
{
if(obj.getBaseTypeId().toString()==”CMIS_FOLDER”)
{
folder = (Folder) obj;
FileList.append(newline+tabs+”/” + folder.getName()+”; ” + folder.getId());
tabs = tabs + tab;
this.getKids(folder);
}
}
}

Results:

  FOLDER (Name; ID)
CHILDREN (Name; ID)Shared Documents; -1
/2011; 83
/05; 84
/03; 85
 

Things to note:

1) The root ID of SharePoint libraries is always “-1”
2) You can detect and display properties of files within the folders by using the base type “CMIS_DOCUMENT”.
3) The document ID produced from CMIS_DOCUMENT is not the one you see exposed in the SharePoint GUI, but it is the one you use to access a document in CMIS.

Adding a Document to a Folder

Putting documents into a SharePoint folder is typical operation you would need to do. I’ve shown how you get and create a folder, so now you have a place to put your file. A number of additional libraries are needed for this operation.
 

 
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.io.ByteArrayInputStream;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.apache.chemistry.opencmis.client.api.Document;

//details of the fileChooser not shown here. The goal is to get an InputStream
File file = fileChooser.getSelectedFile();
InputStream is = new FileInputStream(file);
long length = file.length();
BigInteger bi = BigInteger.valueOf(length);
ContentStream contentStream = new ContentStreamImpl(file.getName(), bi, “text/plain”, is);
Map properties = new HashMap();
properties.put(PropertyIds.OBJECT_TYPE_ID, “cmis:document”);
properties.put(PropertyIds.NAME, file.getName());folder = session.getRootFolder();
Document doc = folder.createDocument(properties, contentStream, VersioningState.MINOR);

Things to note:

1) This code shows the essentials, but actual implementation should provide for the various exceptions that could arise, CmisConstraintException and IOException for example.
2) New ContentStreamImpl requires a BigInteger, hence the conversion of the file length from long.
3) VersioningState can be MAJOR, MINOR, NONE, or CHECKEDOUT. Whichever you use must match the versioning enabled in the SharePoint library.

I have shown how you can use the Chemistry openCMIS library and REST Atom Service to:
1) Access and Create Folders; 2) Iterate over SharePoint folders; and 3) Add documents to a SharePoint folder.

I hope this will “de-mystify” how to perform common DM operations in SharePoint using the CMIS standard!


TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

Sharepoint 2010: REST Atom Service and JAVA

May 26th, 2011 by Tim Lisko

 

This is the first of two blogs where I demonstrate how to use the openCMIS library from a JAVA client to connect to a CMIS enabled SharePoint site through the REST Atom service of SharePoint. The “lessons learned” and tips I include come from examining MSDN and CMIS documentation, “how to” results from google searches, debugging, and the time honored “trial and error” development methodology!

Accessing SharePoint Site from JAVA Application

The following provides instructions and examples for connecting to and manipulating objects in a CMIS enabled SharePoint 2010 site from a JAVA Application using the Apache Chemistry OpenCMIS[2] API.

SharePoint 2010 Configuration

The OpenCMIS API uses the REST AtomPub services provided by a CMIS enabled SharePoint site. So the first step is to ensure the SharePoint site of interest is CMIS enabled. MSDN provides guidance for enabling CMIS in a SharePoint site[3] which I also cover in a previous blog “SharePoint 2010 – Implementing CMIS.”

Two other things are needed to connect to the SharePoint site: Authorization Credentials and the GUID of the SharePoint library of interest. CMIS Repositories are mapped to SharePoint Lists and Libraries. Unfortunately a session is based on a particular repository, so you cannot operate in two separate lists or libraries in the same session.

You need to be aware of two additional configurations in the SharePoint 2010 site: User Authorization and Versioning.

Authorization in IIS for the web site needs to match that set for the site in the Central Administration application.

Versioning is set in the SharePoint library settings. Whatever version setting is made must be mirrored in the REST method when adding a new document to the site.

Connecting to SharePoint CMIS Provider

The following is an example of connecting to a SharePoint CMIS Repository and establishing a session with that repository.

  import org.apache.chemistry.opencmis.client.api.Repository;
importorg.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.SessionFactory;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.enums.BindingType;
import java.util.HashMap;
import java.util.Map;public class CMISConnect {   public void fillParams()    {//define some values for the connection string
String rest_base = “http://armwssp:2333/_vti_bin/cmis/rest”;
String repository_id =”E8AE6B3A-BAC2-4B4C-B889-BAE76A8142E6″;
String atompub_url = rest_base + “/” + repository_id + “?getRepositoryInfo”;
String username =”username”;
String password = “userpassword”;

//put everything into a HashMap
Map<String, String> parameter = new HashMap<String,String>();
parameter.put(SessionParameter.USER,username);
parameter.put(SessionParameter.PASSWORD, password);
parameter.put(SessionParameter.ATOMPUB_URL, atompub_url);
parameter.put(SessionParameter.BINDING_TYPE,BindingType.ATOMPUB.value());
parameter.put(SessionParameter.LOCALE_ISO3166_COUNTRY,”");
parameter.put(SessionParameter.LOCALE_ISO639_LANGUAGE,”en”);
parameter.put(SessionParameter.LOCALE_VARIANT,”US”);
SessionFactory f = SessionFactoryImpl.newInstance();
Repository soleRepository = f.getRepositories(parameter).get(0);
Session session = soleRepository.createSession();
      }
}

 
 
 
 
 
 
 
 


 

Key things to note:

1) The ATOMPUB_URL session parameter is exactly that prescribed in the MSDN library referenced on the first page.
2) You establish a session on a Repository.
3) Though you can really only see one repository at a time in SharePoint, you have to use the “getRepositories” method and the first item in the list to instantiate the Repository object.

Retrieving SharePoint Library Properties and Capabilities

Now that you have a session you can look at some of the Repository (SharePoint library) properties and capabilities.
 

  RepositoryInfo info = session.getRepositoryInfo();
String myInfo = “CMIS version: ” + info.getCmisVersionSupported()+
        ”\n Repository ID: ” + info.getId() +
        ”\n Repository Name: ” + info.getName() +
        ”\n Product Name: ” + info.getProductName();
RepositoryCapabilities rc = soleRepository.getCapabilities();
String capabilities = twolines + “SUPPORTED CAPABILITIES:” + newline
+ tab + “isAllVersionsSearchableSupported: ” + rc.isAllVersionsSearchableSupported() + newline
+ tab + “isGetDescendantsSupported: ” + rc.isGetDescendantsSupported()+ newline
+ tab + “isGetFolderTreeSupported: ” + rc.isGetFolderTreeSupported()+ newline
+ tab + “isMultifilingSupported: ” + rc.isMultifilingSupported()+ newline
+ tab + “isPwcSearchableSupported: ” + rc.isPwcSearchableSupported()+ newline
+ tab + “isPwcUpdatableSupported: ” + rc.isPwcUpdatableSupported()+ newline
+ tab + “isUnfilingSupported: ” + rc.isUnfilingSupported()+ newline
+ tab + “isVersionSpecificFilingSupported: ” + rc.isVersionSpecificFilingSupported()+ newline
+ tab + “CapabilityContentStreamUpdates: ” + rc.getContentStreamUpdatesCapability() + newline
+ tab + “getChangesCapability: ” + rc.getChangesCapability() + newline
+ tab + “getRenditionsCapability: ” + rc.getRenditionsCapability() + newline
+ tab + “CapabilityQuery: ” + rc.getQueryCapability() + newline
+ tab + “CapabilityJoin: ” + rc.getJoinCapability() + newline
+ tab + “CapabilityAcl: ” + rc.getAclCapability();

 

Results:

  CMIS version: 1.0Repository ID: e8ae6b3a-bac2-4b4c-b889-bae76a8142e6
Repository Name: Shared Documents
Product Name: Office SharePoint Server
SUPPORTED CAPABILITIES:
isAllVersionsSearchableSupported: false
isGetDescendantsSupported: false
isGetFolderTreeSupported: true
isMultifilingSupported: false
isPwcSearchableSupported: true
isPwcUpdatableSupported: true
isUnfilingSupported: false
isVersionSpecificFilingSupported: false
CapabilityContentStreamUpdates: ANYTIME
getChangesCapability: OBJECTIDSONLY
getRenditionsCapability: NONE
CapabilityQuery: BOTHESEPARATE
CapabilityJoin: NONE
CapabilityAcl: MANAGE
 

Things to note:

1) Getting a RepositoryInfo object was not necessary – the same methods are available to the Repository object.
2) “twolines”, “newline”, and “tab” are public string variables I created for formatting purposes.
3) The “getCapabilities” method of the respository sets the RespositoryCapabilities object, but that object is not iterable, so you have to retrieve the capabilities you are interested in individually.

I hope that I have shown how you can use the Chemistry openCMIS library to connect to a CMIS enabled SharePoint 2010 site through the REST Atom Service and look at some of the SharePoint site’s capabilities.

In my next blog I will show how you how to use the openCMIS library and REST Atom Service to:
1) Access and Create Folders; 2) Iterate over SharePoint folders; and 3) Add documents to a SharePoint folder.


TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

Webapps and Separation of Concerns

May 19th, 2011 by dmiller

Separation of concerns is a big deal in software engineering.  Program structures should be nicely organized, such that distinct tasks are handled by distinct program elements.  In other words: no business logic wrapped up in our SQL statements; no data access in our user interfaces.  When concerns are not separated, a big ball of mud ensues, and changes to any part of a program ripple around to every other part; the cost of changes to such systems can’t be predicted, and they’re just generally no fun to work on.

OK then; what concerns should be separated, in terms of web application design?  How about these:

  1. The page structure
  2. The page look and feel
  3. The page behavior

Interestingly enough, these 3 concerns map directly to the technologies that so dominate our time:

  1. HTML
  2. CSS
  3. JavaScript

When I wrote that my new project involves some HTML5/CSS3 goodness, I wasn’t quite accurate.  It involves some HTML5/CSS3/JavaScript goodness!

When Web pages separate these concerns, each aspect is easier to read, easier to understand, easier to maintain, and easier to test.

  1. HTML should include only document markup, with hooks for CSS and JavaScript to latch onto (e.g. class attributes, id tags).  No JavaScript in the HTML; no inline styles.  This HTML should be usable as-is, even on devices with no/limited CSS/JavaScript capability.  Even gmail provides an alternative site for users who can’t / don’t want to use the full interactive goodness that is gmail.
  2. CSS should define styles and layouts; it should not include markup; no markup attributes or elements.  Styles should be defined in their own separate files, apart from HTML and JavaScript.
  3. JavaScript defines behavior; feature detection should be used for graceful degradation in the face of missing features.  JavaScript should be in its own separate file, apart from HTML and CSS.  Unobtrusive JavaScript codifies some practices implied by this separation of concerns.

To change the structure, all we have to read is HTML; we don’t have to understand all the implications of the CSS and JavaScript cluttering up the HTML we are trying to change.  Our nicely-encapsulated JavaScript files should aid in supporting the subtle (and not-so-subtle) differences between browsers; the browser quirks are handled in separate JavaScript functions, and don’t clutter our HTML or CSS.

Do your web applications separate markup, look and feel, and behavior nicely and cleanly?  Mine could do better.  Now that I’m aware of these concepts I’m thinking how best to apply them in my projects.

Many thanks to Christian Johansen, whose book Test-Driven JavaScript Development introduced me to these ideas.  The whole book is well worth reading.

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

Oracle and Java = Goodness

May 18th, 2011 by dmiller

Two exciting announcements from Oracle the last few days:

  • JCP.Next – Oracle Proposes Changes to JCP: Enforced transparency (open mailing lists, open issue trackers); and greater throughput (more frequent JSR updates, and deactivating JSRs when they stop making progress): who could ask for more?  The closed nature of many JSR mailing lists, and the slow progress of too many interesting JSRs has been a plague on the Java landscape for years.
  • JRockit is now free: Not only is JRockit free (as in beer, not in speech), but JRockit technology is being merged into the current OpenJDK such that soon, Hotspot and JRockit will converge into one Oracle JDK.  Awesome news on both fronts; by all reports, after a suitable warmup period, JRockit is way faster than Hotspot.  So today, JRockit is available under the same licensing terms as Hotspot; and tomorrow (or soon, anyway) the two VMs will become one!

All in all I am very happy with how Oracle has managed Java.  They brought order to the Java 7 quagmire, introducing real JSRs for Java 7 and Java 8 into the bargain.  They just seem to have a sense of purpose and direction that I hadn’t sensed from Sun for years.

 

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

The Browser is the VM

May 13th, 2011 by dmiller

Just like thousands (millions?) of others, I spent the last 9 years writing services for Web applications.  I always concentrated on the server side, and never took the browser seriously as a software development platform.  I assume I’m not alone, given the prevalence of sites like theserverside.com.

Well, my new project is using some HTML5/CSS3/jQuery goodness right now, and my viewpoint is changing.  HTML5 obviously provides a much broader surface than HTML4.  Still, consider what browsers have provided the last lo-these-many-years:

  • Widget toolkits: jQuery, extJS, JSF components, on and on: there is no end of really interesting UI toolkits for browsers
  • Programming: JavaScript is a seriously powerful language
  • Remote procedure calls: XMLHttpRequest now, Web Workers soon
  • Local storage: jQuery and other JavaScript libraries use DOM tricks to cache data locally now; HTML 5 offers true local storage
  • Sandboxed execution: Chrome runs each tab separately from the others… hopefully other browsers either have already, or will follow soon

In short, browsers offer all the convenience of other virtual machines.  When I write Java programs, I think of the Java Virtual Machine as my platform; essentially, the JVM is my operating system.  Well, when the user interface developers write code to run in a browser, the browser is their platform; the browser is the operating system.

So: what does all this mean for browser developers?

Browser developers have to understand their platform with the same intimacy (or, hopefully, much more intimacy, given the lackadaisical state of software development these days) as Java and dotNET developers understand theirs.  (Unfortunately the diversity between browsers is much much broader than Java & dotNet folk have to deal with!)

Browser developers have to architect and design their solution with the same care and attention to detail (or again, hopefully much more, given that the Big Ball of Mud architecture is still the most popular) as other platform developers.

System and software architects have to think of browsers as client nodes in a client-server system… Maybe everyone else understands this already, but this is a new thought for me.  I always thought of browsers as similar to dumb terminals in a mainframe time-sharing system.

I am probably late to the game in terms of realizing just how capable browsers really are.  I have a strong background in server-side tools like Oracle and the Spring Framework.  But my knowledge of modern HTML, CSS, and JavaScript is very limited!

The browser is a capable virtual machine in its own right.  It behooves me to understand the platform my users have to live with!

 

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare

Beginner Thoughts on Alfresco Architecture

February 15th, 2011 by jhsu

If you asked me three months ago if I enjoy developing to Alfresco’s repository and Share UI (specifically Enterprise 3.3.3), it would have been a frustrated “NO…” — Not frustration about WANTING to learn a new product, since that is always exciting and challenging, but frustration on not understanding the architecture, the BIG PICTURE, to know how to use the product.

I’m not going into any deep personal thoughts on Alfresco ECM architecture today; rather I am going to share my thoughts on working with the Alfresco repository and the Alfresco Share UI from a software developer’s standpoint that had (almost) no prior experience with Alfresco, or ECM platforms in general.

I am relatively new to Armedia, and went headfirst into learning Alfresco ECM.  NOTE! It is generally a good idea to get a “crash-course” in a new product from a product expert before jumping headfirst into a new product, even if Alfresco has thorough documentation via their Wiki (http://wiki.alfresco.com/wiki/Main_Page).  My coworkers and I had the pleasure to have an “Alfresco Architecture Crash-Course” presentation shared with us from Dimy Jeannot who was working on another Armedia Alfresco project.

The high-level architecture is as seen below.  Any numerous UI technology, frameworks, etc are represented by the presentation layer (Alfresco Share, jQuery, EXTJS, etc).  The presentation layer can communicate with the Alfresco repository, or via Alfresco data web scripts (data layer).

High-Level Alfresco Architecture

High-Level Alfresco Architecture

The more detailed Alfresco architecture is seen below; we use the Alfresco Share UI as our example here.  Share, and any custom Share code you write, calls RESTful presentation and/or data web scripts.  These web scripts, in turn, communicate directly with Java Core Services exposed by Alfresco (i.e. document, search, node), or any custom services you may need to add for your application, to communicate with the Alfresco Repository.

Never assume that Alfresco and Share are running on the same server host.  For example, you should not hardcode localhost or any other hostname in your Share code.  Instead, use Share’s proxy URL when invoking a data web script (i.e. from a Share ftl, use Alfresco.constants.PROXY_URI when invoking your data web script -> Alfresco.constants.PROXY_URI + “<URL>”).

Where should you store your web scripts?  If you are concerned with data, store your web scripts in the data layer.  When your data web scripts are deployed, they are stored at webapps\<alfresco webapp context>\WEB-INF\classes\alfresco\templates\webscripts, create custom packages for your application.  Else you are concerned with presentation (such as from a Share dashlet), store your code in the presentation layer (it should then in turn call a data web script to get its data!).  When your presentation web scripts are deployed, they are stored at webapps\<share webapp context>\WEB-INF\classes\alfresco\site-webscripts, create custom packages for your application.

Detailed Alfresco Architecture

Detailed Alfresco Architecture

Looking back now and reading the Alfresco Wiki’s, the Alfresco architecture makes sense, I just needed guidance on putting the big picture together to start feeling comfortable with working with a new product.  If you ask me do I enjoy developing with Alfresco now, I would say yes <with smiles>.

TwitterFacebookLinkedInStumbleUponPinterestGoogle+DeliciousDiggPrintFriendlyShare
Copyright © 2002–2011, Armedia. All Rights Reserved.