You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by je...@apache.org on 2012/08/15 16:07:57 UTC

svn commit: r1373426 [4/4] - in /chemistry/opencmis/trunk: ./ chemistry-opencmis-test/chemistry-opencmis-test-client/ chemistry-opencmis-test/chemistry-opencmis-test-tools/ chemistry-opencmis-test/chemistry-opencmis-test-tools/resources/ chemistry-open...

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapper.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapper.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapper.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapper.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.mapper;
+
+import java.util.Properties;
+
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+
+/**
+ * A property mapper is a class being responsible to map a Content-Type (e.g. image/jpeg)
+ * to a CMIS type id and to map and convert properties. Extracted tags are mapped to 
+ * CMIS property ids and sometimes type conversion is required (e.g. a string to a date).
+ * Classes implementing this interface are not responsible for extracting the metadata 
+ * (see MetadataParser). It only reads a configuration and maps properties. The 
+ * Configurator will read the configuration properties and instantiate implementations 
+ * of this interface (one instance per CMIS type)
+ * 
+ * @author Jens
+ *
+ */
+public interface PropertyMapper {
+    
+    /**
+     * initialize a property mapper
+     * @param cfgPrefix
+     *      prefix for all configuration entries in properties file
+     * @param typeKey
+     *      type key in configuration used to identify this type
+     * @param properties
+     *      all properties read from resource mapping.properties
+     * @return
+     *      true, if processing should continue, false if not 
+     */
+    boolean initialize(String cfgPrefix, String typeKey, Properties properties);
+    
+    /**
+     * Reset all internal data to get ready for a new parsing process 
+     */
+    void reset();
+    
+    /**
+     * return the CMIS type id used for this mapper
+     * @return
+     *      CMIS type id to map this content type to or null if not mappable
+     */
+    String getMappedTypeId();
+    
+    /**
+     * return the CMIS property id for a found tag in a file
+     * @param key
+     *      tag (usually parsed from Tika) found in file
+     * @return
+     *      CMIS property this tag gets mapped to, null if not mapped
+     */
+    String getMappedPropertyId(String key);
+    
+    /**
+     * Convert a value parsed from the file to the type the corresponding property expects
+     * @param id
+     *      CMIS property id
+     * @param propertyType
+     *      property type from type definition
+     * @param strValue
+     *      value read from file (Tika always gives a string)
+     * @return
+     *      converted value conforming to the mapped property
+     */
+    Object convertValue(String id, PropertyDefinition<?> propDef, String strValue);
+
+    /**
+     * get all content types handled by this parser
+     * @return
+     *      array with content types
+     */
+    public String[] getContentTypes();
+
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperExif.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperExif.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperExif.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperExif.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.mapper;
+
+import java.lang.reflect.Array;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.enums.PropertyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.drew.imaging.PhotographicConversions;
+import com.drew.lang.Rational;
+import com.drew.metadata.Directory;
+import com.drew.metadata.Tag;
+import com.drew.metadata.exif.ExifDirectory;
+import com.drew.metadata.exif.GpsDirectory;
+import com.drew.metadata.jpeg.JpegDirectory;
+
+public class PropertyMapperExif extends AbstractPropertyMapper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PropertyMapperExif.class.getName());
+    private static String EXIF_DATE_FORMAT = "yyyy:MM:dd HH:mm:ss";
+
+    private Map<String, String> propMapExif = new HashMap<String, String> (); // tag to property id
+    private Map<String, String> propMapGps = new HashMap<String, String> ();  // tag to property id
+    private Map<String, String> propMapJpeg = new HashMap<String, String> (); // tag to property id
+    
+    protected Map<String, Object> propMap;
+
+    public boolean initialize(String cfgPrefix, String typeKey, Properties properties) {
+        super.initialize(cfgPrefix, typeKey, properties);
+        reset();
+        dateFormat = EXIF_DATE_FORMAT;
+        buildIdMap("exif", propMapExif, properties);
+        buildIdMap("gps", propMapGps, properties);
+        buildIdMap("jpeg", propMapJpeg, properties);
+        
+        return true;
+    }
+
+    public void reset() {
+        propMap = new HashMap<String, Object> ();
+    }
+    
+
+    public Map<String, Object> getMappedProperties() {
+        return propMap;
+    }
+    
+    private void buildIdMap(String dirKey, Map<String, String> propMap, Properties properties) {
+        Set<String> keys = properties.stringPropertyNames(); 
+        String prefix = propPrefix + "." + dirKey + ".id.";
+        for (String key : keys) {
+            if (key.startsWith(prefix)) {
+                String id = key.substring(prefix.length());
+                String cmisPropId = properties.getProperty(key).trim();
+                if (null == cmisPropId)
+                    throw new MapperException("Configuration key " + key + " must have a value assigned");
+                LOG.debug("Found mapping for type " + dirKey + " with " + id + " to " + cmisPropId);
+                propMap.put(id,  cmisPropId);
+            }
+        }
+    }
+
+    public String getMappedTypeId(String mimeType) {
+        return cmisTypeId;
+    }
+
+    public String getMappedPropertyId(String key) {
+        return null;
+    }
+
+    public Object convertValue(String id, PropertyDefinition<?> propDef, String strValue) {
+        return null;
+    }
+
+    private String getHexString(int tagType) {
+        StringBuffer hexStr = new StringBuffer();
+        hexStr.append(Integer.toHexString(tagType));
+        while (hexStr.length() < 4)
+            hexStr.insert(0, "0");
+        hexStr.insert(0, "0x");
+        return hexStr.toString();
+    }
+    /**
+     * store the property id mapped to this tag in a JPEG file in a local map
+     * @param dir
+     *      directory of tag
+     * @param tag
+     *      tag
+     */
+    
+    public void mapTagAndConvert(Directory dir, Tag tag, TypeDefinition td) {
+        String propId = null;
+        String hexStr = getHexString(tag.getTagType());
+        
+        if (GpsDirectory.class.equals(dir.getClass())) {
+            propId = propMapGps.get(hexStr);
+        } else if (ExifDirectory.class.equals(dir.getClass())) {
+            propId = propMapExif.get(hexStr);
+        } else if (JpegDirectory.class.equals(dir.getClass())) {
+            propId = propMapJpeg.get(hexStr);
+        } else
+            propId = null;
+        
+        if (null != propId) {
+            if (null != td) {
+                PropertyDefinition<?> pd = td.getPropertyDefinitions().get(propId);
+                if (null == pd)
+                    throw new MapperException("Unknown property id " + propId + " in type definition " + td.getId());
+                PropertyType pt = pd.getPropertyType();
+                Object convValue = convertValue(dir, tag, pt);
+                propMap.put(propId, convValue);
+            } else
+                propMap.put(propId, dir.getObject(tag.getTagType())); // omit conversion if no type definition is available
+        }
+    }
+    
+    public Object convertValue(Directory dir, Tag tag, PropertyType propType) {
+        
+        Object res = null;
+        String hexStr = getHexString(tag.getTagType());
+
+        // Handle all tags corresponding to their directory specifics
+        if (GpsDirectory.class.equals(dir.getClass())) {
+            // first check for those tags that need special consideration:
+            if ( GpsDirectory.TAG_GPS_LONGITUDE == tag.getTagType()) {
+                Object ref = dir.getObject(GpsDirectory.TAG_GPS_DEST_LONGITUDE_REF);
+                boolean mustInv = ref != null && ref.equals('W');
+                return convertGps(tag, dir, mustInv);
+            } else if ( GpsDirectory.TAG_GPS_LATITUDE == tag.getTagType()) {
+                Object ref = dir.getObject(GpsDirectory.TAG_GPS_DEST_LONGITUDE_REF);
+                boolean mustInv = ref != null && ref.equals('S');
+                return convertGps(tag, dir, mustInv);
+            } else {
+                String propId = propMapGps.get(hexStr);
+                LOG.debug("Found GPS tag '" + tag + "\', property mapped is: " + propId);
+                if (null == propId) {
+                    LOG.info("Ignoring EXIF tag '" + tag + "\' no property mapped to this tag.");
+                } else if (propType == null) {
+                    // should not happen and is a configuration error: we have a property id but no type
+                    LOG.error("Ignoring EXIF tag '" + tag + "\' no property type mapped to this tag.");
+                }
+                Object src = dir.getObject(tag.getTagType());        
+                Class<?> clazz = src.getClass();
+                if (clazz.equals(Rational.class)) {
+                    // expect a CMIS decimal property
+                    if (propType != PropertyType.DECIMAL)
+                        throw new MapperException("Tag value has type Rational and expected CMIS Decimal, but found: " + propType + " for tag: " + tag);
+                    double d = ((Rational) src).doubleValue();
+                    res = d;
+                } else if (clazz.equals(String.class)) {
+                    if (propType != PropertyType.STRING && propType != PropertyType.ID && propType != PropertyType.URI &&
+                            propType != PropertyType.HTML && propType != PropertyType.DATETIME)
+                        throw new MapperException("Tag value has type String and expected CMIS String, but found: " + propType + " for tag: " + tag);
+                    String s = ((String) src);
+                    res = s;
+                } else
+                res = null;
+            }
+        } else if (ExifDirectory.class.equals(dir.getClass())) {
+            // is there a property mapped to this tag?
+            String propId = propMapExif.get(hexStr);
+            LOG.debug("Found EXIF tag '" + tag + "\', property mapped is: " + propId);
+
+            if (null == propId) {
+                LOG.debug("Ignoring EXIF tag '" + tag + "\' no property mapped to this tag.");
+            } else if (propType == null) {
+                // should not happen and is a configuration error: we have a property id but no type
+                LOG.error("Ignoring EXIF tag '" + tag + "\' no property type mapped to this tag.");
+            } else {
+                Object src = dir.getObject(tag.getTagType());        
+                Class<?> clazz = src.getClass();
+                // handle arrays and map them to multi-value properties
+                if (clazz.isArray()) {
+                    LOG.error("Found a multi-value tag " + tag + ": multi value not implemented");
+                    return null;
+                }
+                if (clazz.equals(Rational.class)) {
+                    // expect a CMIS decimal property
+                    if (propType != PropertyType.DECIMAL)
+                        throw new MapperException("Tag value has type Rational and expected CMIS Decimal, but found: " + propType + " for tag: " + tag);
+                    
+                    if (tag.getTagType() == ExifDirectory.TAG_SHUTTER_SPEED) {
+                        // requires special handling, see Tika impl.
+                        double apexValue = ((Rational) src).doubleValue();
+                        res = PhotographicConversions.shutterSpeedToExposureTime(apexValue);
+                    } else if (tag.getTagType() == (ExifDirectory.TAG_APERTURE)) {
+                        double aperture =((Rational) src).doubleValue();
+                        double fStop = PhotographicConversions.apertureToFStop(aperture);
+                        res = fStop;
+                    } else {
+                        // convert to a double
+                        double d = ((Rational) src).doubleValue();
+                        res = d;
+                    }
+                } else if (clazz.equals(Integer.class)) {
+                    if (propType != PropertyType.INTEGER)
+                        throw new MapperException("Tag value has type Integer and expected CMIS Integer, but found: " + propType + " for tag: " + tag);
+                    // convert to a long
+                    long l = ((Integer) src).longValue();
+                    res = l;                                        
+                } else if (clazz.equals(String.class)) {
+                    if (propType != PropertyType.STRING && propType != PropertyType.ID && propType != PropertyType.URI &&
+                            propType != PropertyType.HTML && propType != PropertyType.DATETIME)
+                        throw new MapperException("Tag value has type String and expected CMIS String, but found: " + propType + " for tag: " + tag);
+                    // convert to a String
+                    if (propType == PropertyType.DATETIME) {
+                        // parse format: 2012:02:25 16:23:16
+                        DateFormat formatter = new SimpleDateFormat(dateFormat);
+                        GregorianCalendar cal;
+                        try {
+                            Date date = (Date)formatter.parse((String) src);
+                            // convert date to GreogorianCalendar as CMIS expects
+                            cal = new GregorianCalendar();
+                            cal.setTime(date);
+                        } catch (ParseException e) {
+                            LOG.error(e.toString(), e);
+                            throw new MapperException("Unrecognized date format in EXIF date tag: " + src + " for tag: " + tag + " expected: yyyy:MM:dd HH:mm:ss");
+                        }
+                        res = cal;                     
+                    } else {
+                        String s = ((String) src);
+                        res = s;
+                    }
+                } else if (clazz.equals(Date.class)) {
+                    if (propType != PropertyType.DATETIME)
+                        throw new MapperException("Tag value has type Date and expected CMIS DateTime, but found: " + propType + " for tag: " + tag);
+                    // convert to a String
+                    Date date = ((Date) src);
+                    // convert date to GreogorianCalendar as CMIS expects
+                    GregorianCalendar  cal= new GregorianCalendar();
+                    cal.setTime(date);
+                    res = cal;                                        
+                } else if (clazz.equals(Boolean.class)) {
+                    if (propType != PropertyType.BOOLEAN)
+                        throw new MapperException("Tag value has type Boolean and expected CMIS Boolean, but found: " + propType + " for tag: " + tag);
+                    // convert to a String
+                    Boolean b = ((Boolean) src);
+                    res = b;                                        
+                } else {
+                    LOG.debug("Tag value has unsupported type: " + clazz.getName() + " for EXIF tag: " + tag);
+                    // throw new MapperException("Tag value has unsupported type: " + clazz.getName() + " for tag: " + tag);
+                }                
+            }            
+        } else if (JpegDirectory.class.equals(dir.getClass())) {
+            // is there a property mapped to this tag?
+            String propId = propMapJpeg.get(hexStr);
+            LOG.debug("Found JPEG tag '" + tag + "\', property mapped is: " + propId);
+
+            if (null == propId) {
+                LOG.info("Ignoring JPEG tag '" + tag + "\' no property mapped to this tag.");
+            } else if (propType == null) {
+                // should not happen and is a configuration error: we have a property id but no type
+                LOG.error("Ignoring JPEG tag '" + tag + "\' no property type mapped to this tag.");
+            } else {
+                Object src = dir.getObject(tag.getTagType());        
+                Class<?> clazz = src.getClass();
+
+                if (clazz.equals(Integer.class)) {
+                    if (propType != PropertyType.INTEGER)
+                        throw new MapperException("Tag value has type Integer and expected CMIS Integer, but found: " + propType + " for tag: " + tag);
+                    // convert to a long
+                    long l = ((Integer) src).longValue();
+                    res = l;                                        
+                } else {
+                    LOG.debug("Tag value has unsupported type: " + clazz.getName() + " for JPEG tag: " + tag);
+                }
+            }            
+        }
+            
+        return res;        
+    }
+    
+    private static Object convertGps(Tag tag, Directory dir, boolean mustInvert) {
+        Double res = null; 
+        Object src = dir.getObject(tag.getTagType());
+        
+        Class<?> stringArrayClass = src.getClass();
+        Class<?> stringArrayComponentType = stringArrayClass.getComponentType();
+        if (!stringArrayClass.isArray() || null == stringArrayComponentType || Array.getLength(src) != 3) 
+            throw new MapperException("GPS coordinate \"" + tag + "\" has unknown type.");
+        if (!stringArrayComponentType.equals(Rational.class))
+            throw new MapperException("GPS coordinate \"" + tag + "\" has unknown component type (expected Rational, found: " + 
+                    stringArrayComponentType.getName() + ")");
+        // do conversion
+        Rational[] components;
+        components = (Rational[]) src;
+        int deg = components[0].intValue();
+        double min = components[1].doubleValue();
+        double sec = components[2].doubleValue();
+        Double d = (deg + min / 60 + sec / 3600);
+        if (d > 0.0 && mustInvert)
+            d = -d;
+        res = d;
+        return res;
+    }
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperTika.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperTika.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperTika.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/mapper/PropertyMapperTika.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.mapper;
+
+import java.text.DateFormatSymbols;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+import org.apache.chemistry.opencmis.commons.enums.Cardinality;
+import org.apache.chemistry.opencmis.commons.enums.PropertyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertyMapperTika extends AbstractPropertyMapper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PropertyMapperTika.class.getName());
+    
+    private Map<String, String> propMapTags = new HashMap<String, String> (); // tag to property id
+    private Map<String, String> tokenizerMap = new HashMap<String, String> (); // tag to tokenizer regexp
+
+    public PropertyMapperTika() {
+        reset();
+    }
+    
+    public boolean initialize(String cfgPrefix, String typeKey, Properties properties) {
+        super.initialize(cfgPrefix, typeKey, properties);
+        buildIdMap(typeKey, properties);
+        
+        return true;
+    }
+    
+    public void reset() {
+    }
+    
+    public String getMappedPropertyId(String key) {
+        String propId = propMapTags.get(key);
+        return propId;
+    }
+
+    public Object convertValue(String key, PropertyDefinition<?> propDef, String strValue) {
+        Object value = null;
+        PropertyType pt = propDef.getPropertyType();
+        
+        if (null == pt)
+            value = null;
+        else if (null != strValue && strValue.length() > 0) {
+            // Tika has a bug and sometimes fails to parse MP3 tags, then generates '\0' in String
+            // see https://issues.apache.org/jira/browse/TIKA-887
+            int lastIllegalPos = -1;
+            for (int i=0; i<strValue.length(); i++) {
+              int c = strValue.codePointAt(i);
+              if (Character.isISOControl(c))
+                  lastIllegalPos = i;                  
+            }
+            if (lastIllegalPos >= 0)
+                strValue = strValue.substring(lastIllegalPos+1); // use remaining part after illegal char
+
+            switch (pt) {
+            case STRING:
+            case HTML:
+            case URI:
+            case ID:
+                
+                if (propDef.getCardinality() == Cardinality.SINGLE)
+                    value = strValue;
+                else {
+                    String tokenizer = tokenizerMap.containsKey(key) ? tokenizerMap.get(key) : "\\W";
+                    String[] result = strValue.split(tokenizer);
+                    List<String> valList = new ArrayList<String>();
+                    for (String s : result)
+                        valList.add(s.trim());
+                    value = valList;
+                }
+                break;
+            case INTEGER:
+                if (propDef.getCardinality() == Cardinality.SINGLE)
+                    value = Integer.valueOf(strValue);
+                else {
+                        String tokenizer = tokenizerMap.containsKey(key) ? tokenizerMap.get(key) : "\\W";
+                        String[] result = strValue.split(tokenizer);
+                        List<Integer> valList = new ArrayList<Integer>();
+                        for (String s : result)
+                            valList.add(Integer.valueOf(s.trim()));
+                        value = valList;
+                    }
+                break;
+            case DECIMAL:
+                if (propDef.getCardinality() == Cardinality.SINGLE)
+                    value = Double.valueOf(strValue);                
+                else {
+                    String tokenizer = tokenizerMap.containsKey(key) ? tokenizerMap.get(key) : "[\\s;:]";                        
+                    String[] result = strValue.split(tokenizer);
+                        List<Double> valList = new ArrayList<Double>();
+                        for (String s : result)
+                            valList.add(Double.valueOf(s.trim()));
+                        value = valList;
+                    }
+                break;
+            case DATETIME:
+                try {
+                    SimpleDateFormat sdf = new SimpleDateFormat(dateFormat, new DateFormatSymbols(Locale.US));
+                    if (propDef.getCardinality() == Cardinality.SINGLE) {
+                        Date date = sdf.parse(strValue);
+                        GregorianCalendar cal = new GregorianCalendar();
+                        cal.setTime(date);
+                        value = date;
+                    } else {
+                        String tokenizer = tokenizerMap.containsKey(key) ? tokenizerMap.get(key) : "[;,:]";                        
+                        String[] result = strValue.split(tokenizer);
+                        List<GregorianCalendar> valList = new ArrayList<GregorianCalendar>();
+                        for (String s : result) {
+                            Date date = sdf.parse(s.trim());
+                            GregorianCalendar cal = new GregorianCalendar();
+                            cal.setTime(date);
+                            valList.add(cal);
+                        }
+                        value = valList;
+                    }
+                } catch (ParseException e) {
+                    LOG.error("Could not parse date: " + strValue + " (check date format");
+                    LOG.error(e.toString(), e);
+                    value = null;
+                    e.printStackTrace();
+                }
+                break;
+            default:
+                throw new MapperException("unknown property type " + pt);
+            }            
+        }
+        return value;
+    }
+    
+    void buildIdMap(String typeKey, Properties properties) {
+        Set<String> keys = properties.stringPropertyNames(); 
+        String prefix = propPrefix + ".id.";
+        String tokenizerPrefix = propPrefix + ".tokenizer.";
+
+        for (String key : keys) {
+            if (key.startsWith(prefix)) {
+                String id = key.substring(prefix.length());
+                String cmisPropId = properties.getProperty(key).trim();
+                if (null == cmisPropId)
+                    throw new MapperException("Configuration key " + key + " must have a value assigned");
+                LOG.debug("Found mapping for type " + typeKey + " with " + id + " to " + cmisPropId);
+                propMapTags.put(id,  cmisPropId);
+            }
+            if (key.startsWith(tokenizerPrefix)) {
+                String id = key.substring(tokenizerPrefix.length());
+                String regex = properties.getProperty(key).trim();
+                if (null == regex)
+                    throw new MapperException("Configuration key " + key + " must have a value assigned");
+                LOG.debug("Found tokenizer mapping for property " + id + " to " + regex);
+                tokenizerMap.put(id, regex);
+            }
+        }
+    }
+    
+    int getSize() {
+        return propMapTags.size();
+    }
+    
+ }

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/AbstractMetadataParser.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/AbstractMetadataParser.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/AbstractMetadataParser.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/AbstractMetadataParser.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.parser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.tools.mapper.MapperException;
+import org.apache.chemistry.opencmis.tools.mapper.PropertyMapper;
+
+public abstract class AbstractMetadataParser implements MetadataParser {
+    
+    // private static final Logger LOG = LoggerFactory.getLogger(AbstractMetadataParser.class.getName());
+
+    protected Map<String, Object> cmisProperties;
+    protected PropertyMapper mapper = null;
+    
+    protected AbstractMetadataParser() {
+    }
+
+    public void initialize(PropertyMapper mapper, String contentType) {
+        this.mapper = mapper;
+        reset();
+    }
+
+    public Map<String, Object> getCmisProperties() {
+        return cmisProperties;
+    }
+    
+    public void reset() {
+        String typeId = mapper.getMappedTypeId();
+        cmisProperties = new HashMap<String, Object>();
+        mapper.reset();
+
+        if (null == typeId)
+            throw new MapperException("No CMIS Type configured in this parser.");
+        cmisProperties.put(PropertyIds.OBJECT_TYPE_ID, typeId);
+    }
+    
+    public String[] getContentTypes() {
+        return mapper.getContentTypes();
+    }
+    
+    public String getMappedTypeId() {
+        return mapper.getMappedTypeId();
+    }
+    
+    public PropertyMapper getMapper() {
+        return mapper;
+    }
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParser.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParser.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParser.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParser.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.parser;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.tools.mapper.MapperException;
+import org.apache.chemistry.opencmis.tools.mapper.PropertyMapper;
+
+/**
+ * This interface is used to extract metadata from content. An instance is 
+ * responsible to parse the content and extract the metadata. The metadata
+ * must be stored in a CMIS property bag. Implementations of this class are
+ * created by the Configurator depending on the Content-Type to parse and
+ * the corresponding parser configuration
+ * 
+ * @author Jens
+ *
+ */
+public interface MetadataParser {
+    
+    /**
+     * Initialize a parser with a given property mapper and a content type
+     * @param mapper
+     *      PropertyMapper used to map tags to properties and convert values
+     * @param contentType
+     *      content type of file to extract
+     */
+    void initialize(PropertyMapper mapper, String contentType);
+    
+    /**
+     * get ready for parsing a new file
+     */
+    void reset();
+    
+    /**
+     * Parse a file and extract all metadata and store them in a CMIS property bag
+     * @param f
+     *      file to parse
+     * @throws MapperException
+     */
+    void extractMetadata(File f, TypeDefinition td) throws MapperException;
+    
+    /**
+     * Return all found metadata, called after parsing is completed.
+     * @return
+     *      extracted CMIS properties
+     */
+    Map<String, Object> getCmisProperties();
+    
+    /**
+     * get all content types handled by this parser
+     * @return
+     *      array with content types
+     */
+    String[] getContentTypes();
+    
+    /**
+     * get the CMIS type id used by this type
+     * @return
+     *      CMIS type id
+     */
+    String getMappedTypeId();
+    
+    /**
+     * get the associated property mapper for this CMIS type
+     * 
+     * @return
+     *      property mapper
+     */
+    PropertyMapper getMapper();
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserExif.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserExif.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserExif.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserExif.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.parser;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.tools.mapper.MapperException;
+import org.apache.chemistry.opencmis.tools.mapper.PropertyMapperExif;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.drew.imaging.ImageMetadataReader;
+import com.drew.imaging.ImageProcessingException;
+import com.drew.metadata.Directory;
+import com.drew.metadata.Tag;
+
+/**
+ * A parser implementation using a lower level interface to get more control about the 
+ * EXIF tags than Tika provides
+ * 
+ * @author Jens
+ *
+ */
+public class MetadataParserExif extends AbstractMetadataParser  {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MetadataParserExif.class.getName());
+    
+    public void extractMetadata(File f, TypeDefinition td) throws MapperException {
+        
+        reset();
+        
+        // see  http://code.google.com/p/metadata-extractor/wiki/GettingStarted
+        try {
+            com.drew.metadata.Metadata metadata = ImageMetadataReader.readMetadata(f);
+            Iterator<?> it = metadata.getDirectoryIterator();
+            while (it.hasNext()) {
+                Directory directory = (com.drew.metadata.Directory) it.next();
+                Iterator<?> tagIt = directory.getTagIterator();
+                while (tagIt.hasNext()) {
+                    Tag tag = (Tag) tagIt.next();
+                    Object o = directory.getObject(tag.getTagType());
+                    LOG.debug("Tag: " + tag + ", value: " + o + ", class: " + o.getClass() + 
+                            ", tag type: " + tag.getTagType() + ", hex-value: " + tag.getTagTypeHex());
+                    if (null != cmisProperties) {
+                        ((PropertyMapperExif)mapper).mapTagAndConvert(directory, tag, td);
+                    }
+                }
+            }
+            Map<String, Object> props = ((PropertyMapperExif)mapper).getMappedProperties();
+            cmisProperties.putAll(props);
+        } catch (ImageProcessingException e) {            
+            LOG.error(e.toString(), e);
+        }
+
+    }
+
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserTika.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserTika.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserTika.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/parser/MetadataParserTika.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.parser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.enums.PropertyType;
+import org.apache.chemistry.opencmis.tools.mapper.MapperException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A metadata parser using the Apache Tika library
+ * @author Jens
+ *
+ */
+public class MetadataParserTika extends AbstractMetadataParser {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MetadataParserTika.class.getName());
+
+    public MetadataParserTika() {        
+    }
+    
+    public void extractMetadata(File f, TypeDefinition td) throws MapperException {
+        try {
+            InputStream stream = new FileInputStream(f);
+            Metadata metadata = new Metadata();
+            ContentHandler handler = new DefaultHandler();
+            Parser parser = new AutoDetectParser(); 
+            ParseContext context = new ParseContext();
+            parser.parse(stream, handler, metadata, context);
+
+            reset();
+            
+            for (String key : metadata.names()) {
+                String val = metadata.get(key);
+                LOG.debug("Found metadata \'" + key + "\': " + val);      
+                if (null != cmisProperties) {
+                    String propertyId = mapper.getMappedPropertyId(key);
+                    if (null != propertyId && null != val) {
+                        if (td != null) {
+                            PropertyDefinition<?> propDef = td.getPropertyDefinitions().get(propertyId);
+                            if (null == propDef)
+                                throw new MapperException("Mapping error: unknown property "+ propertyId + " in type definition " + td.getId());
+                            PropertyType propertyType = propDef.getPropertyType();
+                            Object convVal = mapper.convertValue(propertyId, propDef, val);
+                            if (null != convVal)
+                                cmisProperties.put(propertyId, convVal);
+                        } else
+                            cmisProperties.put(propertyId, val); // omit conversion if no type definition is available
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            LOG.error(e.toString(), e);
+            throw new MapperException("Extracting metadata failed for file " + f.getAbsolutePath(), e);
+        }
+    }    
+    
+    public void listMetadata(File f) throws MapperException {
+        try {
+            InputStream stream = new FileInputStream(f);
+            Metadata metadata = new Metadata();
+            ContentHandler handler = new DefaultHandler();
+            Parser parser = new AutoDetectParser(); 
+            ParseContext context = new ParseContext();
+            parser.parse(stream, handler, metadata, context);
+
+            for (String key : metadata.names()) {
+                String val = metadata.get(key);
+                LOG.info("Found metadata \'" + key + "\': " + val);      
+            }
+
+        } catch (Exception e) {
+            LOG.error(e.toString(), e);
+            throw new MapperException("Extracting metadata failed, file not found: " + f.getAbsolutePath(), e);
+        }
+    } 
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/specexamples/Main.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/specexamples/Main.java?rev=1373426&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/specexamples/Main.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-test/chemistry-opencmis-test-tools/src/main/java/org/apache/chemistry/opencmis/tools/specexamples/Main.java Wed Aug 15 14:07:56 2012
@@ -0,0 +1,786 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.tools.specexamples;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.chemistry.opencmis.client.bindings.CmisBindingFactory;
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.data.Ace;
+import org.apache.chemistry.opencmis.commons.data.Acl;
+import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
+import org.apache.chemistry.opencmis.commons.data.ContentStream;
+import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
+import org.apache.chemistry.opencmis.commons.data.Properties;
+import org.apache.chemistry.opencmis.commons.data.PropertyData;
+import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
+import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.BindingType;
+import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
+import org.apache.chemistry.opencmis.commons.enums.VersioningState;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.BindingsObjectFactoryImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.CmisExtensionElementImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ExtensionDataImpl;
+import org.apache.chemistry.opencmis.commons.spi.AclService;
+import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
+import org.apache.chemistry.opencmis.commons.spi.CmisBinding;
+import org.apache.chemistry.opencmis.commons.spi.DiscoveryService;
+import org.apache.chemistry.opencmis.commons.spi.Holder;
+import org.apache.chemistry.opencmis.commons.spi.MultiFilingService;
+import org.apache.chemistry.opencmis.commons.spi.NavigationService;
+import org.apache.chemistry.opencmis.commons.spi.ObjectService;
+import org.apache.chemistry.opencmis.commons.spi.RepositoryService;
+import org.apache.chemistry.opencmis.commons.spi.VersioningService;
+import org.apache.commons.io.filefilter.WildcardFileFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Main {
+
+    private static final String MULTIFILED_DOCUMENT = "MultifiledDocument";
+	private static final String MULTIFILED_FOLDER_2 = "MultifiledFolder2";
+	private static final String MULTIFILED_FOLDER_1 = "MultifiledFolder1";
+	private static final Logger LOG = LoggerFactory.getLogger(Main.class.getName());
+    private static final BigInteger TYPE_DEPTH_ALL = BigInteger.valueOf(-1);
+    private static final BigInteger MAX_ITEMS = null;
+    private static final BigInteger SKIP_COUNT = BigInteger.valueOf(0);
+    private static final String TOPLEVEL_TYPE = "DocumentTopLevel";
+    private static final String VERSIONED_TYPE = "VersionableType";
+    private static final String VERSIONED_PROP = "VersionedStringProp";
+    private static String LOGDIR = System.getProperty("java.io.tmpdir");// + File.separator;
+    private static String ROOT_URL = "http://localhost:8080/inmemory"; 
+    private static String ROOT_URL_OASIS = "http://www.example.com:8080/inmemory"; // required by OASIS rules, add this host to your hosts file
+    static int NO_FILES_LOGGED = 0;
+
+    private String targetDir = System.getProperty("java.io.tmpdir");// + File.separator;
+    private BindingsObjectFactory objFactory = new BindingsObjectFactoryImpl();
+    private BindingType bindingType;
+    private String rootFolderId;
+    private String repositoryId;
+    private ObjectService objSvc;
+    private NavigationService navSvc;
+    private RepositoryService repSvc;
+    private VersioningService verSvc;
+    private MultiFilingService multiSvc;
+    private DiscoveryService discSvc;
+    private AclService aclSvc;
+    
+    private List<String> idsToDelete = new ArrayList<String>();
+    private String multiFiledDoc;
+    private String multiFiledFolder1;
+    private String multiFiledFolder2;
+	private String changeToken;
+    
+    private static final String[] URLS = {ROOT_URL + "/atom", 
+        ROOT_URL + "/services", 
+        ROOT_URL + "/browser"};
+    private static final BindingType[] BINDINGS = {BindingType.ATOMPUB, BindingType.WEBSERVICES, BindingType.BROWSER};
+
+    public Main() {
+    }
+
+    public void runAllBindings() {
+    	cleanLogFilterDir(); // delete directory where Logging filter writes to ensure not to include unwanted files        
+
+    	for (int i = 0; i < BINDINGS.length; i++) {
+    		bindingType = BINDINGS[i];
+    		init(URLS[i], BINDINGS[i]);
+    		run();
+    	}
+    	String dirs[] = {BindingType.ATOMPUB.value(), BindingType.WEBSERVICES.value(), BindingType.BROWSER.value() };        
+    	createZipFile("CMIS-Spec-Examples.zip", dirs);
+     
+    }
+    
+    public void run() {
+        LOG.debug("Generating spec examples for Binding: " + bindingType.value());
+        
+        try {
+        // Repository Service:
+        getRepositories();
+
+        repositoryId = "A1";
+        getRepositoryInfo(); // get root folder id here!
+
+        getTypeDefinition("cmis:folder");
+        
+        String docId = getTestDocId();
+        String folderId = getTestFolderId();
+
+        getTypeChildren(TOPLEVEL_TYPE);
+
+        // Navigation Service:
+        getChildren(folderId);
+        getDescendants(folderId);
+        getObjectParents(folderId);
+        removeObjectFromFolder();
+
+        // Object Service:
+        getObject(docId);
+        getAcl(docId);
+        String id1 = createDocument("SampleDocument", TOPLEVEL_TYPE, rootFolderId, VersioningState.NONE);
+        updateProperties(id1, PropertyIds.NAME, "RenamedDocument");
+        getAllowableActions(id1);
+        
+        deleteObject(id1);
+
+        // Discovery Service:
+        doQuery();
+        getContentChanges(changeToken);
+
+        // Versioning Service
+        String id2 = prepareVersionSeries("VersionedDocument", VERSIONED_TYPE, rootFolderId);
+        checkOut(id2);
+        checkIn(id2, true, "final version in series");
+        getAllVersions(id2);
+
+        // collect all captured files and store them in a ZIP file
+        } catch (Exception e) {
+        	LOG.error("Failed to create spec examples: ", e);
+        } 
+
+        // delete all generated objects
+        cleanup();
+    }
+
+    private void init(String url, BindingType bindingType) {
+        LOG.debug("Initializing connection to InMemory server: ");
+        LOG.debug("   Binding: " + bindingType.value());
+        LOG.debug("   URL: " + url);
+
+        Map<String, String> parameters = new HashMap<String, String>();
+        parameters.put(SessionParameter.USER, "admin");
+        parameters.put(SessionParameter.PASSWORD, "admin");
+
+        parameters.put(SessionParameter.BINDING_TYPE, bindingType.value());
+
+        // get factory and create binding
+        CmisBindingFactory factory = CmisBindingFactory.newInstance();
+        CmisBinding binding = null;
+        
+        if (bindingType == BindingType.ATOMPUB)  {
+            parameters.put(SessionParameter.ATOMPUB_URL, url);
+            binding = factory.createCmisAtomPubBinding(parameters);
+        } else if (bindingType == BindingType.WEBSERVICES) {
+            parameters.put(SessionParameter.WEBSERVICES_ACL_SERVICE, url + "/ACLService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_DISCOVERY_SERVICE,  url + "/DiscoveryService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_MULTIFILING_SERVICE,  url + "/MultiFilingService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_NAVIGATION_SERVICE,  url + "/NavigationService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_OBJECT_SERVICE,  url + "/ObjectService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_POLICY_SERVICE,  url + "/PolicyService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_RELATIONSHIP_SERVICE,  url + "/RelatinshipService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_REPOSITORY_SERVICE,  url + "/RepositoryService?wsdl");
+            parameters.put(SessionParameter.WEBSERVICES_VERSIONING_SERVICE,  url + "/VersioningService?wsdl");
+            binding = factory.createCmisWebServicesBinding(parameters);            
+        } else if (bindingType == BindingType.BROWSER) {
+            parameters.put(SessionParameter.BROWSER_URL, url); 
+            binding = factory.createCmisBrowserBinding(parameters);            
+        } else {
+            LOG.error("Unknown binding type: " + bindingType.value());
+            return;
+        }
+        objFactory = binding.getObjectFactory();
+        repSvc = binding.getRepositoryService();
+        objSvc = binding.getObjectService();
+        navSvc = binding.getNavigationService();
+        verSvc = binding.getVersioningService();
+        multiSvc = binding.getMultiFilingService();
+        discSvc = binding.getDiscoveryService();
+        aclSvc = binding.getAclService();
+        
+        // create a folder where target files will be stored:
+        targetDir = bindingType.value();
+        File in = new File(targetDir);
+        deleteDirRecursive(in); // avoid that there are unwanted files from previous runs
+        boolean ok = in.mkdir();
+        
+        LOG.debug("creating target directory for files: " + ok);
+        LOG.debug("Initializing done. ");
+    }
+
+    private void getRepositories() {
+        LOG.debug("getRepositories()");
+        List<RepositoryInfo> repositories = repSvc.getRepositoryInfos(null);
+        this.repositoryId = repositories.get(0).getId();
+        renameFiles("getRepositoryInfos");
+        LOG.debug("getRepositoryInfo() done.");
+    }
+
+    private void getRepositoryInfo() {
+        LOG.debug("getting repository info for repository " + repositoryId);
+        
+        // Because some bindings silently retrieve all repositories on the first request we call it twice
+        // and use a dummy extension data element to prevent caching
+        RepositoryInfo repoInfo = repSvc.getRepositoryInfo(repositoryId, null);
+        ExtensionDataImpl dummyExt = new ExtensionDataImpl();
+        @SuppressWarnings("serial")
+		List<CmisExtensionElement> extList = new ArrayList<CmisExtensionElement>() {{ add(new CmisExtensionElementImpl("foo", "foo", null, "bar")); }};
+        dummyExt.setExtensions(extList);
+        repoInfo = repSvc.getRepositoryInfo(repositoryId, dummyExt);
+
+        LOG.debug("repository id is: " + repoInfo.getId());
+        rootFolderId = repoInfo.getRootFolderId();
+        changeToken = repoInfo.getLatestChangeLogToken();
+        LOG.debug("root folder id is: " + repoInfo.getRootFolderId());
+        renameFiles("getRepositoryInfo");
+        LOG.debug("getRepositoryInfo() done.");
+    }
+
+    private void getObject(String objectId) {
+        LOG.debug("getObject " + objectId);
+        objSvc.getObject(repositoryId, objectId, "*", true /* includeAllowableActions */,
+                IncludeRelationships.NONE /* includeRelationships */, null /* renditionFilter */,
+                false /* includePolicyIds */, true /* includeAcl */, null);
+        renameFiles("getObject");
+        LOG.debug("getObject() done.");
+    }
+
+    private void getChildren(String folderId) {
+        LOG.debug("getChildren " + folderId);
+        navSvc.getChildren(repositoryId, folderId, "*", null /* orderBy */, true /* includeAllowableActions */,
+                IncludeRelationships.NONE, null /* renditionFilter */, true /* includePathSegment */, MAX_ITEMS,
+                SKIP_COUNT, null);
+        renameFiles("getChildren");
+        LOG.debug("getChildren() done.");
+    }
+
+    private void getDescendants(String folderId) {
+        final BigInteger DEPTH = BigInteger.valueOf(3);
+        LOG.debug("getDescendants " + folderId);
+        navSvc.getDescendants(repositoryId, folderId, DEPTH, "*", true /* includeAllowableActions */,
+                IncludeRelationships.NONE, null /* renditionFilter */, true /* includePathSegment */, null);
+        renameFiles("getDescendants");
+        LOG.debug("getDescendants() done.");
+    }
+    
+    private void getObjectParents(String folderId) {
+    	// get object parents first add object to two folders then get parents
+        LOG.debug("getObjectsParents " + folderId);
+        multiFiledFolder1 = createFolderIntern(MULTIFILED_FOLDER_1, BaseTypeId.CMIS_FOLDER.value(), folderId);
+        idsToDelete.add(multiFiledFolder1);
+        multiFiledFolder2 = createFolderIntern(MULTIFILED_FOLDER_2, BaseTypeId.CMIS_FOLDER.value(), folderId);
+        idsToDelete.add(multiFiledFolder2);
+        multiFiledDoc = createDocumentIntern(MULTIFILED_DOCUMENT, BaseTypeId.CMIS_DOCUMENT.value(), multiFiledFolder1, VersioningState.NONE);
+        idsToDelete.add(0, multiFiledDoc); // add at the beginning must be removed before folders!
+    	multiSvc.addObjectToFolder(repositoryId, multiFiledDoc, multiFiledFolder2, true, null);
+    	navSvc.getObjectParents(repositoryId, multiFiledDoc, "*", false, IncludeRelationships.NONE, null, true, null);
+        renameFiles("getObjectParents");
+        LOG.debug("getObjectParents() done.");
+    }
+    
+    private void removeObjectFromFolder() {
+        LOG.debug("removeObjectFromFolder");
+        multiSvc.removeObjectFromFolder(repositoryId, multiFiledDoc, multiFiledFolder2, null);
+    	renameFiles("removeObjectFromFolder");
+    	try {
+			Thread.sleep(200);
+		} catch (InterruptedException e) {
+
+		}
+        LOG.debug("removeObjectFromFolder() done.");    	
+    }
+
+    private void doQuery() {
+        LOG.debug("doQuery ");
+        String statement = "SELECT * from cmis:document WHERE IN_FOLDER('" + rootFolderId + "')";
+        discSvc.query(repositoryId, statement, false /* searchAllVersions */, true /* includeAllowableActions */,
+                IncludeRelationships.NONE, null, MAX_ITEMS, SKIP_COUNT, null);
+        renameFiles("doQuery");
+        LOG.debug("doQuery() done.");
+    }
+
+    private void getContentChanges(String token) {    	
+        LOG.debug("getContentChanges");
+        Holder<String> changeLogToken = new Holder<String>("token");
+		discSvc.getContentChanges(repositoryId, changeLogToken, false, "*", false, false, null, null);
+    	renameFiles("getContentChanges");
+        LOG.debug("getContentChanges() done.");    	
+    }
+
+    private void getTypeChildren(String typeId) {
+        LOG.debug("getTypeChildren " + typeId);
+        repSvc.getTypeChildren(repositoryId, typeId, true /* includePropertyDefinitions */, MAX_ITEMS, SKIP_COUNT, null);
+        renameFiles("getTypeChildren");
+        LOG.debug("getTypeChildren() done.");
+    }
+
+    private String createDocument(String name, String typeId, String folderId, VersioningState versioningState) {
+        LOG.debug("createDocument " + typeId);
+
+        String id = createDocumentIntern(name, typeId, folderId, versioningState);
+        renameFiles("createDocument");
+        LOG.debug("createDocument() done.");
+
+        return id;
+    }
+
+    private String createDocumentIntern(String name, String typeId, String folderId, VersioningState versioningState) {
+        ContentStream contentStream = null;
+        List<String> policies = null;
+        Acl addACEs = null;
+        Acl removeACEs = null;
+        ExtensionsData extension = null;
+
+        List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
+        properties.add(objFactory.createPropertyIdData(PropertyIds.NAME, name));
+        properties.add(objFactory.createPropertyIdData(PropertyIds.OBJECT_TYPE_ID, typeId));
+        Properties props = objFactory.createPropertiesData(properties);
+
+        contentStream = createContent();
+
+        String id = null;
+        try {
+            id = objSvc.createDocument(repositoryId, props, folderId, contentStream, versioningState, policies, addACEs,
+                    removeACEs, extension);
+        } catch (CmisBaseException e) {
+        	// folder already there, get it:
+            ObjectInFolderList result = navSvc.getChildren(repositoryId, folderId, "*", null, false,
+                    IncludeRelationships.NONE, null, true, MAX_ITEMS, SKIP_COUNT, null);
+
+            List<ObjectInFolderData> children = result.getObjects();
+            LOG.debug(" found " + children.size() + " folders in getChildren()");
+            for (ObjectInFolderData child : children) {
+            	String nameChild = (String) child.getObject().getProperties().getProperties().get(PropertyIds.NAME).getFirstValue();
+                if (name.equals(nameChild))
+                    return child.getObject().getId();
+            }
+        }
+        return id;
+    }
+
+    private String createFolderIntern(String name, String typeId, String parentFolderId) {
+        List<String> policies = null;
+        Acl addACEs = null;
+        Acl removeACEs = null;
+        ExtensionsData extension = null;
+
+        List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
+        properties.add(objFactory.createPropertyIdData(PropertyIds.NAME, name));
+        properties.add(objFactory.createPropertyIdData(PropertyIds.OBJECT_TYPE_ID, typeId));
+        Properties props = objFactory.createPropertiesData(properties);
+
+        String id = null;
+        try {
+        id = objSvc.createFolder(repositoryId, props, parentFolderId, policies, addACEs,
+                removeACEs, extension);
+        } catch (CmisBaseException e) {
+        	// folder already there, get it:
+            ObjectInFolderList result = navSvc.getChildren(repositoryId, parentFolderId, "*", null, false,
+                    IncludeRelationships.NONE, null, true, MAX_ITEMS, SKIP_COUNT, null);
+
+            List<ObjectInFolderData> children = result.getObjects();
+            LOG.debug(" found " + children.size() + " folders in getChildren()");
+            for (ObjectInFolderData child : children) {
+            	String nameChild = (String) child.getObject().getProperties().getProperties().get(PropertyIds.NAME).getFirstValue();
+                if (name.equals(nameChild))
+                    return child.getObject().getId();
+            }
+        }
+        return id;
+    }
+
+    private ContentStream createContent() {
+        ContentStreamImpl content = new ContentStreamImpl();
+        content.setFileName("data.txt");
+        content.setMimeType("text/plain");
+        int len = 32 * 1024;
+        byte[] b = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x0c, 0x0a,
+                0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x0c, 0x0a }; // 32
+        // Bytes
+        ByteArrayOutputStream ba = new ByteArrayOutputStream(len);
+        try {
+            for (int i = 0; i < 1024; i++) {
+                ba.write(b);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to fill content stream with data", e);
+        }
+        content.setStream(new ByteArrayInputStream(ba.toByteArray()));
+        content.setLength(BigInteger.valueOf(len));
+        return content;
+    }
+
+    private void updateProperties(String id, String propertyId, String propertyValue) {
+        LOG.debug("updateProperties " + id);
+        List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
+        properties.add(objFactory.createPropertyStringData(propertyId, propertyValue));
+        Properties newProps = objFactory.createPropertiesData(properties);
+
+        Holder<String> idHolder = new Holder<String>(id);
+        Holder<String> changeTokenHolder = new Holder<String>();
+        objSvc.updateProperties(repositoryId, idHolder, changeTokenHolder, newProps, null);
+        renameFiles("updateProperties");
+        LOG.debug("updateProperties() done.");
+    }
+
+    private void deleteObject(String id) {
+        LOG.debug("deleteObject " + id);
+        objSvc.deleteObject(repositoryId, id, true, null);
+        renameFiles("deleteObject");
+        LOG.debug("deleteObject() done.");
+    }
+
+    private void cleanup () {
+        LOG.debug("cleaning up...");
+        for (String id : idsToDelete) {
+            LOG.debug("deleting object " + id);
+            objSvc.deleteObject(repositoryId, id, true, null);            
+        }
+        idsToDelete.clear();
+        LOG.debug("... cleaning up done");
+    }
+    
+    /**
+     * enumerate the children of the root folder and return the id of the first
+     * document
+     * 
+     * @return id of first doc in root folder
+     */
+    private String getTestDocId() {
+        return getTestId(BaseTypeId.CMIS_DOCUMENT);
+    }
+
+    /**
+     * enumerate the children of the root folder and return the id of the first
+     * sub-folder
+     * 
+     * @return id of first doc in root folder
+     */
+    private String getTestFolderId() {
+        return getTestId(BaseTypeId.CMIS_FOLDER);
+    }
+
+    private String getTestId(BaseTypeId baseTypeId) {
+        LOG.debug("getTestDocId()");
+        ObjectInFolderList result = navSvc.getChildren(repositoryId, rootFolderId, "*", null, false,
+                IncludeRelationships.NONE, null, true, MAX_ITEMS, SKIP_COUNT, null);
+
+        List<ObjectInFolderData> children = result.getObjects();
+        LOG.debug(" found " + children.size() + " folders in getChildren()");
+        for (ObjectInFolderData child : children) {
+            if (baseTypeId.equals(child.getObject().getBaseTypeId()))
+                return child.getObject().getId();
+        }
+        return null;
+    }
+
+    private String prepareVersionSeries(String name, String typeId, String folderId) {
+        String id = createDocumentIntern(name, typeId, folderId, VersioningState.MAJOR);
+        Holder<Boolean> contentCopied = new Holder<Boolean>();
+        Holder<String> idHolder = new Holder<String>(id);
+
+        verSvc.checkOut(repositoryId, idHolder, null, contentCopied);
+        String checkinComment = "Checkin V2.0";
+        verSvc.checkIn(repositoryId, idHolder, true /*major*/, null /*properties*/, null /*content*/,
+                checkinComment, null/*policies*/, null/*addAcl*/, null /*removeAcl*/, null /*extension*/);
+
+        verSvc.checkOut(repositoryId, idHolder, null, contentCopied);
+        checkinComment = "Checkin V2.1";
+        verSvc.checkIn(repositoryId, idHolder, false /*major*/, null /*properties*/, null /*content*/,
+                checkinComment, null/*policies*/, null/*addAcl*/, null /*removeAcl*/, null /*extension*/);
+        
+        return idHolder.getValue();
+    }
+    
+    private void checkOut(String id) {
+        LOG.debug("checkOut()");        
+        Holder<String> idHolder = new Holder<String>(id);
+        Holder<Boolean> contentCopied = new Holder<Boolean>(true);
+        verSvc.checkOut(repositoryId, idHolder, null, contentCopied);
+        renameFiles("checkOut");
+        LOG.debug("checkOut done.");
+    }
+
+    private void checkIn(String id, boolean major, String checkinComment) {
+        LOG.debug("checkIn()");
+        List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
+        properties.add(objFactory.createPropertyStringData(VERSIONED_PROP, "updated value"));
+        Properties newProps = objFactory.createPropertiesData(properties);
+
+        Holder<String> idHolder = new Holder<String>(id);
+        verSvc.checkIn(repositoryId, idHolder, major /*major*/, newProps /*properties*/, null /*content*/,
+                checkinComment, null/*policies*/, null/*addAcl*/, null /*removeAcl*/, null /*extension*/);
+        renameFiles("checkIn");
+        LOG.debug("checkIn done.");
+    }
+
+    private void getAllVersions(String id) {
+        LOG.debug("getAllVersions()");     
+        verSvc.getAllVersions(repositoryId, id/* object id */, id/* series id */, "*"/* filter */,
+                false /* includeAllowableActions */, null /* extension */);
+        renameFiles("getAllVersions");
+        LOG.debug("getAllVersions done.");
+    }
+    
+    private void getAcl(String objectId) {
+        LOG.debug("getting Acl() " + objectId);
+
+        // get old ACL first:
+        Acl oldAcl = aclSvc.getAcl(repositoryId, objectId, true, null);
+
+        // create a new ACL for the test doc
+        List<Ace> aces = new ArrayList<Ace>();
+        aces.add(objFactory.createAccessControlEntry("Alice", Collections.singletonList("cmis:read")));
+        aces.add(objFactory.createAccessControlEntry("Bob", Collections.singletonList("cmis:write")));
+        aces.add(objFactory.createAccessControlEntry("admin", Collections.singletonList("cmis:all")));
+        Acl acl = objFactory.createAccessControlList(aces);
+
+        // add the new ACL and remove the old one
+        aclSvc.applyAcl(repositoryId, objectId, acl, oldAcl, AclPropagation.OBJECTONLY, null);
+            
+        aclSvc.getAcl(repositoryId, objectId, true, null);
+        renameFiles("getAcl");
+        LOG.debug("getting Acl() done.");
+    }
+
+    private void getTypeDefinition(String typeId) {
+        LOG.debug("getTypeDefinition " + typeId);
+        repSvc.getTypeDefinition(repositoryId, typeId, null);
+        renameFiles("getTypeDefinition");
+        LOG.debug("getTypeDefinition() done.");
+    }
+
+    private void getTypeDescendants(String typeId) {
+        LOG.debug("getTypeDescendants " + typeId);
+        repSvc.getTypeDescendants(repositoryId, typeId, TYPE_DEPTH_ALL, true /* includePropertyDefinitions */, null);
+        renameFiles("getTypeDescendants");
+        LOG.debug("getTypeDescendants() done.");
+    }
+
+    private void getAllowableActions(String objectId) {
+        LOG.debug("getAllowableActions " + objectId);
+        objSvc.getAllowableActions(repositoryId, objectId, null);
+        renameFiles("getAllowableActions");
+        LOG.debug("getAllowableActions() done.");
+    }
+
+    private void renameFiles(String name) {
+        String fileNameInReq = findLastFile(LOGDIR, "*-request.log");
+        String fileNameInResp = findLastFile(LOGDIR, "*-response.log");
+        if (null == fileNameInReq) {
+            LOG.error("Failed to find captured request file for " + name);
+            return;
+        }
+        if (null == fileNameInResp) {
+            LOG.error("Failed to find captured response file for " + name);
+            return;
+        }
+        File in = new File(fileNameInReq);
+        File out = new File(targetDir + File.separator + name + "-request.log");
+        if (out.exists())
+            out.delete();
+        boolean ok = in.renameTo(out);
+        if (ok)
+            LOG.debug("Renaming file " + in.getAbsolutePath() + " to " + out.getAbsolutePath() + " succeeded.");
+        else
+            LOG.warn("Renaming file " + in.getAbsolutePath() + " to " + out.getAbsolutePath() + " failed.");
+
+        in = new File(fileNameInResp);
+        out = new File(targetDir + File.separator + name + "-response.log");
+        if (out.exists())
+            out.delete();
+        ok = in.renameTo(out);
+        if (ok)
+            LOG.debug("Renaming file " + in.getAbsolutePath() + "to " + out.getAbsolutePath() + " succeeded.");
+        else
+            LOG.warn("Renaming file " + in.getAbsolutePath() + " to " + out.getAbsolutePath() + " failed.");
+    }
+    
+    private void createZipFile(String zipFileName, String[] dirs) {
+        
+        File out = new File(zipFileName);
+        if (out.exists())
+            out.delete();
+        
+        FileOutputStream fout = null;
+        ZipOutputStream zout =null;
+        try {
+            fout = new FileOutputStream(zipFileName);
+            zout = new ZipOutputStream(fout);
+            for (String dir: dirs) {
+                File dirToZip = new File(dir);
+                addDirectory(zout, dir, dirToZip);
+            }
+        } catch (Exception e) {
+            LOG.error("Creating ZIP file failed: " + e);
+        } finally {
+            try {
+                if (zout != null)
+                    zout.close();
+                if (fout != null)
+                    fout.close();
+            } catch (IOException e) {
+                LOG.error(e.toString(), e);
+            }
+        }
+    }
+    
+    private static void addDirectory(ZipOutputStream zout, String prefix, File sourceDir) throws IOException {
+        
+        File[] files = sourceDir.listFiles();
+        LOG.debug("Create Zip, adding directory " + sourceDir.getName());
+               
+        if (null != files) {
+            for(int i=0; i < files.length; i++)
+            {
+                if(files[i].isDirectory())
+                {
+                    addDirectory(zout, prefix + File.separator + files[i].getName(), files[i]);
+                } else {
+                    LOG.debug("Create Zip, adding file " + files[i].getName());
+                    byte[] buffer = new byte[65536];
+                    FileInputStream fin = new FileInputStream(files[i]);
+                    String zipEntryName = prefix + File.separator + files[i].getName();
+                    LOG.debug("   adding entry " + zipEntryName);
+                    zout.putNextEntry(new ZipEntry(zipEntryName));
+
+                    int length;
+                    while((length = fin.read(buffer)) > 0)
+                    {
+                        zout.write(buffer, 0, length);
+                    }
+
+                    zout.closeEntry();
+                    fin.close();
+                }
+            }      
+        }
+    }
+    
+    public static void clean() {
+        LOG.debug("Cleaning generated and captured request and response logs...");
+        
+        cleanFilesWithFilter(LOGDIR, "*-request.log");
+        cleanFilesWithFilter(LOGDIR, "*-response.log");
+        for (int i = 0; i < BINDINGS.length; i++) {
+            String dir = BINDINGS[i].value();
+            
+            cleanFilesWithFilter(dir, "*-request.log");
+            cleanFilesWithFilter(dir, "*-response.log");
+
+            File dirToDelete = new File (dir);
+            boolean ok = dirToDelete.delete();
+            if (ok)
+                LOG.debug("Deleting dir " + dirToDelete.getAbsolutePath() + " succeeded.");
+            else
+                LOG.warn("Deleting dir " + dirToDelete.getAbsolutePath() + " failed.");
+        }
+        new File("./target/logs/log4j.log").delete();
+        LOG.debug("... done.");        
+    }
+    
+    private static void cleanFilesWithFilter(String directoryPath, String wildcardFilter) {
+        File dir = new File(directoryPath);
+        FileFilter fileFilter = new WildcardFileFilter(wildcardFilter);
+        File[] files = dir.listFiles(fileFilter);
+        if (files != null)
+            for (int i = 0; i < files.length; i++) {
+                boolean ok = files[i].delete();
+                LOG.debug("Deleting file: " + files[i] + ", success: " + ok);
+            }        
+    }
+    
+    private static String findLastFile(String directoryPath, String wildcardFilter) {
+        File dir = new File(directoryPath);
+        FileFilter fileFilter = new WildcardFileFilter(wildcardFilter);
+        File[] files = dir.listFiles(fileFilter);
+        LOG.debug("Number of files in filter dir " + files.length);
+        if (files.length < NO_FILES_LOGGED) {
+        	LOG.warn("WARNING TOO FEW FILES!");
+        	// There might be some problem with disk caching, seems that listFiles
+        	// does not always get the most recent state, ugly workaround
+        	try {
+				Thread.sleep(250);
+			} catch (InterruptedException e) {
+			}
+        	files = dir.listFiles(fileFilter);
+            if (files.length < NO_FILES_LOGGED) 
+            	LOG.error("WARNING TOO FEW FILES EVEN AFTER SECOND TRY!!!");
+        }
+        NO_FILES_LOGGED = files.length;
+        Arrays.sort(files);
+        if (files.length == 0)
+            return null;
+        else
+            return files[files.length-1].getAbsolutePath();
+    }
+
+    public static void main(String[] args) {
+        if (args.length > 0 && args[0].equals("-clean")) {
+            LOG.debug("Cleaning up generated files...");
+            Main.clean();
+            LOG.debug("... cleaning up done.");
+        } else {
+            LOG.debug("Starting generating spec examples...");
+            Main main = new Main();
+            main.runAllBindings();
+            LOG.debug("... finished generating spec examples.");
+        }
+    }
+    
+    static private boolean deleteDirRecursive(File path) {
+        if( path.exists() ) {
+          File[] files = path.listFiles();
+          for(int i=0; i<files.length; i++) {
+             if(files[i].isDirectory()) {
+                 deleteDirRecursive(files[i]);
+             }
+             else {
+               files[i].delete();
+             }
+          }
+        }
+        return( path.delete() );
+      }
+
+    private void cleanLogFilterDir() {
+        File dir = new File(LOGDIR);
+        FileFilter fileFilter = new WildcardFileFilter("*-request.log");
+        File[] files = dir.listFiles(fileFilter);
+        for (File f : files) {
+            f.delete();
+        }
+        fileFilter = new WildcardFileFilter("*-response.log");
+        files = dir.listFiles(fileFilter);
+        for (File f : files) {
+            f.delete();
+        }
+    }
+}

Modified: chemistry/opencmis/trunk/pom.xml
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/pom.xml?rev=1373426&r1=1373425&r2=1373426&view=diff
==============================================================================
--- chemistry/opencmis/trunk/pom.xml (original)
+++ chemistry/opencmis/trunk/pom.xml Wed Aug 15 14:07:56 2012
@@ -191,7 +191,6 @@
         <module>chemistry-opencmis-server/chemistry-opencmis-server-fileshare</module>
         <module>chemistry-opencmis-server/chemistry-opencmis-server-jcr</module>
         <module>chemistry-opencmis-bridge/chemistry-opencmis-bridge</module>        
-        <module>chemistry-opencmis-test/chemistry-opencmis-test-client</module>
         <module>chemistry-opencmis-test/chemistry-opencmis-test-fit</module>
         <module>chemistry-opencmis-test/chemistry-opencmis-test-tck</module>
         <module>chemistry-opencmis-test/chemistry-opencmis-test-tools</module>