Author: cziegeler
Date: Tue Aug 31 09:50:02 2010
New Revision: 991134

URL: http://svn.apache.org/viewvc?rev=991134&view=rev
Log:
SLING-1687 : JobStatusProvider should lazy load events when returning

Added:
    
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
   (with props)
    
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
   (with props)
Modified:
    
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobStatusProvider.java
    
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/AbstractRepositoryEventHandler.java
    
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/EventHelper.java
    
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/JobEventHandler.java

Modified: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobStatusProvider.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobStatusProvider.java?rev=991134&r1=991133&r2=991134&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobStatusProvider.java
 (original)
+++ 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobStatusProvider.java
 Tue Aug 31 09:50:02 2010
@@ -37,7 +37,9 @@ public interface JobStatusProvider {
      * Return a list of currently scheduled jobs.
      * @param topic Topic can be used as a filter, if it is non-null, only 
jobs with this topic will be returned.
      * @return A non null collection.
+     * @deprecated Use {...@link #queryScheduledJobs(String, Map...)} instead.
      */
+    @Deprecated
     Collection<Event> getScheduledJobs(String topic);
 
     /**
@@ -45,7 +47,9 @@ public interface JobStatusProvider {
      * in the cluster, there could be more than one job in processing
      * @param topic Topic can be used as a filter, if it is non-null, only 
jobs with this topic will be returned.
      * @return A non null collection.
+     * @deprecated Use {...@link #queryCurrentJobs(String, Map...)} instead.
      */
+    @Deprecated
     Collection<Event> getCurrentJobs(String topic);
 
     /**
@@ -55,7 +59,9 @@ public interface JobStatusProvider {
      *                    must match the template (AND query). By providing 
several maps, different filters
      *                    are possible (OR query).
      * @return A non null collection.
+     * @deprecated Use {...@link #queryScheduledJobs(String, Map...)} instead.
      */
+    @Deprecated
     Collection<Event> getScheduledJobs(String topic, Map<String, Object>... 
filterProps);
 
     /**
@@ -66,7 +72,9 @@ public interface JobStatusProvider {
      *                    must match the template (AND query). By providing 
several maps, different filters
      *                    are possible (OR query).
      * @return A non null collection.
+     * @deprecated Use {...@link #queryCurrentJobs(String, Map...)} instead.
      */
+    @Deprecated
     Collection<Event> getCurrentJobs(String topic, Map<String, Object>... 
filterProps);
 
     /**
@@ -78,7 +86,9 @@ public interface JobStatusProvider {
      *                    must match the template (AND query). By providing 
several maps, different filters
      *                    are possible (OR query).
      * @return A non null collection.
+     * @deprecated Use {...@link #queryAllJobs(String, Map...)} instead.
      */
+    @Deprecated
     Collection<Event> getAllJobs(String topic, Map<String, Object>... 
filterProps);
 
     /**
@@ -152,4 +162,40 @@ public interface JobStatusProvider {
      * @param jobQueueName The name of the queue.
      */
     void wakeUpJobQueue(final String jobQueueName);
+
+    /**
+     * Return a list of currently scheduled jobs.
+     * @param topic Topic can be used as a filter, if it is non-null, only 
jobs with this topic will be returned.
+     * @param filterProps A list of filter property maps. Each map acts like a 
template. The searched job
+     *                    must match the template (AND query). By providing 
several maps, different filters
+     *                    are possible (OR query).
+     * @return A non null collection.
+     * @since 2.4
+     */
+    JobsIterator queryScheduledJobs(String topic, Map<String, Object>... 
filterProps);
+
+    /**
+     * Return the jobs which are currently in processing. If there are several 
application nodes
+     * in the cluster, there could be more than one job in processing
+     * @param topic Topic can be used as a filter, if it is non-null, only 
jobs with this topic will be returned.
+     * @param filterProps A list of filter property maps. Each map acts like a 
template. The searched job
+     *                    must match the template (AND query). By providing 
several maps, different filters
+     *                    are possible (OR query).
+     * @return A non null collection.
+     * @since 2.4
+     */
+    JobsIterator queryCurrentJobs(String topic, Map<String, Object>... 
filterProps);
+
+    /**
+     * Return all jobs either running or scheduled.
+     * This is actually a convenience method and collects the results from 
{...@link #getScheduledJobs(String, Map...)}
+     * and {...@link #getCurrentJobs(String, Map...)}
+     * @param topic Topic can be used as a filter, if it is non-null, only 
jobs with this topic will be returned.
+     * @param filterProps A list of filter property maps. Each map acts like a 
template. The searched job
+     *                    must match the template (AND query). By providing 
several maps, different filters
+     *                    are possible (OR query).
+     * @return A non null collection.
+     * @since 2.4
+     */
+    JobsIterator queryAllJobs(String topic, Map<String, Object>... 
filterProps);
 }

Added: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java?rev=991134&view=auto
==============================================================================
--- 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
 (added)
+++ 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
 Tue Aug 31 09:50:02 2010
@@ -0,0 +1,62 @@
+/*
+ * 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.sling.event;
+
+import java.util.Iterator;
+
+import org.osgi.service.event.Event;
+
+/**
+ * This <code>Iterator</code> allows to iterate over {...@link Event}s.
+ * In addition to an iterator it might return the number of elements
+ * in the collection and allows to skip several elements.
+ * If the iterator is not used to iterate through the whole collection
+ * of jobs, the {...@link #close()} method must be called in order to
+ * free resources!
+ */
+public interface JobsIterator extends Iterator<Event> {
+
+    /**
+     * Skip a number of jobs.
+     * @param skipNum the non-negative number of elements to skip
+     * @throws java.util.NoSuchElementException
+     *          if skipped past the last job in the iterator.
+     */
+    void skip(long skipNum);
+
+    /**
+     * Returns the total number of jobs. In some cases a precise information
+     * is not available. In these cases -1 is returned.
+     */
+    long getSize();
+
+    /**
+     * Returns the current position within the iterator. The number returned is
+     * the 0-based index of the next job.
+     */
+    long getPosition();
+
+    /**
+     * Releases this iterators resources immediately instead of waiting for 
this
+     * to happen when it is automatically closed. After a call to close, this
+     * iterator should not be used anymore.
+     * The iterator is closed automatically when it reaches it's end.
+     */
+    void close();
+}

Propchange: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/JobsIterator.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/AbstractRepositoryEventHandler.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/AbstractRepositoryEventHandler.java?rev=991134&r1=991133&r2=991134&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/AbstractRepositoryEventHandler.java
 (original)
+++ 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/AbstractRepositoryEventHandler.java
 Tue Aug 31 09:50:02 2010
@@ -290,9 +290,20 @@ public abstract class AbstractRepository
      */
     protected Event readEvent(Node eventNode)
     throws RepositoryException, ClassNotFoundException {
+        return this.readEvent(eventNode, false);
+    }
+
+    /**
+     * Read an event from the repository.
+     * @return
+     * @throws RepositoryException
+     * @throws ClassNotFoundException
+     */
+    protected Event readEvent(Node eventNode, final boolean forceLoad)
+    throws RepositoryException, ClassNotFoundException {
         final String topic = 
eventNode.getProperty(EventHelper.NODE_PROPERTY_TOPIC).getString();
         final ClassLoader cl = this.getDynamicClassLoader();
-        final Dictionary<String, Object> eventProps = 
EventHelper.readEventProperties(eventNode, cl);
+        final Dictionary<String, Object> eventProps = 
EventHelper.readEventProperties(eventNode, cl, forceLoad);
 
         eventProps.put(JobStatusProvider.PROPERTY_EVENT_ID, 
eventNode.getPath());
         this.addEventProperties(eventNode, eventProps);

Modified: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/EventHelper.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/EventHelper.java?rev=991134&r1=991133&r2=991134&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/EventHelper.java
 (original)
+++ 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/EventHelper.java
 Tue Aug 31 09:50:02 2010
@@ -251,7 +251,8 @@ public abstract class EventHelper {
      * @throws ClassNotFoundException
      */
     public static Dictionary<String, Object> readEventProperties(final Node 
node,
-                                                                 final 
ClassLoader objectClassLoader)
+                                                                 final 
ClassLoader objectClassLoader,
+                                                                 final boolean 
forceLoad)
     throws RepositoryException, ClassNotFoundException {
         final Dictionary<String, Object> properties = new Hashtable<String, 
Object>();
 
@@ -266,10 +267,18 @@ public abstract class EventHelper {
                     final Object value = ois.readObject();
                     properties.put(key, value);
                 }
+            } catch (ClassNotFoundException cnfe) {
+                if ( !forceLoad ) {
+                    throw cnfe;
+                }
             } catch (java.io.InvalidClassException ice) {
-                throw new ClassNotFoundException("Found invalid class.", ice);
+                if ( !forceLoad ) {
+                    throw new ClassNotFoundException("Found invalid class.", 
ice);
+                }
             } catch (IOException ioe) {
-                throw new RepositoryException("Unable to deserialize event 
properties.", ioe);
+                if ( !forceLoad ) {
+                    throw new RepositoryException("Unable to deserialize event 
properties.", ioe);
+                }
             }
         }
         // now all properties that have been set directly

Modified: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/JobEventHandler.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/JobEventHandler.java?rev=991134&r1=991133&r2=991134&view=diff
==============================================================================
--- 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/JobEventHandler.java
 (original)
+++ 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/JobEventHandler.java
 Tue Aug 31 09:50:02 2010
@@ -59,9 +59,11 @@ import org.apache.sling.commons.threads.
 import org.apache.sling.event.EventPropertiesMap;
 import org.apache.sling.event.EventUtil;
 import org.apache.sling.event.JobStatusProvider;
+import org.apache.sling.event.JobsIterator;
 import org.apache.sling.event.impl.job.JobBlockingQueue;
 import org.apache.sling.event.impl.job.JobStatusNotifier;
 import org.apache.sling.event.impl.job.JobUtil;
+import org.apache.sling.event.impl.job.JobsIteratorImpl;
 import org.apache.sling.event.impl.job.ParallelInfo;
 import org.osgi.framework.Constants;
 import org.osgi.service.component.ComponentConstants;
@@ -1104,6 +1106,29 @@ public class JobEventHandler
     }
 
     /**
+     * Read an event from the repository.
+     * This method is similar as {...@link #readEvent(Node)} with the exception
+     * that it even loads the event if classes are missing
+     * @throws RepositoryException
+     */
+    public Event forceReadEvent(Node eventNode)
+    throws RepositoryException {
+        try {
+            return this.readEvent(eventNode);
+        } catch (ClassNotFoundException cnfe) {
+            this.ignoreException(cnfe);
+        }
+        // we try it again and set the force load flag
+        try {
+            return this.readEvent(eventNode, true);
+        } catch (ClassNotFoundException cnfe) {
+            // this can never happen but we catch it anyway and rethrow
+            this.ignoreException(cnfe);
+            throw new RepositoryException(cnfe);
+        }
+    }
+
+    /**
      * @see 
javax.jcr.observation.EventListener#onEvent(javax.jcr.observation.EventIterator)
      */
     public void onEvent(EventIterator iter) {
@@ -1513,12 +1538,12 @@ public class JobEventHandler
      * @return
      * @throws RepositoryException
      */
-    private Collection<Event> queryJobs(final String topic,
-                                        final Boolean locked,
-                                        final Map<String, Object>... 
filterProps)  {
+    private JobsIterator queryJobs(final String topic,
+                                   final Boolean locked,
+                                   final Map<String, Object>... filterProps)  {
         // we create a new session
         Session s = null;
-        final List<Event> jobs = new ArrayList<Event>();
+        boolean closeSession = true;
         try {
             s = this.createSession();
             final QueryManager qManager = s.getWorkspace().getQueryManager();
@@ -1591,30 +1616,34 @@ public class JobEventHandler
             }
 
             final NodeIterator iter = q.execute().getNodes();
-            while ( iter.hasNext() ) {
-                final Node eventNode = iter.nextNode();
-                try {
-                    final Event event = this.readEvent(eventNode);
-                    jobs.add(event);
-                } catch (ClassNotFoundException cnfe) {
-                    // in the case of a class not found exception we just 
ignore the exception
-                    this.ignoreException(cnfe);
-                }
-            }
+            closeSession = false;
+            return new JobsIteratorImpl(iter, s, this);
         } catch (RepositoryException e) {
             // in the case of an error, we return an empty list
             this.ignoreException(e);
         } finally {
-            if ( s != null) {
+            if ( s != null && closeSession) {
                 s.logout();
             }
         }
+        return new JobsIteratorImpl(null, null, null);
+    }
+
+    private Collection<Event> queryJobsAsList(final String topic,
+            final Boolean locked,
+            final Map<String, Object>... filterProps)  {
+        final JobsIterator ji = this.queryJobs(topic, locked, filterProps);
+        final List<Event> jobs = new ArrayList<Event>();
+        while ( ji.hasNext() ) {
+            jobs.add(ji.next());
+        }
         return jobs;
     }
 
     /**
      * @see 
org.apache.sling.event.JobStatusProvider#getCurrentJobs(java.lang.String)
      */
+    @Deprecated
     public Collection<Event> getCurrentJobs(String topic) {
         return this.getCurrentJobs(topic, (Map<String, Object>[])null);
     }
@@ -1622,6 +1651,7 @@ public class JobEventHandler
     /**
      * @see 
org.apache.sling.event.JobStatusProvider#getScheduledJobs(java.lang.String)
      */
+    @Deprecated
     public Collection<Event> getScheduledJobs(String topic) {
         return this.getScheduledJobs(topic, (Map<String, Object>[])null);
     }
@@ -1629,26 +1659,28 @@ public class JobEventHandler
     /**
      * @see 
org.apache.sling.event.JobStatusProvider#getCurrentJobs(java.lang.String, 
java.util.Map...)
      */
+    @Deprecated
     public Collection<Event> getCurrentJobs(String topic, Map<String, 
Object>... filterProps) {
-        return this.queryJobs(topic, true, filterProps);
+        return this.queryJobsAsList(topic, true, filterProps);
     }
 
     /**
      * @see 
org.apache.sling.event.JobStatusProvider#getScheduledJobs(java.lang.String, 
java.util.Map...)
      */
+    @Deprecated
     public Collection<Event> getScheduledJobs(String topic, Map<String, 
Object>... filterProps) {
-        return this.queryJobs(topic, false, filterProps);
+        return this.queryJobsAsList(topic, false, filterProps);
     }
 
 
     /**
      * @see 
org.apache.sling.event.JobStatusProvider#getAllJobs(java.lang.String, 
java.util.Map...)
      */
+    @Deprecated
     public Collection<Event> getAllJobs(String topic, Map<String, Object>... 
filterProps) {
-        return this.queryJobs(topic, null, filterProps);
+        return this.queryJobsAsList(topic, null, filterProps);
     }
 
-
     /**
      * @see 
org.apache.sling.event.JobStatusProvider#cancelJob(java.lang.String, 
java.lang.String)
      */
@@ -1696,7 +1728,7 @@ public class JobEventHandler
                     if ( this.backgroundSession.itemExists(jobId) ) {
                         final Node eventNode = (Node) 
this.backgroundSession.getItem(jobId);
                         if ( eventNode.isLocked() ) {
-                            this.logger.info("Attempted to cancel a running 
job at {}", jobId);
+                            this.logger.debug("Attempted to cancel a running 
job at {}", jobId);
                             return false;
                         }
                         // try to load job to send notification
@@ -1790,6 +1822,27 @@ public class JobEventHandler
         }
     }
 
+    /**
+     * @see org.apache.sling.event.JobStatusProvider#queryAllJobs(String, 
Map...)
+     */
+    public JobsIterator queryAllJobs(final String topic, final Map<String, 
Object>... filterProps) {
+        return this.queryJobs(topic, null, filterProps);
+    }
+
+    /**
+     * @see org.apache.sling.event.JobStatusProvider#queryCurrentJobs(String, 
Map...)
+     */
+    public JobsIterator queryCurrentJobs(final String topic, final Map<String, 
Object>... filterProps) {
+        return this.queryJobs(topic, true, filterProps);
+    }
+
+    /**
+     * @see 
org.apache.sling.event.JobStatusProvider#queryScheduledJobs(String, Map...)
+     */
+    public JobsIterator queryScheduledJobs(final String topic, final 
Map<String, Object>... filterProps) {
+        return this.queryJobs(topic, false, filterProps);
+    }
+
     private static final class StartedJobInfo {
         public final Event event;
         public final String nodePath;

Added: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java?rev=991134&view=auto
==============================================================================
--- 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
 (added)
+++ 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
 Tue Aug 31 09:50:02 2010
@@ -0,0 +1,115 @@
+/*
+ * 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.sling.event.impl.job;
+
+import java.util.NoSuchElementException;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.event.JobsIterator;
+import org.apache.sling.event.impl.JobEventHandler;
+import org.osgi.service.event.Event;
+
+/**
+ * JCR Based Implementation of the jobs iterator
+ */
+public class JobsIteratorImpl implements JobsIterator {
+
+    private NodeIterator delegatee;
+
+    private Session session;
+
+    private JobEventHandler handler;
+
+    public JobsIteratorImpl(final NodeIterator ni,
+                            final Session session,
+                            final JobEventHandler handler) {
+        this.delegatee = ni;
+        this.session = session;
+        this.handler = handler;
+    }
+
+    /**
+     * @see org.apache.sling.event.JobsIterator#close()
+     */
+    public void close() {
+        if ( this.session != null ) {
+            this.session.logout();
+            this.session = null;
+            this.delegatee = null;
+            this.handler = null;
+        }
+    }
+
+    /**
+     * @see org.apache.sling.event.JobsIterator#getPosition()
+     */
+    public long getPosition() {
+        return this.delegatee.getPosition();
+    }
+
+    /**
+     * @see org.apache.sling.event.JobsIterator#getSize()
+     */
+    public long getSize() {
+        return this.delegatee.getSize();
+    }
+
+    /**
+     * @see org.apache.sling.event.JobsIterator#skip(long)
+     */
+    public void skip(long skipNum) {
+        this.delegatee.skip(skipNum);
+    }
+
+    /**
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {
+        final boolean result = (this.delegatee == null ? false : 
this.delegatee.hasNext());
+        if ( !result ) {
+            this.close();
+        }
+        return result;
+    }
+
+    /**
+     * @see java.util.Iterator#next()
+     */
+    public Event next() {
+        final Node n = this.delegatee.nextNode();
+        try {
+            return this.handler.forceReadEvent(n);
+        } catch (RepositoryException e) {
+            // if something goes wrong, we shutdown the iterator
+            this.close();
+            throw (NoSuchElementException)new 
NoSuchElementException("Repository exception during job reading").initCause(e);
+        }
+    }
+
+    /**
+     * @see java.util.Iterator#remove()
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("Remove not supported.");
+    }
+}

Propchange: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: 
sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/job/JobsIteratorImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain


Reply via email to