Wednesday, January 30, 2013

Nearly trivial but useful

After I installed Alfresco 4.1.2 via the exe-Installer, I found out that there is now a small tool which is named 'Manager Tool'. The only thing it does is to list you the Alfresco related service by allowing you to start or stop them. Sure, the Windows Service Manager does exactly the same for you and you may even know how to start or stop services via the command line. However, I just installed a test environment and I really appreciate the possibility to control just the Alfresco related service via one single tool. And the most cool thing is that you can extend it by adding own services those are related to your Alfresco installation. In my case I added the services for the SQLServer, the Transformation Server and the Reverse Proxy. The following screen shot explains everything ...


How to install a reverse Proxy on Windows

It's usually not a good idea to install a service as a privileged user. So you may know that it is not possible for an unprivileged user to bind ports below the port number 1000. This is the reason why application containers (like Tomcat) are using the port 8080 instead 80. However, sometimes you want to reach your service via port 80 without the need to run the same as a privileged user. This article explains how to put a reverse proxy in front of a Tomcat server which is listening on port 8080.

At first just install Apache2 by using the following installer: http://mirror.netcologne.de/apache.org//httpd/binaries/win32/httpd-2.0.64-win32-x86-openssl-0.9.8o.msi .

If you are a Linux user, then you can just install the server via your package manager. On most Linux distributions then it is also required to install the modules as seperate packages. What we need is the module 'mod_proxy'.

The above mentioned installation package already includes the proxy modules like 'mod_proxy'. So you just have to configure Apache in order to use it. Apaches's configuration file is named 'httpd.conf'. By default you can find it under %APACHE_HOME%\conf .

Here are the steps:

  1. In the 'Load Module' section enable the Proxy modules
  2. Disable ProxyRequests in order to use the reverse proxy functionalit instead just the foward one
  3. Define who should have the permission to use the proxy
  4. Define the forward and reverse targets
  5. Define who should have the permission to access the root location
#Load the required modules 

...

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so

#Base configuration for a reverse proxy

...
ProxyRequests Off

<Proxy *>
        Order deny,allow
        Allow from all
</Proxy>

ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/

<Location />
        Order deny,allow
        Allow from all
</Location>

... 


Friday, January 25, 2013

How to bootstrap categories in Alfresco

If you want to categorize document it may result in the wish to have custom categories. In order to have a clean deployment of your Alfresco based application you really don't want to create these categories by hand. So what you need is to deploy the categories as part of the bootstrap of you application. This article explains how it works.

Let's assume that you followed this Blog and you know that it is possible to set up a repository project by using the AMP archetype which is provided via Maven. Then you already have the source folder which is 'config' which contains a subfolder which is named 'context''. Here the steps:

  • Create a new folder 'bootsrap' inside the 'context' folder
  • Create a new xml file named bootstrap-context inside the context folder and add the following to it:
 

 <?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   

    <bean id="${groupId}.${artifactId}.myBootstrap" class="org.alfresco.repo.module.ImporterModuleComponent" parent="module.baseComponent">

           

             <!-- Module Details -->

                <property name="moduleId" value="${pom.artifactId}" />

             <property name="name" value="myBootstrap" />

             <property name="description" value="For bootstrapping purposes" />

             <property name="sinceVersion" value="1.0" />

             <property name="appliesFromVersion" value="1.0" />

           

            <!-- Data -->

               <property name="importer" ref="spacesBootstrap"/>

               <property name="bootstrapViews">

                  <list>

                      <!-- Bootstrap the categories -->

                     <props>

                        <prop key="path">/cm:categoryRoot/cm:generalclassifiable</prop>

                        <prop key="location">alfresco/module/${artifactId}/bootstrap/my_categories.xml</prop>

                     </props>

                  </list>

               </property>

    </bean>

</beans>


  •  Now import the this context by adding the following line to the file 'config/module-context.xml':

    <import resource="classpath:alfresco/module/${artifactId}/context/bootstrap-context.xml"/>



  • Finally create an XML file named 'my_categories.xml which contains your category names like this:

 <view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"

           xmlns:sys="http://www.alfresco.org/model/system/1.0"

           xmlns:cm="http://www.alfresco.org/model/content/1.0">



   <cm:category>

      <cm:name>My Categories</cm:name>

      <cm:subcategories>

         <cm:category>

            <cm:name>My first category</cm:name>

            <cm:subcategories>

               <cm:category>

                  <cm:name>My second category</cm:name>

               </cm:category>

               <cm:category>

                  <cm:name>My third category</cm:name>

               </cm:category>

               <cm:category>

                  <cm:name>My last category</cm:name>

               </cm:category>

            </cm:subcategories>

         </cm:category>  

      </cm:subcategories>

   </cm:category>



</view:view>


Tuesday, January 22, 2013

The cat named Tom needs some food

I can't count how often I installed Apache Tomcat for testing and development purposes. More interesting customers often already have their own Application Servers running as part of their infrastructure. So I had not that often to configure a Tomcat application container for production purposes. This article explains the production configuration of Apache Tomcat (6/7).

So here are the steps:

  1. Configure the ports by editing the server.xml file.  (Shutdown port, Connector port, AJP port, SSL port)
  2. Add a manager role by editing the tomcat-users.xml file. The syntax is: <role rolename="admin"/> <role rolename="manager"/> <user usrname="$uname" roles="manager, admin" password="$upwd"/>. This allows you to access the Tomcat Manager application.
  3.  Most important tweak the memory settings because Java apps are quite hungry. You can do this by editing the batch or shell script which is named "setenv". Here an example for the JAVA_OPTS: -server -Xss1024K -Xms1G -Xmx2G -XX:MaxPermSize=128M -XX:NewSize=512m -XX:+UseConcMarkSweepGC  -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80 . This reserves 1GB-2GB memory for the JVM by also changing the garbage collection behavior a bit. (The explainations are available here: http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html)
  4. Make sure that the directory listings are disabled by editing the default web.xml file. It defines a default servlet which has a parameter which is named 'listings'. The parameter value should be 'false'.
  5. You could use a Valve to restrict the access to specific IP-s or hosts (http://tomcat.apache.org/tomcat-6.0-doc/config/valve.html) but I think this is more a task of a firewall.
  6. I usually use specific users for specific services. So I would use a user named 'tomcat' as the installation owner who also runs it. It's not recommended to run Tomcat as an Administrator or the Root user. This means to run it on ports >1000, which can be solved by putting an Apache HTTP server in front of it.
  7. Use GZip compression by editing the server.xml file again. There should be at least one Connector defined. Here an example: <Connector port="8080" URIEncoding="UTF-8" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" compression="on" compressableMimeType="text/html,text/xml,text/plain,application/xml/>. It means basically that resources are compressed on side of the server before they will be transfered to the client. This can help to increase the performance.
Any further recommendations for the production usage of Apache Tomcat? Feel free to reply to this post!

Monday, January 14, 2013

Resolving Share extension build problems if using Maven

A few weeks ago I did a blog post where I explained how simple it is to set up Alfresco projects by using Maven. Today I tried to add some Share extensions and found out that there is an issue with it. If you use the default pom.xml build script which was created the way which I mentioned before, then you may miss some dependencies. The problem begins with the fact that Alfresco added the Share dependencies as a WAR-file dependency which can't be resolved by Eclipse's Maven plug-in. I tried out to add the libraries by hand to the Build Path, but this causes that you can build within Eclipse but no longer by using Maven. So the way how I solved it was to add some additional dependencies and repositories:

You should add the following repository (Not sure if it was already resolved by Alfresco's Maven Nexus).

<repository>

            <id>spring-surf</id>

            <url>https://extensions.springframework.org/milestone</url>

</repository> 



Then I added the following dependencies:

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-core</artifactId>

            <version>3.0.1.RELEASE</version>

        </dependency>

        <dependency>

            <groupId>org.springframework.extensions.surf</groupId>

            <artifactId>spring-webscripts</artifactId>

            <version>1.0.0.M3</version>

        </dependency>

        <dependency>

            <groupId>org.springframework.extensions.surf</groupId>

            <artifactId>spring-webscripts-api</artifactId>

            <version>1.0.0.M3</version>

        </dependency>

        <dependency>

            <groupId>javax.servlet</groupId>

            <artifactId>servlet-api</artifactId>

            <version>2.5</version>

            <scope>provided</scope>

        </dependency>

        <dependency>

            <groupId>org.springframework.extensions.surf</groupId>

            <artifactId>spring-surf</artifactId>

            <version>1.0.0</version>

        </dependency>


Now it was at least possible to build my Share extension. It may be required to add further depencies in order to have full support.

How to disable localization in Alfresco Share

If you have the requirement that Alfresco Share should be available in only one single language then this is not that trivial. By default Alfresco Share takes the web browser language into account. There is out of the box no way to choose the language. This post describes how to set the language to just the English one. What you need to know is that there is a Spring Bean with the Id 'localeResolver'. So we need to override the Bean by using an own implementation of it.If you do not know the Spring framework, then what you need to know at this point is that you can configure Java Classes as Beans by using XML. An instance of the Class is then available via the Spring Context. So you get get an instance of the Class by asking the Spring Context for a Bean with a specific id. It's possible to have multiple instances with different id-s and parameters. So a lot of stuff  in Alfresco is accessible via Spring Beans. If something is available via the above mentioned context, then you can just override it. This changes Alfresco's default behavior. The reason why it works is that the custom configuration becomes loaded later than the original configuration and so the original Spring Bean definition becomes overridden. Here the steps:
  1. In '${TOMCAT_HOME}/shared/classes/alfresco/web-extension' create a context file by naming it to 'custom-slingshot-application-context.xml'. I believe the only important thing here is that it ends with '-context.xml'.
  2. In this file define a Spring Bean by using the following code:
 <?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:hz="http://www.hazelcast.com/schema/config"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.hazelcast.com/schema/config
                http://www.hazelcast.com/schema/config/hazelcast-spring.xsd">
  
        <bean id='localeResolver' class='de.ecmgeek.alfresco.MyLocaleResolver'/>
  
</beans>


Now you can just implement your own LocaleResolver and put it to the classpath (E.G. as a JAR file):


package de.ecmgeek.alfresco;

import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.springframework.extensions.surf.mvc.LocaleResolver;
import org.springframework.extensions.surf.util.I18NUtil;

public class MyLocaleResolver extends LocaleResolver
{
    @Override
    public Locale resolveLocale(HttpServletRequest request)
    {   

        /* Here we could set the locale by getting the value of the 

           cookie with the name ALFRESCO_UI_PREFLANG or by handling

           a request parameter. For now this value is just null. */

        Locale locale = null;
   
        if (locale == null)
        { 

            //To set a constant value

            locale = I18NUtil.parseLocale("en_EN");

        }     

        

        I18NUtil.setLocale(locale); 

        return locale;
    }
}  

How to externalize the SOLR instance which comes with Alfresco 4.x

Alfresco 4.x uses SOLR instead Lucene. In fact SOLR is just a Lucene index with a service on top of it. Furthermore it provides meanwhile some new features (search facettes, joins, ...) If you install Alfresco Enterprise 4.x by using the installation package (bundled with PostgreSQL and Apache Tomcat), then it is already configured to use the bundled SOLR 1.4. The search service is then deployed as a web application to the Tomat servlet container. A better idea is to deploy SOLR to an own Tomcat server which then has its own Java environment and so on. The second Tomcat instance is running on the same server host, but now a little bit more isolated from the Alfresco instance. Here the steps:

  1. Make a new directory 'tomcat_solr' in the Alfresco installation directory $ALF_HOME
  2. Copy the contents of the folder 'tomcat' to the folder 'tomcat_solr'
  3. In 'tomcat_solr/webapps' undeploy the 'share' and 'alfresco' applications, by removing the folders. Also remove the files 'alfresco.war' and 'share.war' to avoid that Alfresco becomes redeployed.
  4.  In 'tomcat_solr/webapps/shared/classes' and 'tomcat_solr/logs' remove everything below it. Also remove 'tomcat_solr/conf/Catalina/localhost/alfresco.xml'. You now have a kind of clean Tomcat instance.
  5. Edit the server's configuration by changing the ports. The shutdown port should be '8095', the connector port should be '8090', the SSL port should be '8449', the AJP port should be 8099. You now have a Tomcat instance which does not conflict with the Alfresco one.
  6. Open the file 'tomcat_solr/conf/Catalina/localhost/solr.xml'. You can see that the home directory of SOLR is set to 'alf_data/solr'. You can change this if you want to store the index files at another location. Also the SOLR web app archive is stored there.
  7. SOLR is configured to use 2 cores. This means that you can find the 2 subfolders 'alf_data/solr/archive-SpacesStore' and 'workspace-SpacesStore'. You can see this maps to Alfresco's content stores. Each SOLR core has its own configuration. In the 'solcore.properties' file you can specify where the index files should be stored. The property 'data.dir.root' points to 'alf_data/solr' for both stores. The 'alfresco.*' settings are used to define which Alfresco instance (host, port, store, interval, ...) is used.
  8. More about the keystore is available here  http://docs.alfresco.com/4.1/topic/com.alfresco.enterprise.doc/tasks/generate-keys-solr.html, currently it's just important that you know that your new Tomcat instance is already configured to use the already existing one. It is located under 'alf_data/keystore'. Also the file 'tomcat_solr/conf/tomcat-users.xml' does already contain the required user definition.
  9. Finally you need to remove the Solr stuff from the old Tomcat installation, and to change the Alfresco configuration (tomcat/shared/classes/alfresco-global.properties' to use the SSL port which was specified for SOLR (8849). Therefore the property 'solr.port' should be changed. If you also moved the index keystore then you need to change the 'dir.keystore' property.
  10. Now start Alfresco!
  11. Now start the Tomcat which hosts the SOLR instance. There should be no error in the log file.
  12. Log-in to Share, create a new document by setting some attributes.
  13. Wait a few seconds (SOLR polls Alfresco!) and then check the log files of SOLR's Tomcat! Afterwards perform a search by using Alfresco Share!