Friday, June 12, 2009

iGoogle like interface using Apache Shindig + JQuery

Shindig is a very useful Apache project currently in incubation. It provides a gadget server, social data server and javascript gadgets.* and opensocial.* APIs.

My team at Wipro has been working on Apache Shindig since July 2008 and, we have successfully put together a framework that allows rapid development of Social Enterprise applications. This framework is called WiproWeb2Works and, provides among other things, a fully functional igoogle-like container for gadgets. We have in fact put a reference implementation of WiproWeb2Works framework - Festa on the Web for any one to get a feel of a "portal" application based on Apache Shindig. I'd encourage you to register (free!) and try it out today.

In my honest opinion, Shindig is the best way to jumpstart development of a Web 2.0 content mashup/social application. While this is great news for those of us that want to quickly create slick applications, there are some rough edges though that need to be ironed out to make it production ready.

Shindig provides a sample container right out of the box. This container is as elementary as a "hello world" application that you try in Chapter 1 of any book. It hardly demonstrates the power of Shindig or the potential it holds for building an app that looks & feels like iGoogle.

In the remainder sections, I'd focus on the Container development in particular and, share tips & tricks to help you learn from our mistakes and, build apps that are better and faster than portal apps.

There are plenty of articles out there on building websites that look like igoogle. Here are 2 of my favorites -
(1) A JQuery based iGoogle interface by James Padolsey
(2) A Dojo based iGoogle interface by Matthew Russell

In the first article, James has provided a fully functional browser compatible source code on implementing an interface with drag & drop niceties. In the second article, Matthew shares his experience of implementing a shindig container using dojo.

To be continued...

Thursday, May 21, 2009

Enable security in Tomcat

I finally figured a way to enable Security Manager on Tomcat 6.0 (windows) . The documentation at Apache Tomcat's official website is incorrect and, needs revision.

Please follow these steps to enable security on your Tomcat server:-
1) Right click on the tomcat monitor icon

2) Open the Configure dialog.
3) Click on the Java tab

4) Add 2 system properties - java.security.manager and java.security.policy that points to the policy file. I'm pointing to the default catalina.policy file that ships with tomcat distribution.

5) Add the 2 properties to java options
6) Click OK.
7) Stop Service
8) Start Service
9) Tomcat is now running with security enabled.

Thanks for reading this post. Please leave comments/suggestions to let me know if it worked for you.

Saturday, March 28, 2009

Ant Task for YUICompressor

Here is a macro I wrote to look for javascript files recursively in a source folder, compress every one of them using YUICompressor and copy the compressed javascript files to target folder. The usage of the task cannot get any simpler than this:-


< target name="compress" >
<yuicompress src="uncompressed_folder" target="target_folder"/>
</target>

Here is the macro definition for yuicompress. Just place it before the yuicompress task in buildfile.xml


<property description="YUICompressor" name="YUICompressor" value="./build/yuicompressor/yuicompressor-2.4.2.jar" />


<macrodef name="yuicompress">
<attribute name="src"/>
<attribute name="target"/>
<sequential>
<echo message="Compressing files at @{src}" />
<!-- create target folder if it doesn't exist already -->
<mkdir dir="@{target}"/>
<!-- Create directories recursively first.
Exclude javascript files as they will be put by YUICompressor later on -->

<copy todir="@{target}">
<fileset dir="@{src}">
<exclude name="**/*.js"/>
</fileset>
</copy>

<!-- Compress js files recursively -->
<apply executable="java" parallel="false" verbose="true" dest="@{target}">
<fileset dir="@{src}">
<include name="**/*.js"/>
</fileset>
<arg line="-jar" />
<arg path="${YUICompressor}" />
<arg value="--charset" />
<arg value="ANSI" />
<arg value="-o" />
<targetfile />
<mapper type="glob" from="*.js" to="*.js" />
</apply>
<echo message="Files compressed and copied to @{target}" />
</sequential>
</macrodef>


Reminder: Remember to replace YUICompressor's location marked in red above

Please leave a comment if you found it useful.

Wednesday, December 31, 2008

Deploying Shindig in a non root context

Shindig is configured, by default, to be accessed in ROOT context. While this is ok for development purposes, it is not desirable in a production setting where main applications may already be running in ROOT context OR in cases where a web server acts as a HTTP Reverse Proxy for an Appserver.

This post describes the steps I followed to make Shindig gadget and opensocial containers work on my Tomcat Server in a non-Root Context. Thanks a ton to Kevin Brown for providing valuable pointers in this email chain.

The following steps would enable deployment of Shindig in a non Root context on Tomcat and other AppServers.

1) Build shindig using steps provided at http://incubator.apache.org/shindig/#tab-building

2) Copy shindig/java/server/target/shindig-server-1.1-SNAPSHOT.war to a temporary folder (say c:\temp)

3) cd c:\temp

4) unzip shindig-server-1.1-SNAPSHOT.war to c:\temp\shindig folder

5) cd shindig/WEB-INF/classes/containers/default

6) Open container.js in a text editor. Since the default setting assumes shindig to be in the root context, it is necessary to modify all URLs for opensocial calls to work correctly. After updating all URLs, save changes and close container.js.

(There are some properties in shindig still that are pending migration to container.js, hence step#7 to 10. Thanks Carmen and Gerald for this suggestion)

7) Extract shindig.properties from shindig/WEB-INF/lib/shindig-common-1.1-SNAPSHOT.jar to shindig/WEB-INF/classes folder

8) Remove shindig.properties from shindig/WEB-INF/lib/shindig-common-1.1-SNAPSHOT.jar

9) cd shindig/WEB-INF/classes

10) Open shindig.properties in a text editor. Since the default setting assumes shindig to be in the root context, it is necessary to modify all URLs for gadget/concat calls to work correctly.

11) Fix Shindig's sample opensocial container "samplecontainer"
(a) cd c:/temp/shindig/gadgets/files/samplecontainer
(b) Open samplecontainer.js in a text editor
(c) Append Shindig's context name in the URL assigned to the variable "socialDataPath" (line# 44)
(d) Append Shindig's context name in the URI passed to sendRequestToServer() within requestGadgetMetaData function (line# 192)
(e) save & close samplecontainer.js
(f) cd examples
(g) Open all gadget xmls and, update URL of images (for ex:- bubble.gif, nophoto.if etc.)
(h) Also, make the changes mentioned in steps b-g for samplecontainer provided under c:/temp/shindig/WEB-INF/classes/gadgets/files/samplecontainer

12) Zip the contents of shindig folder and rename the zip file as shindig.war or any other name based on your preference.

13) This war file would now work on Tomcat or any other app server in a non-root context

Friday, August 22, 2008

Google Gadget XSD doesn't pass XSD validation

If you are having trouble getting your development tools to work smoothly with gadget XSD to do tasks such as validating & parsing gadget xmls, then look no further. The problem lies in gadget XSD itself. The latest and greatest version 0.8 of the gadget is not a valid XSD.

Proof: Run it through W3C XSD validator and you will find close to 12 errors.

Here is the fixed XSD. Let me know if this works for you.

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Module">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="ModulePrefs" minOccurs="0">
          <xs:complexType>
            <xs:choice minOccurs="0" maxOccurs="unbounded">
              <xs:element name="Require" type="GadgetFeatureType"/>
              <xs:element name="Optional" type="GadgetFeatureType"/>
              <xs:element name="Preload">
                <xs:complexType>
                  <xs:attribute name="href" type="xs:string" use="required"/>
                  <xs:attribute name="authz" default="none">
                     <xs:simpleType>
                      <xs:restriction base="xs:string">
                        <xs:enumeration value="none"/>
                        <xs:enumeration value="signed"/>
                        <xs:enumeration value="oauth"/>
                      </xs:restriction>
                    </xs:simpleType>
                  </xs:attribute>
                  <xs:attribute name="sign_owner" type="xs:boolean" default="true"/>
                  <xs:attribute name="sign_viewer" type="xs:boolean" default="true"/>
                  <xs:attribute name="views" type="xs:string" use="optional"/>
                </xs:complexType>
              </xs:element>
              <xs:element name="Icon">
                <xs:complexType>
                  <xs:simpleContent>
                    <xs:extension base="xs:string">
                      <xs:attribute name="mode">
                        <xs:simpleType>
                          <xs:restriction base="xs:string">
                            <xs:enumeration value="base64"/>
                          </xs:restriction>
                        </xs:simpleType>
                      </xs:attribute>
                      <xs:attribute name="type" type="xs:string"/>
                    </xs:extension>
                  </xs:simpleContent>
                </xs:complexType>
              </xs:element>
              <xs:element name="Locale">
                <xs:complexType>
          <xs:sequence>
            <xs:element name="msg" minOccurs="0" maxOccurs="unbounded">
            <xs:complexType>
              <xs:simpleContent>
              <xs:extension base="xs:string">
                <xs:attribute name="name" type="xs:string" use="required"/>
                <xs:attribute name="desc" type="xs:string" use="optional"/>
              </xs:extension>
              </xs:simpleContent>
            </xs:complexType>
            </xs:element>
          </xs:sequence>
                  <xs:attribute name="lang" type="xs:string" use="optional"/>
                  <xs:attribute name="country" type="xs:string" use="optional"/>
                  <xs:attribute name="messages" type="xs:string" use="optional"/>
                  <xs:attribute name="language_direction">
                    <xs:simpleType>
                      <xs:restriction base="xs:string">
                        <xs:enumeration value="ltr"/>
                        <xs:enumeration value="rtl"/>
                      </xs:restriction>
                    </xs:simpleType>
                  </xs:attribute>
                </xs:complexType>
              </xs:element>
              <xs:element name="Link">
                <xs:complexType>
                  <xs:attribute name="href" type="xs:string" use="required"/>
                  <xs:attribute name="rel" use="required">
                    <xs:simpleType>
                      <xs:restriction base="xs:string">
                        <xs:enumeration value="gadgets.help"/>
                        <xs:enumeration value="gadgets.support"/>
                        <xs:enumeration value="icon"/>
                      </xs:restriction>
                    </xs:simpleType>
                  </xs:attribute>
                </xs:complexType>
              </xs:element>
              <xs:element name="OAuth" minOccurs="0" maxOccurs="1">
                <xs:complexType>
          <xs:sequence>
            <xs:element name="Service" maxOccurs="unbounded">
            <xs:complexType>
            <xs:sequence>
              <xs:element name="Request" type="OAuthResourceType" minOccurs="0"/>
              <xs:element name="Access" type="OAuthResourceType" minOccurs="0"/>
              <xs:element name="Authorization" minOccurs="0">
              <xs:complexType>
                <xs:attribute name="url" type="xs:string" use="required"/>

              </xs:complexType>
              </xs:element>
              </xs:sequence>
            </xs:complexType>
            </xs:element>
            </xs:sequence>
                  <xs:attribute name="name" type="xs:string" use="optional"/>
                </xs:complexType>
              </xs:element>
            </xs:choice>
            <xs:attribute name="title" type="xs:string" use="optional"/>
            <xs:attribute name="title_url" type="xs:string" use="optional"/>
            <xs:attribute name="description" type="xs:string" use="optional"/>
            <xs:attribute name="author" type="xs:string" use="optional"/>
            <xs:attribute name="author_email" type="xs:string" use="optional"/>
            <xs:attribute name="screenshot" type="xs:string" use="optional"/>
            <xs:attribute name="thumbnail" type="xs:string" use="optional"/>
            <xs:attribute name="directory_title" type="xs:string" use="optional"/>
            <xs:attribute name="author_affiliation" type="xs:string" use="optional"/>
            <xs:attribute name="author_location" type="xs:string" use="optional"/>
            <xs:attribute name="author_photo" type="xs:string" use="optional"/>
            <xs:attribute name="author_aboutme" type="xs:string" use="optional"/>
            <xs:attribute name="author_quote" type="xs:string" use="optional"/>
            <xs:attribute name="author_link" type="xs:string" use="optional"/>
            <xs:attribute name="show_stats" type="xs:boolean" use="optional"/>
            <xs:attribute name="show_in_directory" type="xs:boolean" use="optional"/>
            <xs:attribute name="string" type="xs:string" use="optional"/>
            <xs:attribute name="width" type="xs:int" use="optional"/>
            <xs:attribute name="height" type="xs:int" use="optional"/>
            <xs:attribute name="category" type="xs:string" use="optional"/>
            <xs:attribute name="category2" type="xs:string" use="optional"/>
            <xs:attribute name="singleton" type="xs:boolean" use="optional"/>
            <xs:attribute name="render_inline" type="xs:string" use="optional"/>
            <xs:attribute name="scaling" type="xs:boolean" use="optional"/>
            <xs:attribute name="scrolling" type="xs:boolean" use="optional"/>
          </xs:complexType>
        </xs:element>
        <xs:element name="UserPref" minOccurs="0" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="EnumValue" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:attribute name="value" type="xs:string" use="required"/>
                  <xs:attribute name="display_value" type="xs:string" use="optional"/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string" use="required"/>
            <xs:attribute name="display_name" type="xs:string" use="optional"/>
            <xs:attribute name="default_value" type="xs:string" use="optional"/>
            <xs:attribute name="required" type="xs:string" use="optional"/>
            <xs:attribute name="datatype" default="string">
              <xs:simpleType>
                <xs:restriction base="xs:string">
                  <xs:enumeration value="string"/>
                  <xs:enumeration value="hidden"/>
                  <xs:enumeration value="bool"/>
                  <xs:enumeration value="enum"/>
                  <xs:enumeration value="list"/>
                  <xs:enumeration value="number"/>
                  <xs:enumeration value="location"/>
                </xs:restriction>
              </xs:simpleType>
            </xs:attribute>
            <xs:attribute name="urlparam" type="xs:string" use="optional"/>
            <xs:attribute name="autocomplete_url" type="xs:string" use="optional"/>
            <xs:attribute name="num_minval" type="xs:double" use="optional"/>
            <xs:attribute name="num_maxval" type="xs:double" use="optional"/>
            <xs:attribute name="str_maxlen" type="xs:int" use="optional"/>
            <xs:attribute name="restrict_to_completions" type="xs:boolean" use="optional"/>
            <xs:attribute name="prefix_match" type="xs:boolean" use="optional"/>
            <xs:attribute name="publish" type="xs:boolean" use="optional"/>
            <xs:attribute name="listen" type="xs:boolean" use="optional"/>
            <xs:attribute name="on_change" type="xs:string" use="optional"/>
            <xs:attribute name="group" type="xs:string" use="optional"/>
          </xs:complexType>
        </xs:element>
        <xs:element name="Content" minOccurs="1" maxOccurs="unbounded">
          <xs:complexType>
            <xs:simpleContent>
              <xs:extension base="xs:string">
                <xs:attribute name="type" use="optional" default="html">
                  <xs:simpleType>
                    <xs:restriction base="xs:string">
                      <xs:enumeration value="html"/>
                      <xs:enumeration value="url"/>
                      <xs:enumeration value="html-inline"/>
                    </xs:restriction>
                  </xs:simpleType>
                </xs:attribute>
                <xs:attribute name="href" type="xs:string" use="optional"/>
                <xs:attribute name="view" type="xs:string" use="optional"/>
                <xs:attribute name="preferred_height" type="xs:integer" use="optional"/>
                <xs:attribute name="preferred_width" type="xs:integer" use="optional"/>
              </xs:extension>
            </xs:simpleContent>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="GadgetFeatureType">
    <xs:sequence>
      <xs:element name="Param" minOccurs="0" maxOccurs="unbounded">
        <xs:complexType>
          <xs:simpleContent>
            <xs:extension base="xs:string">
              <xs:attribute name="name" type="xs:string" use="required"/>
            </xs:extension>
          </xs:simpleContent>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="feature" type="xs:string" use="required"/>
  </xs:complexType>
  <xs:complexType name="OAuthResourceType">
    <xs:attribute name="url" type="xs:string" use="required"/>
    <xs:attribute name="method" use="optional" default="GET">
  <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:enumeration value="GET"/>
        <xs:enumeration value="POST"/>
      </xs:restriction>
    </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="param_location" use="optional" default="header">
  <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:enumeration value="header"/>
        <xs:enumeration value="url"/>
        <xs:enumeration value="body"/>
      </xs:restriction>
    </xs:simpleType>
    </xs:attribute>
  </xs:complexType>
</xs:schema>

Wednesday, August 6, 2008

Enable gzip compression on Apache web server in 3 easy steps

Make the following changes in apache/conf/httpd.conf :-

1) Uncomment "LoadModule deflate_module modules/mod_deflate.so"

2) Add "SetOutputFilter DEFLATE" inside <<IfModule mime_module> section

3) Add "AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript text/css" right after SetOutputFilter... line.

Restart Apache. You should see a significant improvement in the turnaround time for your web applications. This setting works for all responses tunneled trough Apache for ex:- for java web applications deployed on a Tomcat integrated with apache web server etc.

Tuesday, June 24, 2008

Integrate php with MySQL

Open {PHP_DIR}/php.ini
1) Check if the extension directory is set correctly. is is set incorrectly in the php.ini file that ships with the installer. In that , Replace extension_dir="./" with extension_dir="./ext/"
(2) Uncomment extension=php_mysql.dll
(3) You should now be able to access your mysql database.