Friday, June 12, 2015

Agile Software Development Videos from Bob Martin

Here are some really good videos about agile development.   Bob Martin is a great presenter. 

These are all pretty long but they are all worth watching.   The first 3 in the list below are my favorites.

Professional Software Development

Craftsmanship and Ethics

Demanding Professionalism


Clean Architecture and Design

The Single Responsibility Principle


The SOLID Principles of OO and Agile Design

Saturday, May 31, 2014

Moving from traditional filesystem storage to ATMOS for Alfresco

I recently had the opportunity to configure Alfresco with ATMOS using the Alfresco S3 connector.

The company I work for has an ATMOS cloud setup across multiple data centers.  

ATMOS is an object-based cloud storage platform to store, archive and access unstructured content at scale.  

We had an existing instance of Alfresco leveraging NetApp storage that I had to migrate to ATMOS.

In order to do this I needed to download and install 2 tools.

The steps to make the move to ATMOS are fairly simple…

    1.  Stop your Alfresco and Solr servers

    2.  Create your S3 bucket with ATMOS FOX. 
    3.  After you create the bucket you must add a non listable meta tag (bucket-mapping-type=one_to_one) to the bucket folder. 

     4.  Copy your files in your contentstore and contentstore.deleted to Atmos using AtmosSync.jar. 

     5.  Update your alfresco-global.properties with your S3 configuration.
          ### S3 Config ###
          s3.accessKey=xxxxxxxxxxxx/xxxx
          s3.secretKey=xxxxxxxxs3.bucketName=bucketNAME

             #s3.bucketLocation=US
             s3.flatRoot=falses3service.https-only=false
             s3service.s3-endpoint=ATMOSHOST
             s3service.s3-endpoint-http-port=8080#
             s3service.disable-dns-buckets=false
             dir.contentstore=contentstore
             dir.contentstore.deleted=contentstore.deleted
          #Maximum disk usage for the cache in MB 

             system.content.caching.maxUsageMB=51200
             #Maximum size of files which can be stored in the cache in MB (zero implies no limit)              
             system.content.caching.maxFileSizeMB=0 

      6.  Back up your DB if you haven’t already

      7.  Update all records in the ALF_CONTENT_URL table (store:// to s3://)
               UPDATE alf_content_url SET content_url = replace(content_url, 'store:', 's3:’)

 
     8.   Startup Alfresco and Solr servers and you should be good to go.   


Once you have verified that Alfresco is functioning properly you can repurpose the filesystem storage.

Wednesday, January 8, 2014

Duplicate entries put into HashSet Java issue

I have recently run into an issue using HashSet.addAll(Object) where duplicates are added to my Set.

I even made sure that the Object I was using Overrode hashCode().

Apparently the addAll() implementation for HashSet doesn't check for duplicates.  This is bad because a reason for using a Set over a List is to avoid duplicate values.

If you loop through all of the values in the collection you are adding to the Set it works as expected not adding the duplicates.  This is the approach I took to get around this issue.

for (item in myCollection){
    set.add(item)
}

vs what I was trying to do which didn't work:

set.addAll(myCollection)

Another approach could be extend the HashSet and override the addAll() with the logic I used.


Saturday, June 22, 2013

JUnit TemporaryFolder Rule


If you need a temporary directory or file for testing and you are using jUnit, @Rule together with TemporaryFolder solves your problem. 
The TemporaryFolder Rule allows creation of files and folders that are guaranteed to be deleted when the test method finishes (whether it passes or fails) 
@Rule

public TemporaryFolder tempFolder = new TemporaryFolder();
...

@Test
public void testFileCreation (){
        File myFolder = tempFolder.newFile("test.);
        fileGenerator.createFiles(2, myFolder);
        myFolder.listFiles().length == 2;
}
The files that were generated will be deleted once the test finishes executing.

Wednesday, May 15, 2013

Using Groovy Closures To Simplify Working With Alfresco Transactions

If you are familiar with Alfresco transactions than you will be familiar with the following:
  
Boolean result = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Boolean>() {
            public Boolean execute() throws Throwable {
                //Your code
                return true;
            }
}, false, true);
This is quite a bit of code and gets ugly if you start embedding a bunch of code inside this block. It is also not very testable with out some crazy mocking.


Groovy Closures to the rescue. http://groovy.codehaus.org/Closures
I created a simple TransactionHelper Groovy class with a method that takes a closure and wraps the closure inside of the above code required for a transaction and wired it as a Spring Bean.

import org.alfresco.repo.transaction.RetryingTransactionHelper
import org.alfresco.service.transaction.TransactionService
class TransactionHelper {
    private TransactionService transactionService
    def executeInSperarateTransaction(closure) {
        Boolean result = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Boolean>() {
            public Boolean execute() throws Throwable {
                closure()
                return true
            }
        }, false, true)
    }
    void setTransactionService(TransactionService transactionService) {
        this.transactionService = transactionService
    }
}

Now if I need to execute a transaction I can simply do the following.
transactionHelper.executeInSperarateTransaction({ nodeService.addAspect(nodeRef, MyModel.THUMBNAIL_ASPECT, new HashMap<QName, Serializable>())})


You can simply pass a closure statement like above or pass in a closure like the following.

def closure() {
  def localVariable = new java.util.Date()
  return { println localVariable }
}
transactionHelper.executeInSeperateTransaction(closure)
 
For more information on Groovy and Closures see http://groovy.codehaus.org/Closures 

Wednesday, April 10, 2013

Bootstraping Users with Alfresco



Bootstrapping data is a very common thing to do when deploying new modules.   You may be expecting a certain taxonomy, certain set of rules, certain categories, certain users, certain metadata , etc. to be available to your new module.  http://wiki.alfresco.com/wiki/Bootstrap_Data shows you how to bootstrap files, spaces, and categories. But I wanted to bootstrap a user that was going to be used to for a third-party system to interact with Alfresco.  After some searching I came across http://forums.alfresco.com/forum/developer-discussions/repository-services/howto-bootstrap-users-09182008-1214 which  described most of what I needed to do.
In order to bootstrap users you need to:

Create an xml file containing the person information (people.xml)

<?xml version="1.0" encoding="UTF-8"?>
<view:view xmlns:view="http://www.alfresco.org/view/repository/1.0" xmlns:alf="http://www.alfresco.org" xmlns:d="http://www.alfresco.org/model/dictionary/1.0" xmlns:sys="http://www.alfresco.org/model/system/1.0" xmlns:act="http://www.alfresco.org/model/action/1.0" xmlns:rule="http://www.alfresco.org/model/rule/1.0" xmlns:fm="http://www.alfresco.org/model/forum/1.0" xmlns:app="http://www.alfresco.org/model/application/1.0" xmlns:usr="http://www.alfresco.org/model/user/1.0" xmlns:ver="http://www.alfresco.org/model/versionstore/1.0" xmlns:cm="http://www.alfresco.org/model/content/1.0" xmlns="">
    <cm:person view:childName="cm:0001">
        <view:acl>
            <view:ace view:access="ALLOWED">
                <view:authority>jbarrett</view:authority>
                <view:permission>All</view:permission>
            </view:ace>
        </view:acl>
        <view:properties>
            <cm:firstName>Josh</cm:firstName>
            <cm:lastName>Barrett</cm:lastName>
            <cm:email>jbarrett2k3@gmail.com</cm:email>
            <cm:userName>jbarrett</cm:userName>
            <cm:homeFolder>workspace://SpacesStore/MY_USERS_HOME</cm:homeFolder>
            <cm:organizationId></cm:organizationId>
            <cm:sizeQuota>-1</cm:sizeQuota>
            <cm:sizeCurrent>0</cm:sizeCurrent>
        </view:properties>
    </cm:person>
</view:view>

Create an xml file containing the authority information (authorities.xml)

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

<view:view xmlns:view="http://www.alfresco.org/view/repository/1.0" xmlns:alf="http://www.alfresco.org" xmlns:d="http://www.alfresco.org/model/dictionary/1.0" xmlns:sys="http://www.alfresco.org/model/system/1.0" xmlns:act="http://www.alfresco.org/model/action/1.0" xmlns:rule="http://www.alfresco.org/model/rule/1.0" xmlns:fm="http://www.alfresco.org/model/forum/1.0" xmlns:app="http://www.alfresco.org/model/application/1.0" xmlns:usr="http://www.alfresco.org/model/user/1.0" xmlns:ver="http://www.alfresco.org/model/versionstore/1.0" xmlns:cm="http://www.alfresco.org/model/content/1.0" xmlns="">

    <usr:user view:childName="usr:jbarrett"><!-- the value of usr must be the username -->

        <view:properties>

            <usr:username>jbarrett</usr:username>

            <usr:password>b02e3a7432ac716fdc2bb8df46ec5ab8</usr:password> <!—MD4 Hash which can be generated following the instructions http://wiki.alfresco.com/wiki/Security_and_Authentication#How_to_generate_the_correct_MD4_hash. -->

            <usr:accountExpires>false</usr:accountExpires>

            <usr:credentialsExpire>false</usr:credentialsExpire>

            <usr:accountLocked>false</usr:accountLocked>

            <usr:enabled>true</usr:enabled>

        </view:properties>

    </usr:user>

</view:view>

Bootstrap the xml files via a spring context file (mymodulebootstrap-context.xml)

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

<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>

    <bean id="myModlue_bootstrapSpaces" class="org.alfresco.repo.module.ImporterModuleComponent" parent="module.baseComponent">

        <property name="moduleId" value="myModule" />

        <property name="name" value="myModule.bootstrapSpaces" />

        <property name="description" value="Initial data requirements" />

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

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

        <property name="executeOnceOnly" value="true" />



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

        <property name="bootstrapViews">

            <list>

                <props>

                    <prop key="path">/${system.system_container.childname}/${system.people_container.childname}</prop>

                    <prop key="location">alfresco/module/myModule/bootstrap/people.xml</prop>

                </props>

            </list>

        </property>

    </bean>



    <bean id="myModule_bootstrapUserGroups" class="org.alfresco.repo.module.ImporterModuleComponent" parent="module.baseComponent">

        <property name="moduleId" value="myModule" />

        <property name="name" value="myModule.bootstrapGroups" />

        <property name="description" value="Initial data requirements" />

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

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

        <property name="executeOnceOnly" value="false" />

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

        <property name="bootstrapViews">

            <list>

                <props>



                    <prop key="path">/${alfresco_user_store.system_container.childname}/${alfresco_user_store.user_container.childname}</prop>

                    <prop key="location">alfresco/module/mymodule/bootstrap/authorities.xml</prop>

                </props>

            </list>

        </property>

    </bean>
</beans>

That is all that is needed to bootstrap a user.


Tuesday, January 22, 2013

Cool Online Agile Board

http://leankit.com/

I stumbled across this tool today and it looks pretty neat. 

The free account allows for 25 users and 10 boards.