Wednesday, May 11, 2011

E2.0 A-Team Blog

For the latest news and great technical tips on Oracle's Fusion Middleware E20, I recommend you follow the E2.0 A-Team blog.

And, if you're looking for WebCenter PS3 installation, checkout these links:
  • Installing WebCenter 11g PS3 from scratch
  • How to install Oracle WebCenter 11.1.1.4 (11gR1 PS3)
  • Wednesday, November 3, 2010

    Quick Update

    I apologize the blog has no new updates for a while, and that's because I'm now working on the new release of Oracle WebCenter Spaces 11g iPhone Application.
    The blog will remain open and will be answering any questions you might have regarding E20 & WebCenter installs.
    Thanks

    Monday, August 9, 2010

    WebCenter REST + Ruby = Awesome!

    These days, I've been studying the WebCenter REST APIs and was happy to see the power of these APIs. The team invested a lot of time building these APIs, specially the Link model (HATEOAS) which is the heart of it.

    HATEOAS is a heavy concept that needs to be understood, so you can fully take advantage of REST APIs. It is out of scope of this article, but I found a good reference from Ryan Kinderman with lots of links to other resources. BTW, I thought I knew REST until I read Ryan's post :)

    Another cool thing I've been reading/studying these days is Ruby! I have to say it is awesome! The amount of things you can do with such little coding is impressive. Coming from Java & Objective C, the first time you look at Ruby code you don't quite get the syntax. But, after reading just a little about it, you see how you can do things pretty easy and with less coding than other languages.

    Ok - time to mix things together now :)

    So, WebCenter REST API is great! And, Ruby is perfect for running quick tests and get the ball rolling. Assuming you're a bit familiar with REST and Ruby, let's write a simple client to get the Person's activity stream from WebCenter Server. I also assume you have access to a WebCenter instance -- don't have one??? It is really easy to install :-)

    On this sample, I'm using the following:
    • Ruby 1.9.2 -- actually used RVM to setup my Ubuntu machine
    • Mechanize 1.0.0 -- install with gem install mechanize - this helps with the HTTP requests, maintain session, cookies, etc.
    • JSON 1.4.6 -- install with gem install json

    Connection

    The first thing is to setup a wrapper for the connection. This class will handle the communication with the WebCenter REST server, perform authentication, and send the GET requests. In future, it should also wrap the other types of requests - POST, PUT and DELETE.
    require 'rubygems'
    require 'pp'
    require 'mechanize'
    
    USER_AGENT_ALIAS = 'Linux Mozilla' #provided by mechanize
    
    class WcConn
      attr_accessor :wc_user, :wc_pass
    
      # Create the object
      def initialize(wc_user, wc_pass)
        @wc_user = wc_user
        @wc_pass = wc_pass
    
        #init mechanize fmk
        @mechanize_agent = Mechanize.new do |agent|
          agent.user_agent_alias = USER_AGENT_ALIAS
          agent.request_headers = {'accept' => 'application/json'}
        end
      end
    
      ##Login -- and retrieve resourceIndex
      def wc_login(uri)
        t1 = Time.now
        puts "--wc_login [#{uri}]"
    
        @mechanize_agent.auth(@wc_user,@wc_pass)
        logged_in_page = @mechanize_agent.get(uri)
    
        puts "--wc_login took [#{Time.now-t1}]s"
        #return the page, so processing can continue
        logged_in_page
      end
    
      ## access a page
      def goto_page(uri)
        t1 = Time.now
        puts "--goto_page [#{uri}]"
      
        page = @mechanize_agent.get(uri)
    
        puts "--goto_page took [#{Time.now-t1}]s"
        #return the page
        page
      end #goto_page
    
    end #class
    

    Wrappers for Objects

    I also created wrappers for Link, ResourceIndex, Person, and Activities. These will read the JSON object to retrieve the value of few attributes. These objects are far from being complete, but they work fine for this simple example.
    =begin
      object representation for Link & ResourceIndex
    =end
      
    #resource types -- not complete list 
    RT_MSG_BOARD = 'urn:oracle:webcenter:messageBoard'.to_sym
    RT_CMIS = 'urn:oracle:webcenter:cmis'.to_sym
    RT_FORUMS = 'urn:oracle:webcenter:discussions:forums'.to_sym
    RT_RC_IDX = 'urn:oracle:webcenter:resourceindex'.to_sym
    RT_ACTIVITIES = 'urn:oracle:webcenter:activities:stream'.to_sym
    RT_PERSON_ACTIVITIES =  'urn:oracle:webcenter:activities:stream:person'.to_sym
    RT_FEEDBACK = 'urn:oracle:webcenter:feedback'.to_sym
    RT_SPACES = 'urn:oracle:webcenter:spaces'.to_sym
    RT_PEOPLE = 'urn:oracle:webcenter:people'.to_sym
    
    
    class Link
      attr_accessor :resourceType, :href, :template, :capabilities, :rel, :type
      
      def initialize(options)
        @resourceType = options['resourceType'] ? options['resourceType'].to_sym : :EMPTY
        @href = options['href']
        @template = options['template']
        @capabilities = options['capabilities'] ? options['capabilities'].to_sym : :EMPTY
        @rel = options['rel'] ? options['rel'].to_sym : :EMPTY
        @type = options['type'] ? options['type'].to_sym : :EMPTY
      end
      
      # to String ---
      def inspect
        "LINK rt[#{@resourceType}] hr[#{@href}] tm[#{@template}] cp[#{@capabilities}] rel[#{@rel}] tp[#{@type}]"
      end
      
      def to_s
        inspect
      end
    end #class_Link
    
    
    class ResourceIndex
      #construct
      def initialize
        @links = []
      end
      
      def add_link(options)
        @links << Link.new(options)
      end
      
      def get_link_by_rel(rel)
        @links.find { |l| l.rel == rel }
      end
    
      def get_link_by_resource_type(resource_type)
        @links.find { |l| l.resourceType == resource_type }
      end
    
      # to String ---
      def inspect
        "RESOURCE_INDEX #{@links}"
      end
      
      def to_s
        inspect
      end
    end #class_resourceIndex
    
    =begin
      object representation for Person
    =end
    
    class Person
      attr_accessor :guid, :id, :display_name, :links   
      
      def initialize(options)
        @guid = options['guid']
        @id = options['id']
        @display_name = options['displayName']
        @links = []
        options["links"].each { |l| @links << Link.new(l) }
      end
    
      # to String ---
      def inspect
        "PERSON guid[#{@guid}] id[#{@id}] name[#{@display_name}]" # links[#{@links}]"
      end
      
      def to_s
        inspect
      end
    end
    
    =begin
      object representation for Activities 
    =end
    
    class Activities
      attr_accessor :messages   
      
      def initialize(array_items)
        @messages = []
        array_items.each do |i|
          msg_template = i['message']
    
          tp_items = i['templateParams']['items'] #arrays of items
    
          msg_items = {}
          tp_items.each do |tpi|
            tp_item_k = tpi['key']
            tp_item_v = tpi['displayName']
            msg_items[tp_item_k] = tp_item_v
          end
          d = DateTime.parse(i['createdDate'])
          #run the substitutions
          msg_items.each { |k,v| msg_template.sub!(/#{Regexp.escape(k)}/,"'#{v}'") }
          m = "#{d}, #{msg_template}"
          #puts m
          @messages << m
        end
      end
      
      # to String ---
      def inspect
        "Activities messages[#{@messages}]"
      end
      
      def to_s
        inspect
      end
    end
    

    Main class

    The main class -- WcRest -- is responsible for the actual retrieval of the activities. It will login to WebCenter REST server using Basic authentication, retrieve the Resource Index. After parsing it, it will retrieve the Person object, and we need the guid. It will then follow the template for urn:oracle:webcenter:activities:stream to retrieve the 10 latest activities for that user, and display it as output.

    Notes:
    • as this is main file, you should make sure if can be executed, or run it as ruby wc_rest.rb
    • the response from WcConn#wc_login and WcConn#goto_page is the Mechanize#Page, and thus we need to JSON parse the body of the page
    • #follow_template method will first substitute variables on the link using the options Map
    • #run method actually contains the sequence of the REST commands
    #!/usr/bin/env ruby
    
    require 'rubygems'
    require 'json/ext'
    load 'conn.rb'
    load 'resource_index.rb'
    load 'person.rb'
    load 'activities.rb'
    
    class WcRest
      # Create the object
      def initialize(wc_user, wc_pass, wc_uri)
        @resIdx = ResourceIndex.new 
        #initialize connection
        @wc_conn = WcConn.new(wc_user, wc_pass)
        @wc_uri = wc_uri
      end #initialize 
      
      ## gets /rest/api/resourceIndex
      def get_resourceIndex
        wc_resourceIdx =  @wc_conn.wc_login @wc_uri
        doc = JSON.parse(wc_resourceIdx.body)
        #  
        doc["links"].each do |element|
          @resIdx.add_link(element)  #new Link object from JSON
        end
        
        #pp @resIdx #uncomment to see ResourceIndex object
      end #get_resourceIndex
      
      ## 
      def follow_link(link)
        puts "FOLLOWING LINK... [#{link}]"
        return unless link.href 
    
        wc_rest_page =  @wc_conn.goto_page link.href
        # to JSON
        JSON.parse(wc_rest_page.body)
      end #follow_link
      
      ## templates have values that must be substituted prior to GET
      def follow_template(link, options)
        puts "FOLLOWING TEMPLATE... [#{link}]"
        return unless link.template 
    
        #prepare template
        template = link.template
        options.each { |k,v| template.sub!(/#{k}/,"#{v}") }
    
        wc_rest_page =  @wc_conn.goto_page template
        # to JSON
        JSON.parse(wc_rest_page.body)
      end #follow_template
     
      ## 
      def run
        #resourceindex
        puts "\n================================================================"
        puts "== #{RT_RC_IDX} =="
        puts "================================================================\n"
        get_resourceIndex
        
        #get person
        puts "\n================================================================"
        puts "== #{RT_PEOPLE} =="
        puts "================================================================\n"
        person = follow_link @resIdx.get_link_by_resource_type RT_PEOPLE
        p = Person.new person
        pp p
        
        #get ACTIVITIES
        puts "\n================================================================"
        puts "== #{RT_ACTIVITIES} =="
        puts "================================================================\n"
        template_values = {'{startIndex}'=>'0','{serviceIds}'=>'','{personal}'=>'true',
                           '{connections}'=>'true','{personGuid}'=>p.guid,
                           '{itemsPerPage}'=>'10','{groupSpaces}'=>'true',}
        activities = follow_template(@resIdx.get_link_by_rel(RT_ACTIVITIES), template_values)
        a = Activities.new activities['items']
        #pp a
    
        puts "\n================================================================"
        puts "== Activities Stream == "
        puts "================================================================\n"
        puts a.messages.join("\n")
    
        puts "\n--END--\n\n"
      end #run
    end
    
    # ===================================
    # MAIN Execution
    # ===================================
    if __FILE__ == $0
      # 
      wcrest = nil
    
      if (ARGV.length == 3) #connect to given URL
        puts "WCR> connecting to #{ARGV[2]} with BASIC-AUTH..."
        wcrest = WcRest.new(ARGV[0],ARGV[1],ARGV[2])
      else 
          puts <<END_HELP
    = Synopsis 
      Simple WebCenter Spaces REST client that displays user's activity stream
    
    = Usage 
       Connect to given server, and uses BASIC-AUTH
       wcr.rb USER PASS SPACES_SERVER_URI
       
    END_HELP
      end
    
      #run
      wcrest.run if wcrest
    end
    

    Sample Run

    Below is an example of running the above against my test server, to display the activities of user weblogic
    oracle@rolima-home:~/Documents/work/ruby/wcr-post$ ./wc_rest.rb weblogic welcome1 http://localhost:8888/rest/api/resourceIndex
    WCR> connecting to http://localhost:8888/rest/api/resourceIndex with BASIC-AUTH...
    
    ================================================================
    == urn:oracle:webcenter:resourceindex ==
    ================================================================
    --wc_login [http://localhost:8888/rest/api/resourceIndex]
    --wc_login took [0.212828757]s
    
    ================================================================
    == urn:oracle:webcenter:people ==
    ================================================================
    FOLLOWING LINK... [LINK rt[urn:oracle:webcenter:people] hr[http://localhost:8888/rest/api/people/@me/@self?stoken=FHMh49RIbgzsSGAO5IMed5xWr9ah4Oo*] tm[http://localhost:8888/rest/api/people/@me/@self?startIndex={startIndex}&projection={projection}&itemsPerPage={itemsPerPage}&stoken=FHMh49RIbgzsSGAO5IMed5xWr9ah4Oo*] cp[urn:oracle:webcenter:read] rel[EMPTY] tp[EMPTY]]
    --goto_page [http://localhost:8888/rest/api/people/@me/@self?stoken=FHMh49RIbgzsSGAO5IMed5xWr9ah4Oo*]
    --goto_page took [0.016172011]s
    PERSON guid[599A52A05D3511DF8F701DCDD2E623D6] id[weblogic] name[weblogic]
    
    ================================================================
    == urn:oracle:webcenter:activities:stream ==
    ================================================================
    FOLLOWING TEMPLATE... [LINK rt[urn:oracle:webcenter:activities:stream] hr[] tm[http://localhost:8888/rest/api/activities?startIndex={startIndex}&serviceIds={serviceIds}&personal={personal}&connections={connections}&personGuid={personGuid}&itemsPerPage={itemsPerPage}&groupSpaces={groupSpaces}&stoken=FHMh49RIbgzsSGAO5IMed5xWr9ah4Oo*] cp[urn:oracle:webcenter:read] rel[urn:oracle:webcenter:activities:stream] tp[EMPTY]]
    --goto_page [http://localhost:8888/rest/api/activities?astartIndex=0&serviceIds=&personal=true&connections=true&personGuid=599A52A05D3511DF8F701DCDD2E623D6&itemsPerPage=10&groupSpaces=true&stoken=FHMh49RIbgzsSGAO5IMed5xWr9ah4Oo*]
    --goto_page took [0.05946188]s
    
    ================================================================
    == Activities Stream == 
    ================================================================
    2010-07-28T15:24:52-04:00, 'weblogic' created the page 'public docs'
    2010-07-28T15:21:59-04:00, 'weblogic' created the document 'test.htm'
    2010-07-22T17:52:18-04:00, 'weblogic' 'wasssuuuuuuuuuup!'
    
    --END--
    
    oracle@rolima-home:~/Documents/work/ruby/wcr-post$ 
    

    Let me know if you have any questions, and have fun with REST & Ruby!

    Thursday, July 22, 2010

    WebCenter Spaces for iPhone

    Just to let you know the WebCenter Spaces application for iPhone is now available at Apple's App Store.
    Here is the link for you -- http://itunes.apple.com/us/app/oracle-webcenter-spaces-11g/id382334215?mt=8

    Wednesday, June 23, 2010

    WebCenter WIKI Macro - Table of Contents

    WebCenter WIKI Macro - Table of Contents I was asked to help extending Oracle WebCenter's Wiki server, to add a macro to auto-generate a Table of Contents for a wiki page. It follows the same layout as Wikipedia's table of contents, and also has the ability to hide/show itself.

    Description:
    The <macro:Toc> will generate a simple Table of Contents, in the forms of Wikipedia's Table of Contents. It will search the wiki page content for all Header tags (!, !!, !!!, !!!!) up to 4h level.
    Anchors to the title are automatically generated based text for this title
    Example: !Description generates anchor <a name="Description"></a><h1>Description</h1>

    Syntax:
    #1) simple -- will create TOC with title = "Contents" and no auto numbering:
    <macro:Toc>
    </macro>
    

    #2) with numbering -- numbers are automatically added to the TOC
    <macro:Toc numbering="true">
    </macro>
    

    #3) with title & numbering -- the title will be set to the text within the macro tags
    <macro:Toc numbering="true">
    My Table of Contents
    </macro>
    

    If you want to give it a try, get the following files and place them on the location below:
    Note: It was tested with WebCenter 11g 11.1.1.3.0
    -- $WLS_HOME/user_projects/applications//owc_wiki/tags/toc.vm
    -- $WLS_HOME/user_projects/applications//owc_wiki/WEB-INF/classes/org/jzwiki/macros/TocMacro.class
    -- $WLS_HOME/user_projects/applications//owc_wiki/WEB-INF/lib/yawiki-engine-2.1.jar - file changed is org.jzonic.yawiki.converter.HeaderConverter.class

    Here is a screenshot of the macro in action :


    Monday, June 7, 2010

    WLP Portlet Subscribe/Unsubscribe

    Last week, I had to simulate a "subscribe/unsubscribe" mechanism for WebLogic Portal (WLP) 10.3. The basic idea is that the user has an option to subscribe to a portlet, from a list of available portlets, and that is displayed in his main page.

    After some research, I found some good reference from Balz Schreier on the WLP Portlet Preferences APIs. In his example, Balz shows how to get access to PortalCustomizationManager which allows us to perform various operations, including removing - .removePlaceable - and adding - .addPlaceable - portlets to a Page.

    Using similar code, we could also get access to PortletDefinitionManager , which will give us access to all portlets for the Web Application - .getPublicPortletDefinitions.

    In my example, the idea is to "replace" the existing portlet subscription with a new one. To simplify things in my example, I have 2 portlets - weather & stocks - and user can either view one or the other. So, when it is time to change the subscription, I just look for the one that is currently showing - _oldPlaceableView -, so I can get its PlaceholderDefinitionId & PlaceHolderPosition. This is needed, as the new portlet - _newPortletDef - will show up in the same location.

    Now, we have all the APIs to:
    • getPortalCustomizationManager -- return instance of PortalCustomizationManager.
    • getPortletDefinitionManager -- return instance of PortletDefinitionManager.
    • getAllPortlets -- returns a list of all available portlets for the Web Application.
    • findPortlet -- finds the PortletDefinition for the new portlet to be subscribed to.
    • findPlaceableView -- finds the PlaceableView instance of the current portlet.
    • deleteCustomizations -- helper to clean up all the customizations for this user, and reset the original state of the page. Useful for the testing, where things start getting messy after some time, and you want to clean it all up. I received this code from an Oracle peer.
    • subscribePortlet -- wraps all the calls into a single method, to make it easier for the test JSP page.
    It is also important to say that the user must be authenticated for this to work the way we wanted. In my test.jsp, I have a reference to an authenticate  method, that simply calls  com.bea.p13n.security.Authentication.login.

    Putting it all together, here is WlpHelper.java and test.jsp. The idea is to start with a simple JSP just to validate the flow, but that later could turn into a "Preferences Page" of some sort, where user views all the available portlets and can choose which one to subscribe to.

    Here is the original page, showing the Stocks portlet:


    and this is after executing the test.jsp with the following parameters:
    http://localhost:7001/tutorial_portalWeb/test.jsp?username=rolima&password=welcome1&action=subWeather


    The example is really simple, but the starting APIs are there for you build from it.
    Have fun!

    Wednesday, May 12, 2010

    Gwibber on Ubuntu 10.04 LTS

    While playing with the new Ubuntu 10.04 LTS, I saw the Gwibber client for Twitter, Facebook, Digg, Flickr and others. I know this has not much to do with WebCenter, Enterprise 2.0, Fusion, or even Oracle, but just would like to share in case you read my earlier post and decided to use Ubuntu 10.04.

    I tried to make it work, so I could just see the tweets popping up on the desktop via notification-applet, but the first tries were frustrating...
    I did manage to add my twitter account, but no updates would come. Facebook was even worst, as I could not even add the account.

    After spending some time searching around, I found this bug #530195, where the comment #22 suggested that you increased the timeout in network.py.

    I opened up  /usr/lib/python2.6/dist-packages/gwibber/microblog/network.py and noticed the original connection timeout was set to 8secs.

    Not sure what internet provider people are using, and I actually thought I had a good speed (around 20Mbps), but that was still not enough. I'm also not sure about the DNS tricks (openDNS + google DNS = magic DNS :), so I just went the lazy way -- multiply by 10 :)

    #/usr/lib/python2.6/dist-packages/gwibber/microblog/network.py
    
        self.curl.setopt(pycurl.TIMEOUT, 150)
        self.curl.setopt(pycurl.CONNECTTIMEOUT, 80)
    

    That solved the Twitter problem, but I was still not able to add my account to Facebook.
    After some more research, I found another bug #552227, with many suggestions on how to overcome that. I tried all the suggestions on that page but still no luck.

    Ok, let's try the "twitter-hack" then :)... Found that there connection timeout was actually being set on another file -- /usr/lib/python2.6/dist-packages/gwibber/microblog/util/facelib.py

    I again applied the "lazy 10x higher" fix:
    #/usr/lib/python2.6/dist-packages/gwibber/microblog/util/facelib.py
    
        c.setopt(pycurl.TIMEOUT, 150)
        c.setopt(pycurl.CONNECTTIMEOUT, 80)
    

    Yay! Finally able to see both Twitter and Facebook accounts on Gwibber! And the cool desktop icon notifications keep popping up now,  a quick not-that-intrusive way to keep updated on things. I really like it better than having either  an extra browser window, or another application running.