Friday, September 2, 2011

I've moved

It's been a while since I've been writing at narmnevis.com blog.

Friday, July 2, 2010

Custom JSON serialization with Spring MVC

With the rise of Spring RESTful support and the existence of JSON libraries in Java, there could come up the problem of JSON serialization of custom domain models to views. In this article, through the example, I want to share my experience on how to serialize some custom model with JSON through Spring MVC configuration.

Spring 3.0 introduces the notion of ContentNegotiatingViewResolver that is a way to configure Spring to respond to certain media types such application/xml or application/json while letting others do their routine job. More details on this could be found at here and here

In this sample, I am going to serialize the instances of the model Customer to be used in an AJAX-based combo box. There are some steps to go through:

  1. Write the custom JSON serializer
  2. Define and configure the JSON serializer factories in configuration files
  3. Configure the content negotiating view resolver to take care of the JSON requests
Step One

The JSON library that I using in this project is Jackson JSON. Simply, we write a custom serializer for Customer model:
public class CustomerSerializer extends JsonSerializer {

 protected final Log logger = LogFactory.getLog(getClass());

 @Override
 public void serialize(Customer value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
   JsonProcessingException {
  jgen.writeStartObject();
  jgen.writeFieldName("id");
  jgen.writeNumber(value.getId());
  jgen.writeFieldName("name");
  jgen.writeString(value.toString());
  jgen.writeEndObject();
  logger.debug("Customer [" + value + "] mapped to JSON");
 }

}


Step Two

Now, I need some configurable factory bean in Spring that would hold my custom domain models for JSON serialization and also it is an instance of CustomSerializerFactory. Simply, I extend it:
public class CustomSerializerFactoryRegistry extends CustomSerializerFactory implements InitializingBean {

 protected final Log logger = LogFactory.getLog(getClass());

 private Map<Class, JsonSerializer> serializers = new HashMap<Class, JsonSerializer>();

 @Override
 public <T> JsonSerializer<T> createSerializer(Class<T> type, SerializationConfig config) {
  JsonSerializer<T> serializer = super.createSerializer(type, config);
  logger.debug("Found serializer [" + serializer + "] for type [" + type + "].");
  return serializer;
 }

 @Override
 public void afterPropertiesSet() throws Exception {
  for (Map.Entry<Class, JsonSerializer> e : serializers.entrySet()) {
   addGenericMapping(e.getKey(), e.getValue());
  }
  logger.info("Registered all serializers: " + serializers);
 }

 public void setSerializers(Map<Class, JsonSerializer> serializers) {
  this.serializers = serializers;
 }

}

With my CustomSerializerFactoryRegistry, I can easily register all the custom JSON serializers that I define for my application.

However, there is a slight tweak in configuration. To configure JSON in Spring configurations, normally, we define a bean of type ObjectMapper. By default this object mapper bean, initializes some serializer factory, however, we need to inject our own serializer factory instance. The problem is that the Object Mapper defines the setter method that does not have a void return type (required by Spring IoC), so, I define a CustomObjectMapper:
public class CustomObjectMapper extends ObjectMapper {

 protected final Log logger = LogFactory.getLog(getClass());

 public void setCustomSerializerFactory(SerializerFactory factory) {
  setSerializerFactory(factory);
  logger.debug("Using [" + factory + "] as the custom Jackson JSON serializer factory.");
 }

}


Now, glueing step one and two, I can write some configuration for the custom JSON serialization beans:
 <bean id="jacksonJsonObjectMapper" class="nl.hajari.wha.service.json.CustomObjectMapper">
  <property name="customSerializerFactory" ref="jacksonJsonCustomSerializerFactory" />
 </bean>

 <bean id="jacksonJsonCustomSerializerFactory"
  class="nl.hajari.wha.service.json.CustomSerializerFactoryRegistry">
  <property name="serializers">
   <map>
    <entry key="nl.hajari.wha.domain.Customer" value-ref="customerSerializer" />
   </map>
  </property>
 </bean>

 <bean id="customerSerializer" class="nl.hajari.wha.service.json.CustomerSerializer" />


Step Three

The last step is to configure the content negotiating view resolver. The most important point on this view resolver is that it should be highest order of processing in Spring MVC chain since it automatically delegates to other view resolvers when the media type does not apply, so:
 <bean
  class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
  <property name="order" value="1" />
  <property name="mediaTypes">
   <map>
    <entry key="json" value="application/vnd.com.mobilivity+json" />
   </map>
  </property>
  <property name="defaultViews">
   <list>
    <bean
     class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
     <property name="contentType" value="application/vnd.com.mobilivity+json" />
     <property name="objectMapper" ref="jacksonJsonObjectMapper" />
     <property name="renderedAttributes">
      <set>
       <value>items</value>
      </set>
     </property>

    </bean>
   </list>
  </property>
 </bean>



Now, I can serialize my custom domain model such as Customer through Spring MVC REST requests:
@RequestMapping(value = "/customer/list/all", method = RequestMethod.GET)
that can be used for instance in Dojo widgets for a combo box in a view page.



Monday, June 21, 2010

Django Jalali Calendar Widget

Using the original JSCal and the Jalali version for it, I managed to create a simple Django widget that displays a Jalali calendar in view layer but actually uses the Gregorian server side:

calbtn = u'''calendar
'''

class DateTimeWidget(forms.widgets.TextInput):
    class Media:
        css = {
            'all': ('/static/styles/jscalendar/skins/calendar-system.css',)
        }
        js = (
              '/static/js/jscalendar/jalali.js',
              '/static/js/jscalendar/calendar.js',
              '/static/js/jscalendar/calendar-setup.js',
              '/static/js/jscalendar/lang/calendar-fa.js',
        )

    dformat = '%Y-%m-%d'
    def render(self, name, value, attrs=None):
        if value is None: value = ''
        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
        if value != '':
            try:
                final_attrs['value'] = \
                                   force_unicode(value.strftime(self.dformat))
            except:
                final_attrs['value'] = \
                                   force_unicode(value)
        if not final_attrs.has_key('id'):
            final_attrs['id'] = u'%s_id' % (name)
        id = final_attrs['id']

        jsdformat = self.dformat #.replace('%', '%%')
        cal = calbtn % (id, id, id, id, jsdformat)
        parsed_atts = forms.util.flatatt(final_attrs)
        a = u' %s%s' % (id, parsed_atts, self.media, cal)
        return mark_safe(a)

    def value_from_datadict(self, data, files, name):
        dtf = forms.fields.DEFAULT_DATETIME_INPUT_FORMATS
        empty_values = forms.fields.EMPTY_VALUES

        value = data.get(name, None)
        if value in empty_values:
            return None
        if isinstance(value, datetime.datetime):
            return value
        if isinstance(value, datetime.date):
            return datetime.datetime(value.year, value.month, value.day)
        for format in dtf:
            try:
                return datetime.datetime(*time.strptime(value, format)[:6])
            except ValueError:
                continue
        return None

    def _has_changed(self, initial, data):
        """
        Return True if data differs from initial.
        Copy of parent's method, but modify value with strftime function before final comparsion
        """
        if data is None:
            data_value = u''
        else:
            data_value = data

        if initial is None:
            initial_value = u''
        else:
            initial_value = initial

        try:
            if force_unicode(initial_value.strftime(self.dformat)) != force_unicode(data_value.strftime(self.dformat)):
                return True
        except:
            if force_unicode(initial_value) != force_unicode(data_value):
                return True
        return False

Also, if you need to do manipulations on the date object in server-side, you can use the utility written in GNU Jalali Calendar.


Tuesday, May 18, 2010

Eclipse 3.5+ Internal Browser/Flash on Ubuntu 10.04 Lucix Lynx

I am developing an eclipse plugin for the test of which I need the support of eclipse internal browser with Flash support enabled. As I upgraded to Ubuntu 10.04 Lucid Lynx, there seems to be a bug introduced in understanding the swt-mozilla-gtk-xxxx.so plugin for eclipse in connection with XUL Runner. It also complains about a MOZILA_FIVE_HOME environment variable.

My configuration is:
eclipse: 3.5.2
XUL Runner 1.9.1.9

There is also a complete discussion on the same topic here. But this would be useful if you're using eclipse as a bundle in Ubuntu. I am using an eclipse package downloaded from eclipse web site.

My workaround to this was run eclipse through the following script:
xuldir=/usr/lib/xulrunner-$(/usr/bin/xulrunner --gre-version)
LD_LIBRARY_PATH=$xuldir${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} exec /path/to/eclipse/eclipse "$@" 

Also, I added the following VM options to eclipse.ini after -vm segment:
-Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib/xulrunner-1.9.1.9

Before applying, make sure you have the same configuration.

Wednesday, March 17, 2010

Contextor 0.2 released

After a couple of months of redesign and coding, I released Contextor 0.2. It is still somehow beta but it works rather nice.

Wednesday, March 10, 2010

Software Experience - Trust

Trivially simple and obvious yet effective and fundamental. The primary goal in every software development process is to deliver a specified application in an acceptable qualitative operational state. Apart from all the technical issues, activities and concerns in every software project, there are many others usually referred to umbrella activities. The umbrella ones usually include project management and environmental activities both to lead and support the development process in all phases. Talking of project management, here emerges the simple practice of trust. I have experienced different aspects of trust recently as in:

The customer does not know exactly what he wants. They hold some informal meetings with the contractor to reach a conclusion yet not so promising. The contractor initiates some development moves letting the customer know of. Customer adaptively accepts some general impressions and issues an OK to start the work. This also may happen when the customer has some legacy system and desires to create and build a new one with more exciting features; still, the customer is unsure of what he wants. Alongside this mutual trust, comes the flexibility of the contractor. Usually, the contractor is abound to being firm and fixed on the rules and scope exactly mentioned in the contract not to lose time or extra costs on something that will not be paid for. When trust gets some more credit in communications between the customer and the contractor, the flexibility of the contractor also rises; it seems that trust can act as a catalyst to bond the tie between two sides. As a personal experience, trust makes me happier when I'm in either sides of this communication. The trust gives me the feeling that this work will sure end in a win-win situation. And, I admit that there are many projects in very very very larger scales that require some official bureaucracy before any start; still I firmly believe that still some form of trust should be established and grounded during any negotiations in initiative phases of any such project. More formally, considering the scalability and supplementary requirements of a software project, there can still be ways to inject the trust in different levels of communication between the customer and the contractor to boost the process and ultimate quality.

What about the money? On one side: no trust, no software. And, on the other: no trust, no money. Simply chemically, the mood seems to be conveyed to either side of the project. On one hand, when the contractor comes to the vision (even if false) that the customer is not willing and determined to pay off for the work done, he becomes more and more reluctant and carefree of the concerns in the project trying to just and just abide to the scope of the contract and not a single bit more. The same atmosphere gets provoked on the customer side observing that the contractor is not so willingly doing his job and duties according to the contract; leading to critical faultfinding mannerism. Though, when two sides have confident trust in one another, the financial matters on one side and software ones on the other tend to resolve more smoothly and automatically. There are situations in which, due to any reasons, the customer is not ready to do the finances; the contractor with the trust will sure continue the quality work as before with no hesitation. There are situations in which the contractor has financial needs or requirements in some period; why not and why not the customer shouldn't help him out if possible?! Sadly, this is not happening very often that could be a point of attention for managers in organizations and companies to cultivate the culture. The finance departments in many organizations usually play a crucial and ruling role that tends to be less flexible and adaptable; however, managers at the customer side should have authority to cope with situations in which their finances should be more flexible.

The Social Trust Culture. All the trust that we're talking about roots in the social and cultural foundations common and norm to a society. It is disappointing to admit that the trust in software project management will comply to the social common concept; if trust is not virtuous in the daily life in a society, it would be so hard and weary to create a base on which both sides could build their relationship upon mutual trust. In other words, we should have trust in daily communications, relationships, and interactions so that we can take this trust to some other level to the software project management and development process. If, for instance, lying is common in a society, it could be bitterly sadly factual that customers and contractors would not act upon trust in the other side. One other issue that should be considered is that if under any situation, cause, or justification, one side of the customer-contractor tie breaks the relationship trust or distrust the other side, the relationship is ill and, effectively annulled. When there is no pivot of trust in the software project management and process, each side will act upon his own priorities and concerns.

The views expressed in this post are all based on personal hands-on experiences in a couple of software projects all of which interestingly were and still are managed completely remote.

Related:







Sunday, March 7, 2010

Software Experience - Introduction

To start with, I am Iranian (Persian). I left home to do stuff in Computer Science. I have couple of years of experience in software development and project management in a small company in Tehran; ASTA. Currently, I'm in the Netherlands, and out of many reasons I had to start some part-time co-operations in software sector here. During the period, there's been a lot of contrasts and comparisons to my previous impressions, I've interestingly come across that I'd like to share (thanks to Seyyed Jamal for his great idea to share these). Still, before that, I'd like to mention a few things:

  • I am using English language rather than Persian (Farsi); I believe in true open source as in GPL; not the type that one says he supports and then ridiculously bans the resources to people around the world based on some witless rules and regulations. Also, using Persian may somehow lower the possibility of future collaborations. 
  • I also admit that the main target audience will be Persian specialists in software, however, we'll be happy to share ideas with peers from around the world.
  • The related posts are based on my personal background, experiences and viewpoints in software design, development, and project management; so, not necessarily based on scientific or proved theories in software.
  • No comparison or differences mentioned in the related posts are meant at all to disrespect or disregard different people's attitudes, skills, or profession. They are only based on personal observations for discussions and analysis.
  • Feedbacks in any language will be most welcome.


Related Posts:

Monday, February 22, 2010

Localized Date with Dojo and Spring

We have developed a simple application in which we need to have localized date objects with Dojo DateTextBox on Spring Roo. The problem is that Spring JS does not completely honor the original widget attributes of Dojo considering the date patterns that are specified. To specific, we need to have date patterns of MM/dd/yyyy on English locales and dd-MM-yyyy on Dutch locales.

Localized Date Patterns
We have two property files, namely, application.properties and application_nl.properties to hold the localized messages. Using the two, we added a key to the files holding the localized date pattern:

date.pattern=MM/dd/yyyy

and

date.pattern=dd-MM-yyyy

Configuring a localized CustomDateEditor
To achieve this, first, we need a controller which is aware of localized date patterns defined above; simply, we make the controller an instance of MessageSourceAware so that the applications's localized message source is injected.

public class AbstractController implements MessageSourceAware {

 protected final Log logger = LogFactory.getLog(getClass());

 @Resource
 @Qualifier("messageSource")
 protected MessageSource messages;

 @InitBinder
 public void initBinder(WebDataBinder binder) {
  binder.registerCustomEditor(Date.class, getCustomDateEditor());
 }

 protected CustomDateEditor getCustomDateEditor() {
  String datePattern = messages.getMessage(Constants.DATE_PATTERN_KEY, new Object[] {}, getLocale());
  logger.debug("Customer editor for date pattern: " + datePattern);
  return new CustomDateEditor(new SimpleDateFormat(datePattern), true);
 }

 protected Locale getLocale() {
  Locale locale = getDefaultLocale();
  try {
   locale = LocaleContextHolder.getLocale();
  } catch (Exception e) {
  }
  logger.debug("Current Locale: " + locale);
  return locale;
 }
 
 protected Locale getDefaultLocale() {
  return Locale.US;
 }

 @Override
 public void setMessageSource(MessageSource messageSource) {
  this.messages = messageSource;
 }

}


So, when the controller is initializing the MVC, it registers a custom date editor which is based on the current locale. The use of LocaleContextHolder is interesting as it holds (through ThreadLocal) the locale currently set for the FrameworkServlet in the current thread.

Customizing Dojo DateTextBox
Now, we can simply add a localized DateTextBox to a form:

<spring:message code="date.pattern" var="datePattern" />
<script type="text/javascript">
 Spring.addDecoration(new Spring.ElementDecoration({
  elementId : 'myFormInputId',
  widgetType : 'dijit.form.DateTextBox',
  widgetAttrs : {
   constraints: {min: new Date().setDate(0), max: new Date().setDate(31), datePattern: '${datePattern}'},
   promptMessage : 'Prompt Message',
   invalidMessage : 'Validation Message',
   required : true,
   datePattern : '${datePattern}',
  }
 }));
</script>

Note that, originally, the second datePattern is enough, however, as integration of Spring JS and Dojo is not complete, so you need the additional constraint on the datePattern.

Sunday, February 7, 2010

MySQL MyISAM Tuning

MySQL MyISAM is known to be good for large text indexing and fast retrieval. Still, it can create bottlenecks if not tuned properly. In my case, I need to run many SQL operations on a database of around 500`000 URL's with text on this engine. A little of bit searching and tuning, I used the following configuration for my.cnf on MySQL 5+:

# key buffer size
key_buffer          = 512M
# table caching
table_cache         = 128
# query caching
query_cache_limit   = 512M
query_cache_size    = 512M

The configurations proposed are also based on the machine you're using; so if you have no problem at resources you can go higher.

Sunday, June 7, 2009

Object Reference and Method Call in JavaScript with GWT JSNI

There are times we need to call JavaScript functions from the hosted page in GWT that would use the runtime objects of the GWT compiled code. Put it in another way, there are situations the in the middle of the JavaScript, you may need the real object from GWT code. This means that we cannot use the native static methods in JSNI. Thanks to JavaScript, there could be a solution to this:
1- Define a JavaScript variable that would actually hold a reference to a function. And, define a JavaScript function that would set this reference (This code is written in your index.html GWT page):
var functionReference;

function setFunctionReference(fr) {
     functionReference = fr;
}



2- Use GWT JSNI (JavaScript Native API) to set the object reference for the function:
public class MyEntryPoint implements EntryPoint{

public void onModuleLoad() {

// BLAH BLAH CODE

setFunctionReferenceForMe(this);

// BLAH BLAH CODE
}

public native void setFunctionReferenceForMe(MyEntryPoint ep)/*- {
   $wnd.setFunctionReference(funtion(param) {
      if (param == 0) {
         ep.@com.myworld.edu.gwt.client.MyEntryPoint
         ::method1();
      } else {
         ep.@com.myworld.edu.gwt.client.MyEntryPoint
         ::method2(Ljava/lang/String;)(param);
      }
    });
}-*/;

public void method1() {
// A non-static method called from JavaScript
}

public void method2(String param) {
// A non-static method called from JavaScript
}

}

Through this piece of code, you see that we can actually call a non-static method of an object in GWT using a trick with JSNI and some JavaScript in hosted page. Alongside, we are actually passing parameter from JavaScrip to that object, too. So, now we can call the method wherever we want through JavaScript:
functionReference.call(this, 0)
And, sure it knows about the object reference from GWT.

Wednesday, May 27, 2009

Compass - Lucene - Hibernate - PDF / Document (2)

In a previous post, I just discussed what would be needed for bring up the combination of Compass - Lucence - Spring - Hibernate. Now, I want to plug in some functionality for indexing and searching BLOB contents of PDF and documents. In Compass bean settings in Spring context, we had a configuration:
<prop key="compass.converter.blobConverter.registerClass">java.sql.Blob</prop>
<prop key="compass.converter.blobConverter.type">BlobConverter</prop>
This is required to, first, tell Compass that we are registering a converter for type java.sql.Blob, and second, the fully-qualified converter class is provided. Here, we are trying to index PDF files with Compass with JdbcDirectory. I chose PDFBox for stripping text out of the PDF content; so the coverter would like this:
public boolean marshall(Resource resource, Blob root, Mapping mapping, MarshallingContext context) throws ConversionException {
        if (root == null) {
            return false;
        }
        try {

            byte[] bytes = root.getBytes(1, (int) root.length());
            parser = new PDFParser(new ByteArrayInputStream(bytes));
            parser.parse();
            COSDocument document = parser.getDocument();
            logger.warn("Parsed an instance BLOB with PDF content type: " + document);
            String pdfText = stripper.getText(new PDDocument(document));
            logger.warn("Extracted text from PDF: " + pdfText);

            Property p = context.getResourceFactory().createProperty(mapping.getPath().getPath(), pdfText.getBytes(), Store.YES);
            logger.warn("Created a Compass property on PDF text: " + p);

            resource.addProperty(p);

            return true;

        } catch (IOException e) {
            throw new ConversionException("Failed to initialize a PDF Parser: ", e);
        } catch (SQLException e) {
            throw new ConversionException("Failed to initialize a PDF Parser: ", e);
        }
    }
Remember that this may not be complete as to retrieve something from the index you may need another piece of data such as the ID of the model or entity of which this PDF content is a part. And, if you'd like to index and search document formats in this way, you may want to use Apache POI. To wrap it up, using Compass's feature on JdbcDirectory and its extension on using Hibernated models and configuring it with Spring, you'd have a high-level API to index and search content that is stored in database.

Thursday, May 21, 2009

IBM ICU Persian (Farsi) / Arabic Shaping Bug Fix

Courtesy of Nima Honarmand and Seyyed Jamal Pishvayi, there is now a patch that fixes IBM's ICU project issue 6169. This issue addresses the Persian/Arabic Shaping Support of FB50 block. Hope this contribution would help resolve the issue faster. The patch source: ArabicShaping.java

Tuesday, May 19, 2009

Compass - Lucene - Spring - Hibernate (1)

Compass is a framework over Apache Lucene delivering robust and useful services. Besides, it provides pluggable points for frameworks such as Spring and Hibernate. Though having a reference manual, putting it all together is somehow complicated and cumbersome. This would be probably multi-part how-to on this issue. Consider this scenario: we are developing a web application with all the stuff that I do not delve into. The main focus here is rotating around an entity called "Story". So, we'd have an HBM:
<hibernate-mapping package="ir.asta.wise.core.datamanagement.textsearch.sample.story">
<class name="StoryEntity" table="LUC_STORY">
  <id name="id" column="STORY_ID" type="java.lang.String">
   <generator class="uuid"></generator>
               </id>
  <property name="name" type="java.lang.String" column="NAME" null="true"></property>
               <property name="content" type="java.lang.String" column="CONTENT" null="true" length="4096"></property>
  <property name="blobContent" type="java.sql.Blob" column="BLOB_CONTENT">  </property>
</class>
The first step is to tell Compass to index Story: we create a file named Story.cpm.xml; the Core Mapping for Story:
<compass-core-mapping package="ir.asta.wise.core.datamanagement.textsearch.sample.story">
    <class name="StoryEntity" alias="StoryEntity">
        <id name="id" />
        <property name="content">
            <meta-data>${wiseCompass.story}</meta-data>
        </property>
        <property name="blobContent" converter="blobConverter">
            <meta-data>${wiseCompass.story}</meta-data>
        </property>
</class>
</compass-core-mapping>
Along this mapping, we need to introduce Story to compass through a meta-data descriptor, call it compass.cmd.xml that provides a big picture of all indexables:
<compass-core-meta-data>
    <meta-data-group id="wiseCompass" displayName="WiSE Core Lucene/Compasss">
        <description>WiSE Core Lucene/Compass Core Meta Data</description>
        <uri>http://wise/compass</uri>
        <meta-data id="story" displayName="Story Metadata">
            <description>Story Entity Compass Metadata</description>
            <uri>http://wise/compass/story</uri>
            <name>story</name>
        </meta-data>
        <meta-data id="file" displayName="File Entity Content Metadata">
            <description>File Entity Content Metadata</description>
            <uri>http://wise/compass/file</uri>
            <name>file</name>
        </meta-data>
    </meta-data-group>
</compass-core-meta-data>
Now, let's get to Spring configuration. First, we need to create a bean to be the Compass object:
    <bean id="compass" class="org.compass.spring.LocalCompassBean">
        <property name="dataSource" ref="dataSource" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="resourceLocations">
            <list>
                <value>classpath:config/lucene/compass/*.xml</value>
            </list>
        </property>
        <property name="compassSettings">
            <props>
                <prop key="compass.name">compass</prop>
                <prop key="compass.engine.connection">jdbc://</prop>
                <prop key="compass.converter.blobConverter.registerClass">java.sql.Blob</prop>
                <prop key="compass.converter.blobConverter.type">BlobConverter</prop>
            </props>
        </property>
    </bean>
Some comments on the 'compass' bean configuration:
  • transactionManager is the reference to your bean of Spring that is the actual transaction manager that is also used for Hibernate.
  • resourceLocations is an option to tell where all the *.cpm.xml and *.cmd.xml files are.
  • As pure Lucene does not implement the JDBC Directory concept, through jdbc:// we are telling Compass that we're using JdbcDirectory implementation of Compass and for that dataSource is injected.
  • In this example, we aim to index and search BLOB types (such as PDF or Document). So we need to configure Compass for our BLOB converters. I'd discuss this more in the second part of the tutorial.
The next steps fall into two parts: saving the index and searching it. For both, we need a Compass Session that is also bound to the Compass bean with all the Hibernate bindings. To do so, we need to define two other beans for adding Hibernate collaboration for Compass:
    <bean id="hibernateGpsDevice" class="org.compass.gps.device.hibernate.HibernateGpsDevice">
        <property name="name">
            <value>Hibernate-GPS-Device</value>
        </property>
        <property name="sessionFactory" ref="sessionFactory" />
        <property name="nativeExtractor">
            <bean class="org.compass.spring.device.hibernate.SpringNativeHibernateExtractor" />
        </property>
    </bean>
And:
    <bean id="hibernateGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop">
        <property name="compass" ref="compass" />
        <property name="gpsDevices">
            <list>
                <ref bean="hibernateGpsDevice" />
            </list>
        </property>
    </bean>
Now, we can use the hibernateGps bean to for using Compass Session API. To save an index, we assume that a StoryEntity has been saved and we want to save the index:
    @Transactional(readOnly = false)
    private void saveStoryIndex(StoryEntity s) {
        CompassIndexSession session = hibernateGps.getIndexCompass().openIndexSession();
        session.save(s);
        session.commit();
        logger.warn("Index saved.");
        session.close();
    }
And, to search:
    @Transactional(readOnly = false)
    private void searchSomeStory() {
        logger.warn("Searching....");
        CompassSearchSession session = hibernateGps.getIndexCompass().openSearchSession();
        CompassHits hits = session.find("sample");
        logger.warn("Hits: " + hits.getLength());
        logger.warn("First result: " + hits.hit(0).data());
        session.close();
    }
This is a brief overview on what the integration needs. On the next part, I'd discuss on how indexing and converting of PDF's and Document's could be handled. Hope this would help.

Monday, May 18, 2009

Acegi - CAS - Service Ticket - OC4J 10.1.3.*?! - Ticket lost

Thanks to Oracle OC4J (Standalone/Embedded) that from time to time, reminds me that we still can break the rules in software collaboration. The problem begins where you have a CAS - Acegi integrated SSO solution on some application server on a machine along with another application server with some applications on it using Oracle OC4J Standalone 10.1.3.*?! to host the applications. Now, when a client application goes to the CAS server and the SSO does the sing-in process, Acegi now should return to the client application using targetting the CasProcessingFilter:
https://oc4japphost:8443/myapp/j_acegi_security_check?ticket=[CAS SERVICE TICKET]
Here comes our here OC4J that, it seems, takes it as an offence that some referrer is going to some of its hosted applications with a Query String and a request parameter. So, very logically(!!!), the OC4J container just truncates the query string and this is how the CAS ticket gets lost in the midlle of nowhere. Thanks to Seyyed Jamal, a great friend, the idea is to pass the ticket through CAS using CLEAN URL's instead of query strings such as:
https://oc4japphost:8443/myapp/j_acegi_security_check/ticket/[CAS SERVICE TICKET]
This way the OC4J container is actually unaware of what's going on. To implement the solution:
  1. CAS login-webflow.xml should edited for external redirection after successful sing-in.
  2. Acegi's CasProcessingFilter need be edited for CAS ticket lookup based on clean URL's.
CAS: login-webflow.xml The end-state should be edited so that the value for its view would be:

<end-state id="redirect" view="externalRedirect:${externalContext.requestParameterMap['service']}${requestScope.ticket== null ? '' : '/ticket/' + requestScope.ticket}"></end-state>
Acegi: CasProcessingFilter
public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException {
        String username = CAS_STATEFUL_IDENTIFIER;
        String password = extractTicket(request);

        logger.warn("[ CUSTOMIZED OC4J CAS Processing Filter ] Found CAS ticket: " + password);

        if (password == null) {
            password = "";
        }
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));

        Authentication authenticationResult = this.getAuthenticationManager().authenticate(authRequest);

        if (authenticationResult != null) {
           logger.warn("[ CUSTOMIZED OC4J CAS Processing Filter ] CAS authentication completed. Its success will be decided afterwards.");
        }
        return authenticationResult;
    }
And, now:
    protected String extractTicket(HttpServletRequest request) {
        String ticket = request.getParameter("ticket");
        if (StringUtils.hasText(ticket)) {
            logger.warn("Service TICKET found on query string: " + ticket);
            return ticket;
        }
        String uri = request.getRequestURI();
        if (uri.indexOf("/ticket/") > 0) {
            ticket = uri.substring(uri.lastIndexOf('/') + 1);
            if (StringUtils.hasText(ticket)) {
                logger.warn("Service TICKET found on clean URL: " + ticket);
                return ticket;
            }
        }
        logger.error("No SERVICE TICKET FOUND on request: " + uri);
        return null;
    }
Then, remeber to edit you Acegi's bean of org.acegisecurity.util.FilterChainProxy and it property filterInvocationDefinitionSource so that the value for j_acegi_security_check would be:
/j_acegi_security_check*/**=httpSessionContextIntegrationFilter,casProcessingFilter
It seems that OC4J is working through this solution on separate CAS server and its applications get singed in through. This issue has also been discussed in Spring Forums: http://forum.springsource.org/showthread.php?t=38897 Hope this would help.