You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by jl...@apache.org on 2008/11/22 12:24:35 UTC

svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act...

Author: jleroux
Date: Sat Nov 22 03:24:28 2008
New Revision: 719836

URL: http://svn.apache.org/viewvc?rev=719836&view=rev
Log:
A patch from Philipp Hoppen " Individual logfiles for scheduled jobs" (https://issues.apache.org/jira/browse/OFBIZ-2042) - OFBIZ-2042


Modified:
    ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java
    ofbiz/trunk/framework/service/entitydef/entitymodel.xml
    ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java
    ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java
    ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml
    ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy
    ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml

Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java (original)
+++ ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java Sat Nov 22 03:24:28 2008
@@ -25,6 +25,7 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Vector;
 
 import org.apache.avalon.util.exception.ExceptionHelper;
 import org.apache.log4j.Level;
@@ -70,6 +71,8 @@
     protected static final boolean useLevelOnCache = true;
     
     protected static Logger root = Logger.getRootLogger();
+    
+    private static Map<String, Vector<Appender>> activeThreadGroupLoggerMap = new HashMap<String, Vector<Appender>>();
 
     static {
         levelStringMap.put("verbose", Debug.VERBOSE);
@@ -128,6 +131,108 @@
             return root;
         }
     }
+    
+    /**
+     * Checks if a logger exists and if it is in activeThreadGroupLoggerMap
+     * @param threadGroupId a thread group id
+     * @return
+     */
+    private static boolean hasActiveThreadLogger(String threadGroupId) {
+        //used so entries are not doubled in rootAppenders
+        Logger threadGroupLogger = org.apache.log4j.LogManager
+                .exists(threadGroupId);
+        return threadGroupLogger != null
+                && (activeThreadGroupLoggerMap.get(threadGroupId) != null && activeThreadGroupLoggerMap
+                        .get(threadGroupId).size() > 0);
+    }
+
+    private static Appender getAppender(String threadGroupId,
+            String appenderName) {
+        Vector<Appender> appenders = activeThreadGroupLoggerMap
+                .get(threadGroupId);
+        if (appenders != null) {
+            for (Appender appender : appenders) {
+                if (appender != null && appender.getName().equals(appenderName)) {
+                    return appender;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static void addAppenderToThreadGroupMap(String threadGroupId,
+            Appender appender) {
+        Vector<Appender> appenders = activeThreadGroupLoggerMap.get(threadGroupId);
+        if (appenders == null) {
+            appenders = new Vector<Appender>();
+        }
+        appenders.add(appender);
+        activeThreadGroupLoggerMap.put(threadGroupId, appenders);
+    }
+
+    private static void removeAppenderFromThreadGroupMap(String threadGroupId,
+            Appender appender) {
+        Vector<Appender> appenders = activeThreadGroupLoggerMap.get(threadGroupId);
+        if (appenders != null && appenders.contains(appender)) {
+            appenders.remove(appender);
+            activeThreadGroupLoggerMap.put(threadGroupId, appenders);
+        }
+    }
+
+    private static String currentThreadGroupId() {
+        return "" + Thread.currentThread().getThreadGroup().hashCode();
+    }
+
+    public static void registerCurrentThreadGroupLogger(String logFile, String appenderName) {
+        String pattern = "<div class=%p>%d (%t) [%24F:%-3L:%-5p]%x %m </div>%n";
+        registerThreadAppender(getNewFileAppender(appenderName,
+                logFile, 0, 0, pattern));
+    }
+
+    public static void registerThreadAppender(Appender appender) {
+        String threadGroupId = currentThreadGroupId();
+        
+        if (threadGroupId != null && threadGroupId.length() > 0) {
+            Logger theLogger = getLogger(threadGroupId);
+            if (theLogger != null) {
+                theLogger.setAdditivity(false);
+                theLogger.addAppender(appender);
+                addAppenderToThreadGroupMap(threadGroupId, appender);
+            }
+        }
+    }
+
+    public static void unregisterCurrentThreadGroupLogger(String appenderName) {
+        String threadGroupId = currentThreadGroupId();
+        Appender foundAppender = getAppender(threadGroupId, appenderName);
+        unregisterThreadAppender(foundAppender);
+    }
+
+    public static void unregisterThreadAppender(Appender appender) {
+        String threadGroupId = currentThreadGroupId();
+        if (threadGroupId != null && threadGroupId.length() > 0
+                && appender != null) {
+            Logger theLogger = getLogger(threadGroupId);
+            theLogger.removeAppender(appender);
+            removeAppenderFromThreadGroupMap(threadGroupId, appender);
+        }
+    }
+
+    /**
+     * Thread-specific logging
+     */
+    private static void logThreadGroup(int level, Throwable t, String msg, String module, 
+            String callingClass) {
+        String threadGroupId = currentThreadGroupId();
+        if (hasActiveThreadLogger(threadGroupId)) {
+            Logger grplogger = getLogger(threadGroupId + "." + module);
+            if (SYS_DEBUG != null) {
+                grplogger.setLevel(Level.DEBUG);
+            }
+            grplogger.log(callingClass, levelObjs[level], msg, t);
+        }
+    }
+    
 
     /** Gets an Integer representing the level number from a String representing the level name; will return null if not found */
     public static Integer getLevelFromString(String levelName) {
@@ -164,6 +269,7 @@
                     logger.setLevel(Level.DEBUG);
                 }
                 logger.log(callingClass, levelObjs[level], msg, t);
+                logThreadGroup(level, t, msg, module, callingClass);
             } else {
                 StringBuilder prefixBuf = new StringBuilder();
 

Modified: ofbiz/trunk/framework/service/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/entitydef/entitymodel.xml?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/entitydef/entitymodel.xml (original)
+++ ofbiz/trunk/framework/service/entitydef/entitymodel.xml Sat Nov 22 03:24:28 2008
@@ -64,6 +64,8 @@
         <field name="startDateTime" type="date-time"></field>
         <field name="finishDateTime" type="date-time"></field>
         <field name="cancelDateTime" type="date-time"></field>
+        <field name="ownLogfile" type="indicator"></field>
+        <field name="logLocation" type="long-varchar"></field>
         <prim-key field="jobId"/>
         <relation type="one" fk-name="JOB_SNDBX_RECINFO" rel-entity-name="RecurrenceInfo">
             <key-map field-name="recurrenceInfoId"/>

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java Sat Nov 22 03:24:28 2008
@@ -59,8 +59,18 @@
      * @see org.ofbiz.service.LocalDispatcher#schedule(java.lang.String, java.lang.String, java.lang.String, java.util.Map, long, int, int, int, long, int)
      */
     public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException {
+        schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry, false);
+    }
+    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, Object... context) throws GenericServiceException {
+        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, endTime, maxRetry);
+    }
+    
+    /**
+     * @see org.ofbiz.service.LocalDispatcher#schedule(java.lang.String, java.lang.String, java.lang.String, java.util.Map, long, int, int, int, long, int, boolean)
+     */
+    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws GenericServiceException {
         try {
-            getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry);
+            getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
                 
             if (Debug.verboseOn()) {
                 Debug.logVerbose("[LocalDispatcher.schedule] : Current time : " + (new Date()).getTime(), module);
@@ -70,15 +80,16 @@
                 Debug.logVerbose("[LocalDispatcher.schedule] : Count        : " + count, module);
                 Debug.logVerbose("[LocalDispatcher.schedule] : EndTime      : " + endTime, module);
                 Debug.logVerbose("[LocalDispatcher.schedule] : MazRetry     : " + maxRetry, module);
+                Debug.logVerbose("[LocalDispatcher.schedule] : OwnLogfile     : " + ownLogfile, module);
             }
             
         } catch (JobManagerException e) {
             throw new GenericServiceException(e.getMessage(), e);
         }
     }
-
-    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, Object... context) throws GenericServiceException {
-        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, endTime, maxRetry);
+    
+    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile, Object... context) throws GenericServiceException {
+        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
     }
 
     /**

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java Sat Nov 22 03:24:28 2008
@@ -228,6 +228,25 @@
     public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException;
     public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, Object... context) throws GenericServiceException;
 
+    /**
+     * Schedule a service to run asynchronously at a specific start time.
+     * @param jobName Name of the job
+     * @param poolName Name of the service pool to send to.
+     * @param serviceName Name of the service to invoke.
+     * @param context The name/value pairs composing the context.
+     * @param startTime The time to run this service.
+     * @param frequency The frequency of the recurrence (RecurrenceRule.DAILY, etc).
+     * @param interval The interval of the frequency recurrence.
+     * @param count The number of times to repeat.
+     * @param endTime The time in milliseconds the service should expire
+     * @param maxRetry The number of times we should retry on failure
+     * @param ownLogfile Indicator whether this job uses it's own logfile
+     * @throws ServiceAuthException
+     * @throws ServiceValidationException
+     * @throws GenericServiceException
+     */
+    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws GenericServiceException;
+    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile, Object... context) throws GenericServiceException;
 
     /**
      * Schedule a service to run asynchronously at a specific start time.

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java Sat Nov 22 03:24:28 2008
@@ -38,6 +38,8 @@
 
 import javax.servlet.http.HttpServletRequest;
 import javax.transaction.Transaction;
+
+import java.io.File;
 import java.sql.Timestamp;
 import java.util.*;
 
@@ -420,7 +422,14 @@
                         boolean beganTx2 = false;
                         try {
                             beganTx2 = TransactionUtil.begin();
+                            String logLocation = job.getString("logLocation");
+                            
                             job.remove();
+                            
+                            if (logLocation != null) {
+                                File logfile = new File(job.getString("logLocation"));
+                                logfile.delete();
+                            }
                             runtimeToDelete.add(runtimeId);
                         } catch (GenericEntityException e) {
                             Debug.logInfo("Cannot remove job data for ID: " + jobId, module);

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java Sat Nov 22 03:24:28 2008
@@ -38,7 +38,9 @@
 
     private String service = null;
     private Map<String, Object> context = null;
-
+    
+    private String logLocation = null;
+        
     public GenericServiceJob(DispatchContext dctx, String jobId, String jobName, String service, Map<String, Object> context, GenericRequester req) {
         super(jobId, jobName);
         this.dctx = dctx;
@@ -61,12 +63,18 @@
      */
     public void exec() throws InvalidJobException {
         init();
-
+        String appenderName = module;
         // no transaction is necessary since runSync handles this
         try {
             // get the dispatcher and invoke the service via runSync -- will run all ECAs
             LocalDispatcher dispatcher = dctx.getDispatcher();
-            Map<String, Object> result = dispatcher.runSync(getServiceName(), getContext());
+            
+            if (this.logLocation != null) {
+                Debug
+                .registerCurrentThreadGroupLogger(this.logLocation,
+                        appenderName);
+            }
+            Map result = dispatcher.runSync(getServiceName(), getContext());
 
             // check for a failure
             boolean isError = ModelService.RESPOND_ERROR.equals(result.get(ModelService.RESPONSE_MESSAGE));
@@ -87,6 +95,8 @@
 
             // call the failed method
             this.failed(t);
+        }finally{
+            Debug.unregisterCurrentThreadGroupLogger(appenderName);
         }
 
         // call the finish method
@@ -131,5 +141,13 @@
      */
     protected String getServiceName() throws InvalidJobException {
         return service;
-    }        
+    }
+    
+    /**
+     * Sets the logfile name
+     * @param logLocation
+     */
+    protected void setLogLocation(String logLocation) {
+        this.logLocation = logLocation;
+    }
 }

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java Sat Nov 22 03:24:28 2008
@@ -19,6 +19,7 @@
 package org.ofbiz.service.job;
 
 import java.util.Date;
+import java.util.Random;
 
 import org.ofbiz.service.config.ServiceConfigUtil;
 import org.ofbiz.base.util.Debug;
@@ -60,11 +61,11 @@
 
         // service dispatcher delegator name (for thread name)
         String delegatorName = jp.getManager().getDelegator().getDelegatorName();
-
+        
         // get a new thread
-        this.thread = new Thread(this);
+        this.thread = new Thread(new ThreadGroup("JobInvoker" + this.hashCode()), this);
         this.name = delegatorName + "-invoker-" + this.thread.getName();
-
+        
         this.thread.setDaemon(false);
         this.thread.setName(this.name);
 

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java Sat Nov 22 03:24:28 2008
@@ -28,6 +28,7 @@
 import javolution.util.FastMap;
 
 import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.FileUtil;
 import org.ofbiz.base.util.GeneralRuntimeException;
 import org.ofbiz.base.util.UtilDateTime;
 import org.ofbiz.base.util.UtilMisc;
@@ -310,7 +311,7 @@
      *@param endTime The time in milliseconds the service should expire
      */
     public void schedule(String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime) throws JobManagerException {
-        schedule(null, null, serviceName, context, startTime, frequency, interval, count, endTime, -1);
+        schedule(null, null, serviceName, context, startTime, frequency, interval, count, endTime, -1, false);
     }
 
     /**
@@ -324,9 +325,10 @@
      *@param interval The interval of the frequency recurrence
      *@param count The number of times to repeat
      *@param endTime The time in milliseconds the service should expire
+     *@param ownLogfile Indicator whether this job uses it's own logfile
      *@param maxRetry The max number of retries on failure (-1 for no max)
      */
-    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws JobManagerException {
+    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws JobManagerException {
         if (delegator == null) {
             Debug.logWarning("No delegator referenced; cannot schedule job.", module);
             return;
@@ -348,7 +350,7 @@
         }
 
         // schedule the job
-        schedule(jobName, poolName, serviceName, dataId, startTime, frequency, interval, count, endTime, maxRetry);
+        schedule(jobName, poolName, serviceName, dataId, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
     }
 
     /**
@@ -359,7 +361,7 @@
      *@param startTime The time in milliseconds the service should run
      */
     public void schedule(String poolName, String serviceName, String dataId, long startTime) throws JobManagerException {
-        schedule(null, poolName, serviceName, dataId, startTime, -1, 0, 1, 0, -1);
+        schedule(null, poolName, serviceName, dataId, startTime, -1, 0, 1, 0, -1, false);
     }
 
     /**
@@ -374,8 +376,9 @@
      *@param count The number of times to repeat
      *@param endTime The time in milliseconds the service should expire
      *@param maxRetry The max number of retries on failure (-1 for no max)
+     *@param ownLogfile Indicator whether this job uses it's own logfile
      */
-    public void schedule(String jobName, String poolName, String serviceName, String dataId, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws JobManagerException {
+    public void schedule(String jobName, String poolName, String serviceName, String dataId, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws JobManagerException {
         if (delegator == null) {
             Debug.logWarning("No delegator referenced; cannot schedule job.", module);
             return;
@@ -411,6 +414,12 @@
 
         // set the max retry
         jFields.put("maxRetry", Long.valueOf(maxRetry));
+        
+        if (ownLogfile) {
+            jFields.put("ownLogfile", "Y");
+        } else {
+            jFields.put("ownLogfile", "N");
+        }
 
         // create the value and store
         GenericValue jobV;

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java Sat Nov 22 03:24:28 2008
@@ -174,6 +174,24 @@
             throw new RuntimeException(e.getMessage());
         }
         if (Debug.infoOn()) Debug.logInfo(this.toString() + "[" + getJobId() + "] -- Next runtime: " + new Date(nextRecurrence), module);
+    
+        //set the location of the logfile if this was desired
+        if ("Y".equals(job.get("ownLogfile"))) {
+            String logLocation = System.getProperty("ofbiz.log.dir", "runtime/logs") + "/"
+            + getServiceName()
+            + "_"
+            + UtilDateTime.getTimestamp(System.currentTimeMillis())
+                    .toString().trim().replace(" ", "_") + ".html";
+            this.setLogLocation(logLocation);
+            job.set("logLocation", logLocation);
+            try {
+                job.store();
+            } catch (GenericEntityException e) {
+                Debug.logError(e, "Cannot update the job [" + getJobId() + "] sandbox", module);
+            }
+        }
+        
+    
     }
 
     private void createRecurrence(GenericValue job, long next) throws GenericEntityException {

Modified: ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java (original)
+++ ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java Sat Nov 22 03:24:28 2008
@@ -216,6 +216,11 @@
         String serviceIntr = (String) params.remove("SERVICE_INTERVAL");
         String serviceCnt = (String) params.remove("SERVICE_COUNT");
         String retryCnt = (String) params.remove("SERVICE_MAXRETRY");
+        
+        boolean ownLogfile = false;
+        if ("Y".equals(params.get("OWN_LOGFILE"))) {
+            ownLogfile = true;
+        }
 
         // the frequency map
         Map<String, Integer> freqMap = FastMap.newInstance();
@@ -409,7 +414,7 @@
             if(null!=request.getParameter("_RUN_SYNC_") && request.getParameter("_RUN_SYNC_").equals("Y")){
                 syncServiceResult = dispatcher.runSync(serviceName, serviceContext);
             }else{
-                dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, endTime, maxRetry);
+                dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
             }
         } catch (GenericServiceException e) {
             String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_dispatcher_exception", locale);

Modified: ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml (original)
+++ ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml Sat Nov 22 03:24:28 2008
@@ -1204,6 +1204,10 @@
         <value xml:lang="th">รายชื่องาน</value>
         <value xml:lang="zh">任务列表</value>
     </property>
+    <property key="WebtoolsJobLog">
+        <value xml:lang="en">Logfile</value>
+        <value xml:lang="de">Logdatei</value>
+    </property>
     <property key="WebtoolsLHSMapName">
         <value xml:lang="en">LHS map name</value>
         <value xml:lang="it">Nome mappa LHS</value>
@@ -1744,6 +1748,10 @@
         <value xml:lang="th">Output Directory</value>
         <value xml:lang="zh">输出目录</value>
     </property>
+    <property key="WebtoolsOwnLogfile">
+        <value xml:lang="en">Use seperate logfile</value>
+        <value xml:lang="de">Eigene Logdatei</value>
+    </property>
     <property key="WebtoolsParameterName">
         <value xml:lang="en">Parameter Name</value>
         <value xml:lang="it">Nome Parametro</value>

Modified: ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy (original)
+++ ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy Sat Nov 22 03:24:28 2008
@@ -19,6 +19,13 @@
 
 import org.ofbiz.base.util.FileUtil;
 
+if (parameters.jobId!=null) {
+    value = delegator.findByPrimaryKey("JobSandbox", [jobId:parameters.jobId]);
+    if (value.getString("logLocation") != null) {
+        context.logFileName = value.getString("logLocation");
+        logFileName = value.getString("logLocation");
+    }
+}
 sb = null;
 try {
     sb = FileUtil.readTextFile(logFileName, true);

Modified: ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml?rev=719836&r1=719835&r2=719836&view=diff
==============================================================================
--- ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml (original)
+++ ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml Sat Nov 22 03:24:28 2008
@@ -43,6 +43,7 @@
         <field name="SERVICE_INTERVAL" title="${uiLabelMap.WebtoolsInterval}" tooltip="${uiLabelMap.WebtoolsMessage8}"><text/></field>
         <field name="SERVICE_COUNT" title="${uiLabelMap.WebtoolsCount}" tooltip="${uiLabelMap.WebtoolsMessage9}"><text default-value="1"/></field>
         <field name="SERVICE_MAXRETRY" title="${uiLabelMap.WebtoolsMaxRetry}" tooltip="${uiLabelMap.WebtoolsMessage10}"><text/></field>
+        <field name="OWN_LOGFILE" title="${uiLabelMap.WebtoolsOwnLogfile}"><check/></field>
         <field name="submitButton" title="${uiLabelMap.CommonSubmit}"><submit button-type="button"/></field> 
     </form>
 
@@ -72,6 +73,9 @@
     <form name="JobList" title="" target="" type="list" list-name="jobs"
         paginate-target="jobList" override-list-size="${jobListSize}"
         odd-row-style="alternate-row" default-table-style="basic-table hover-bar">
+        <row-actions>
+            <set field="ownLogfile" from-field="ownLogfile" default-value="N"/>
+        </row-actions>
         <field name="jobName" title="${uiLabelMap.WebtoolsJob}"><display/></field>
         <field name="jobId" title="${uiLabelMap.CommonId}"><display/></field>
         <field name="poolId" title="${uiLabelMap.WebtoolsPool}"><display/></field>
@@ -82,8 +86,11 @@
         </field>
         <field name="statusId" title="${uiLabelMap.CommonStatus}"><display-entity entity-name="StatusItem" description="${description}"/></field>
         <field name="cancelDateTime" title="${uiLabelMap.CommonEndDateTime}"><display/></field>
+        <field name="ownLogfile" title="${uiLabelMap.WebtoolsOwnLogfile}"><display/></field>
+        <field name="logFile" use-when="logLocation != null" title="${uiLabelMap.WebtoolsJobLog}"><hyperlink target="LogView?jobId=${jobId}" description="${uiLabelMap.WebtoolsJobLog}"/></field>
+        <field name="logFile" use-when="logLocation == null" title="${uiLabelMap.WebtoolsJobLog}"><display/></field>
         <field name="cancelButton" title="${uiLabelMap.CommonEmptyHeader}" use-when="startDateTime==null&amp;&amp;finishDateTime==null&amp;&amp;cancelDateTime==null" widget-style="buttontext">
             <hyperlink also-hidden="false" description="${uiLabelMap.WebtoolsCancelJob}" target="cancelJob?jobId=${jobId}"/>
         </field>
     </form>
-</forms>
\ No newline at end of file
+</forms>



Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Jacques Le Roux <ja...@les7arts.com>.
Bump!

Jacques

From: "Jacques Le Roux" <ja...@les7arts.com>
> Hi Adam,
>
> I resurrect this message (related to https://issues.apache.org/jira/browse/OFBIZ-2042) because I think it's worth it.
> At this moment you said you will have something for us. Could you confirm please? Else I will try to follow the way the patch 
> did...
>
> Jacques
>
> From: "Jacques Le Roux" <ja...@les7arts.com>
>> Hi Philipp,
>>
>> Looks like Adam did not receive your message (Adam?)
>> Maybe you could follow Adam's advices, but it would be far better to wait an answer from him as he seems to have a good solution 
>> for this
>>
>> Thanks
>>
>> Jacques
>>
>> From: "Philipp Hoppen" <ph...@nowhow.ch>
>>> Hi Adam,
>>>
>>> Is someone still working on this? Or is there anything i can do to fix my original patch to get it in again?
>>>
>>> Adam Heath schrieb:
>>>> I'm writing the per-thread init code now.  It's not completely new code,
>>>> it's something I wrote for webslinger.
>
> 

Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Jacques Le Roux <ja...@les7arts.com>.
Hi Adam,

I resurrect this message (related to https://issues.apache.org/jira/browse/OFBIZ-2042) because I think it's worth it.
At this moment you said you will have something for us. Could you confirm please? Else I will try to follow the way the patch did...

Jacques

From: "Jacques Le Roux" <ja...@les7arts.com>
> Hi Philipp,
>
> Looks like Adam did not receive your message (Adam?)
> Maybe you could follow Adam's advices, but it would be far better to wait an answer from him as he seems to have a good solution 
> for this
>
> Thanks
>
> Jacques
>
> From: "Philipp Hoppen" <ph...@nowhow.ch>
>> Hi Adam,
>>
>> Is someone still working on this? Or is there anything i can do to fix my original patch to get it in again?
>>
>> Adam Heath schrieb:
>>> I'm writing the per-thread init code now.  It's not completely new code,
>>> it's something I wrote for webslinger.



Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Jacques Le Roux <ja...@les7arts.com>.
Hi Philipp,

Looks like Adam did not receive your message (Adam?)
Maybe you could follow Adam's advices, but it would be far better to wait an answer from him as he seems to have a good solution for 
this

Thanks

Jacques

From: "Philipp Hoppen" <ph...@nowhow.ch>
> Hi Adam,
>
> Is someone still working on this? Or is there anything i can do to fix my original patch to get it in again?
>
> Adam Heath schrieb:
>> I'm writing the per-thread init code now.  It's not completely new code,
>> it's something I wrote for webslinger.
>>
>>
>>
>
>
> -- 
> Philipp Hoppen,
> nowhow solutions AG, Laupenstrasse 1, CH-3008 Bern
> Phone +41 (0)31 380 00 71 http://www.nowhow.ch
> 


Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Philipp Hoppen <ph...@nowhow.ch>.
Hi Adam,

Is someone still working on this? Or is there anything i can do to fix 
my original patch to get it in again?

Adam Heath schrieb:
> I'm writing the per-thread init code now.  It's not completely new code,
> it's something I wrote for webslinger.
>
>
>   


-- 
Philipp Hoppen,
nowhow solutions AG, Laupenstrasse 1, CH-3008 Bern
Phone +41 (0)31 380 00 71 http://www.nowhow.ch


Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Adam Heath <do...@brainfood.com>.
Jacques Le Roux wrote:
> Thanks for guidance, I will try it later. Reverted in revision: 728536 
> Jacques

I'm writing the per-thread init code now.  It's not completely new code,
it's something I wrote for webslinger.

Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Jacques Le Roux <ja...@les7arts.com>.
Thanks for guidance, I will try it later. Reverted in revision: 728536  

Jacques

From: "Adam Heath" <do...@brainfood.com>
> Jacques Le Roux wrote:
>> Hi Adam,
>> 
>> I don't mind to revert this patch, but I think it's nice to have this
>> feature.
>> I suppose that what you call "multiple calls in series" are expressions
>> like (and yes it's not synchronized, my bad !)
>> 
>>>> +            for (Appender appender : appenders) {
>>>> +                if (appender != null &&
>>>> appender.getName().equals(appenderName)) {
>>>> +                    return appender;
>>>> +                }
> 
> No, that's not what I mean.
> 
>> Could we not replace Vector (bad I agree) by synchronizedMap and use
>> synchronization on the synchronized Map, like in
>> http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#synchronizedList(java.util.List)
> 
> Foo hasFoo() {
>  synchronized (fooContainer) {
>  }
> }
> 
> void setFoo() {
>  synchronized (fooContainer) {
>  }
> }
> 
> Foo getFoo() {
>  if (!hasFoo()) setFoo();
>  return getFoo();
> }
> 
> This is the pattern that is in this patch, and it's a race condition.
> 
> In addition, synchronizing on the internal Collection(the value in the
> map) isn't always worth it, if you are already inside a synchronized
> block on some other variable.
> 
>> 
>> 
>> For the memorty leaks, I think removeAppenderFromThreadGroupMap would be
>> OK, isn'it ?
>> 
>>> Integer.toString(), not "" + int.
>> is bad I agree
>> 
>> Also here result is not genericized as it was "before" (but I guess the
>> patch was anterior too the genericization, and I did not spot that also,
>> maybe there are more I will be carefull on this aspect too)
>> 
>>>> -            Map<String, Object> result =
>>>> dispatcher.runSync(getServiceName(), getContext());
>>>> +
>>>> +            if (this.logLocation != null) {
>>>> +                Debug
>>>> +                .registerCurrentThreadGroupLogger(this.logLocation,
>>>> +                        appenderName);
>>>> +            }
>>>> +            Map result = dispatcher.runSync(getServiceName(),
>>>> getContext());
>> 
>> So, by usint also FlexibleLocation instead of File, don't you think we
>> could amend this "patch" instead of simply reverting it ?
> 
> No, because there are issues with memory loss too, due to the
> reimplementation of what ThreadLocal actually accomplishes.
> 
> What would be better, is to implement a singleton per-thread
> initialization and cleanup system.
> 
> ==
> void ThreadWorker.run() {
>  try {
>    doWork();
>  } finally {
>    ThreadInit.runCleanup();
>  }
> }
> 
> static <T> T ThreadInit.getInitObject(ThreadInit<T> init) {
>  Map<Class<? extends ThreadInit>, InitData> initMap = tl.get();
>  if (initMap == null) {
>    initMap = FastMap.newInstance();
>    tl.set(initMap);
>  }
>  InitData<T> initData = initMap.get(init.getClass());
>  if (initData != null) return initData.getValue();
>  T initValue = init.start();
>  initMap.put(init.getClass(), new InitData<T>(init, initValue));
>  return initValue;
> }
> 
> static void ThreadInit.runCleanup() {
>  Map<Class<? extends ThreadInit>, InitData<?>> initMap = tl.get();
>  if (initMap == null) return;
>  for (InitData<?> initData: initMap.values()) {
>    runCleanup(initData);
>  }
> }
> 
> static <T> void ThreadInit.runCleanup(InitData<T> initData) {
>  initData.getInit().runCleanup(initData.getValue());
> }
> ==
> 
> This allows any random code anywhere to have init code that runs *once*
> per thread invocation; it just requires that very early in the
> invocation chain, is a try/finally block, that runs the cleanup code.
> 
> It's also possible to extend this slightly, so there is a concept of
> push/pop ThreadLocal state.
> 
> You'll note there is *no* synchronization here.  This is because the
> top-level variable that holds the object graph, is a ThreadLocal.
> Synchronization is only used for cross-thread control.  ThreadLocal is
> local, and can't corrupt across threads.
>

Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Adam Heath <do...@brainfood.com>.
Jacques Le Roux wrote:
> Hi Adam,
> 
> I don't mind to revert this patch, but I think it's nice to have this
> feature.
> I suppose that what you call "multiple calls in series" are expressions
> like (and yes it's not synchronized, my bad !)
> 
>>> +            for (Appender appender : appenders) {
>>> +                if (appender != null &&
>>> appender.getName().equals(appenderName)) {
>>> +                    return appender;
>>> +                }

No, that's not what I mean.

> Could we not replace Vector (bad I agree) by synchronizedMap and use
> synchronization on the synchronized Map, like in
> http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#synchronizedList(java.util.List)

Foo hasFoo() {
  synchronized (fooContainer) {
  }
}

void setFoo() {
  synchronized (fooContainer) {
  }
}

Foo getFoo() {
  if (!hasFoo()) setFoo();
  return getFoo();
}

This is the pattern that is in this patch, and it's a race condition.

In addition, synchronizing on the internal Collection(the value in the
map) isn't always worth it, if you are already inside a synchronized
block on some other variable.

> 
> 
> For the memorty leaks, I think removeAppenderFromThreadGroupMap would be
> OK, isn'it ?
> 
>> Integer.toString(), not "" + int.
> is bad I agree
> 
> Also here result is not genericized as it was "before" (but I guess the
> patch was anterior too the genericization, and I did not spot that also,
> maybe there are more I will be carefull on this aspect too)
> 
>>> -            Map<String, Object> result =
>>> dispatcher.runSync(getServiceName(), getContext());
>>> +
>>> +            if (this.logLocation != null) {
>>> +                Debug
>>> +                .registerCurrentThreadGroupLogger(this.logLocation,
>>> +                        appenderName);
>>> +            }
>>> +            Map result = dispatcher.runSync(getServiceName(),
>>> getContext());
> 
> So, by usint also FlexibleLocation instead of File, don't you think we
> could amend this "patch" instead of simply reverting it ?

No, because there are issues with memory loss too, due to the
reimplementation of what ThreadLocal actually accomplishes.

What would be better, is to implement a singleton per-thread
initialization and cleanup system.

==
void ThreadWorker.run() {
  try {
    doWork();
  } finally {
    ThreadInit.runCleanup();
  }
}

static <T> T ThreadInit.getInitObject(ThreadInit<T> init) {
  Map<Class<? extends ThreadInit>, InitData> initMap = tl.get();
  if (initMap == null) {
    initMap = FastMap.newInstance();
    tl.set(initMap);
  }
  InitData<T> initData = initMap.get(init.getClass());
  if (initData != null) return initData.getValue();
  T initValue = init.start();
  initMap.put(init.getClass(), new InitData<T>(init, initValue));
  return initValue;
}

static void ThreadInit.runCleanup() {
  Map<Class<? extends ThreadInit>, InitData<?>> initMap = tl.get();
  if (initMap == null) return;
  for (InitData<?> initData: initMap.values()) {
    runCleanup(initData);
  }
}

static <T> void ThreadInit.runCleanup(InitData<T> initData) {
  initData.getInit().runCleanup(initData.getValue());
}
==

This allows any random code anywhere to have init code that runs *once*
per thread invocation; it just requires that very early in the
invocation chain, is a try/finally block, that runs the cleanup code.

It's also possible to extend this slightly, so there is a concept of
push/pop ThreadLocal state.

You'll note there is *no* synchronization here.  This is because the
top-level variable that holds the object graph, is a ThreadLocal.
Synchronization is only used for cross-thread control.  ThreadLocal is
local, and can't corrupt across threads.


Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act

Posted by Jacques Le Roux <ja...@les7arts.com>.
Hi Adam,

I don't mind to revert this patch, but I think it's nice to have this feature.
I suppose that what you call "multiple calls in series" are expressions like (and yes it's not synchronized, my bad !)

>> +            for (Appender appender : appenders) {
>> +                if (appender != null && appender.getName().equals(appenderName)) {
>> +                    return appender;
>> +                }

Could we not replace Vector (bad I agree) by synchronizedMap and use synchronization on the synchronized Map, like in 
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#synchronizedList(java.util.List)

For the memorty leaks, I think removeAppenderFromThreadGroupMap would be OK, isn'it ?

>Integer.toString(), not "" + int.
is bad I agree

Also here result is not genericized as it was "before" (but I guess the patch was anterior too the genericization, and I did not 
spot that also, maybe there are more I will be carefull on this aspect too)

>> -            Map<String, Object> result = dispatcher.runSync(getServiceName(), getContext());
>> +
>> +            if (this.logLocation != null) {
>> +                Debug
>> +                .registerCurrentThreadGroupLogger(this.logLocation,
>> +                        appenderName);
>> +            }
>> +            Map result = dispatcher.runSync(getServiceName(), getContext());

So, by usint also FlexibleLocation instead of File, don't you think we could amend this "patch" instead of simply reverting it ?

Thanks

Jacques


From: "Adam Heath" <do...@brainfood.com>
> jleroux@apache.org wrote:
>> Author: jleroux
>> Date: Sat Nov 22 03:24:28 2008
>> New Revision: 719836
>>
>> URL: http://svn.apache.org/viewvc?rev=719836&view=rev
>> Log:
>> A patch from Philipp Hoppen " Individual logfiles for scheduled jobs" (https://issues.apache.org/jira/browse/OFBIZ-2042) - 
>> OFBIZ-2042
>
> Sorry for the late reply, but please revert this patch, it has several
> issues; see comments inline.
>
> As a general comment, this should use an InheritableThreadLocal, not a
> string-key-managed map.
>
>
>> Modified:
>>     ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java
>>     ofbiz/trunk/framework/service/entitydef/entitymodel.xml
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java
>>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java
>>     ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java
>>     ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml
>>     ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy
>>     ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml
>>
>> Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java (original)
>> +++ ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java Sat Nov 22 03:24:28 2008
>> @@ -25,6 +25,7 @@
>>  import java.util.Enumeration;
>>  import java.util.HashMap;
>>  import java.util.Map;
>> +import java.util.Vector;
>>
>>  import org.apache.avalon.util.exception.ExceptionHelper;
>>  import org.apache.log4j.Level;
>> @@ -70,6 +71,8 @@
>>      protected static final boolean useLevelOnCache = true;
>>
>>      protected static Logger root = Logger.getRootLogger();
>> +
>> +    private static Map<String, Vector<Appender>> activeThreadGroupLoggerMap = new HashMap<String, Vector<Appender>>();
>
> This variable should not be using a Vector; yes, a single call to a
> Vector's method is synchronized, but multiple calls in series are *not*.
>
> In addition, activeThreadGroupLoggerMap is not synchronized at all either.
>
> Plus, there are memory leaks with this code, as when are items removed
> from the map?
>
>>
>>      static {
>>          levelStringMap.put("verbose", Debug.VERBOSE);
>> @@ -128,6 +131,108 @@
>>              return root;
>>          }
>>      }
>> +
>> +    /**
>> +     * Checks if a logger exists and if it is in activeThreadGroupLoggerMap
>> +     * @param threadGroupId a thread group id
>> +     * @return
>> +     */
>> +    private static boolean hasActiveThreadLogger(String threadGroupId) {
>> +        //used so entries are not doubled in rootAppenders
>> +        Logger threadGroupLogger = org.apache.log4j.LogManager
>> +                .exists(threadGroupId);
>> +        return threadGroupLogger != null
>> +                && (activeThreadGroupLoggerMap.get(threadGroupId) != null && activeThreadGroupLoggerMap
>> +                        .get(threadGroupId).size() > 0);
>> +    }
>> +
>> +    private static Appender getAppender(String threadGroupId,
>> +            String appenderName) {
>> +        Vector<Appender> appenders = activeThreadGroupLoggerMap
>> +                .get(threadGroupId);
>> +        if (appenders != null) {
>> +            for (Appender appender : appenders) {
>> +                if (appender != null && appender.getName().equals(appenderName)) {
>> +                    return appender;
>> +                }
>> +            }
>> +        }
>> +        return null;
>> +    }
>> +
>> +    private static void addAppenderToThreadGroupMap(String threadGroupId,
>> +            Appender appender) {
>> +        Vector<Appender> appenders = activeThreadGroupLoggerMap.get(threadGroupId);
>> +        if (appenders == null) {
>> +            appenders = new Vector<Appender>();
>> +        }
>> +        appenders.add(appender);
>> +        activeThreadGroupLoggerMap.put(threadGroupId, appenders);
>> +    }
>> +
>> +    private static void removeAppenderFromThreadGroupMap(String threadGroupId,
>> +            Appender appender) {
>> +        Vector<Appender> appenders = activeThreadGroupLoggerMap.get(threadGroupId);
>> +        if (appenders != null && appenders.contains(appender)) {
>> +            appenders.remove(appender);
>> +            activeThreadGroupLoggerMap.put(threadGroupId, appenders);
>> +        }
>> +    }
>> +
>> +    private static String currentThreadGroupId() {
>> +        return "" + Thread.currentThread().getThreadGroup().hashCode();
>> +    }
>
> Integer.toString(), not "" + int.
>
>
>> +
>> +    public static void registerCurrentThreadGroupLogger(String logFile, String appenderName) {
>> +        String pattern = "<div class=%p>%d (%t) [%24F:%-3L:%-5p]%x %m </div>%n";
>> +        registerThreadAppender(getNewFileAppender(appenderName,
>> +                logFile, 0, 0, pattern));
>> +    }
>> +
>> +    public static void registerThreadAppender(Appender appender) {
>> +        String threadGroupId = currentThreadGroupId();
>> +
>> +        if (threadGroupId != null && threadGroupId.length() > 0) {
>> +            Logger theLogger = getLogger(threadGroupId);
>> +            if (theLogger != null) {
>> +                theLogger.setAdditivity(false);
>> +                theLogger.addAppender(appender);
>> +                addAppenderToThreadGroupMap(threadGroupId, appender);
>> +            }
>> +        }
>> +    }
>> +
>> +    public static void unregisterCurrentThreadGroupLogger(String appenderName) {
>> +        String threadGroupId = currentThreadGroupId();
>> +        Appender foundAppender = getAppender(threadGroupId, appenderName);
>> +        unregisterThreadAppender(foundAppender);
>> +    }
>> +
>> +    public static void unregisterThreadAppender(Appender appender) {
>> +        String threadGroupId = currentThreadGroupId();
>> +        if (threadGroupId != null && threadGroupId.length() > 0
>> +                && appender != null) {
>> +            Logger theLogger = getLogger(threadGroupId);
>> +            theLogger.removeAppender(appender);
>> +            removeAppenderFromThreadGroupMap(threadGroupId, appender);
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Thread-specific logging
>> +     */
>> +    private static void logThreadGroup(int level, Throwable t, String msg, String module,
>> +            String callingClass) {
>> +        String threadGroupId = currentThreadGroupId();
>> +        if (hasActiveThreadLogger(threadGroupId)) {
>> +            Logger grplogger = getLogger(threadGroupId + "." + module);
>> +            if (SYS_DEBUG != null) {
>> +                grplogger.setLevel(Level.DEBUG);
>> +            }
>> +            grplogger.log(callingClass, levelObjs[level], msg, t);
>> +        }
>> +    }
>> +
>>
>>      /** Gets an Integer representing the level number from a String representing the level name; will return null if not found 
>> */
>>      public static Integer getLevelFromString(String levelName) {
>> @@ -164,6 +269,7 @@
>>                      logger.setLevel(Level.DEBUG);
>>                  }
>>                  logger.log(callingClass, levelObjs[level], msg, t);
>> +                logThreadGroup(level, t, msg, module, callingClass);
>>              } else {
>>                  StringBuilder prefixBuf = new StringBuilder();
>>
>>
>> Modified: ofbiz/trunk/framework/service/entitydef/entitymodel.xml
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/entitydef/entitymodel.xml?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/entitydef/entitymodel.xml (original)
>> +++ ofbiz/trunk/framework/service/entitydef/entitymodel.xml Sat Nov 22 03:24:28 2008
>> @@ -64,6 +64,8 @@
>>          <field name="startDateTime" type="date-time"></field>
>>          <field name="finishDateTime" type="date-time"></field>
>>          <field name="cancelDateTime" type="date-time"></field>
>> +        <field name="ownLogfile" type="indicator"></field>
>> +        <field name="logLocation" type="long-varchar"></field>
>>          <prim-key field="jobId"/>
>>          <relation type="one" fk-name="JOB_SNDBX_RECINFO" rel-entity-name="RecurrenceInfo">
>>              <key-map field-name="recurrenceInfoId"/>
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java Sat Nov 22 03:24:28 2008
>> @@ -59,8 +59,18 @@
>>       * @see org.ofbiz.service.LocalDispatcher#schedule(java.lang.String, java.lang.String, java.lang.String, java.util.Map, 
>> long, int, int, int, long, int)
>>       */
>>      public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long 
>> startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException {
>> +        schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry, false);
>> +    }
>> +    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int 
>> count, long endTime, int maxRetry, Object... context) throws GenericServiceException {
>> +        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, 
>> endTime, maxRetry);
>> +    }
>> +
>> +    /**
>> +     * @see org.ofbiz.service.LocalDispatcher#schedule(java.lang.String, java.lang.String, java.lang.String, java.util.Map, 
>> long, int, int, int, long, int, boolean)
>> +     */
>> +    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long 
>> startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws GenericServiceException 
>> {
>>          try {
>> -            getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, 
>> maxRetry);
>> +            getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, 
>> maxRetry, ownLogfile);
>>
>>              if (Debug.verboseOn()) {
>>                  Debug.logVerbose("[LocalDispatcher.schedule] : Current time : " + (new Date()).getTime(), module);
>> @@ -70,15 +80,16 @@
>>                  Debug.logVerbose("[LocalDispatcher.schedule] : Count        : " + count, module);
>>                  Debug.logVerbose("[LocalDispatcher.schedule] : EndTime      : " + endTime, module);
>>                  Debug.logVerbose("[LocalDispatcher.schedule] : MazRetry     : " + maxRetry, module);
>> +                Debug.logVerbose("[LocalDispatcher.schedule] : OwnLogfile     : " + ownLogfile, module);
>>              }
>>
>>          } catch (JobManagerException e) {
>>              throw new GenericServiceException(e.getMessage(), e);
>>          }
>>      }
>> -
>> -    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int 
>> count, long endTime, int maxRetry, Object... context) throws GenericServiceException {
>> -        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, 
>> endTime, maxRetry);
>> +
>> +    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int 
>> count, long endTime, int maxRetry, boolean ownLogfile, Object... context) throws GenericServiceException {
>> +        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, 
>> endTime, maxRetry, ownLogfile);
>>      }
>>
>>      /**
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java Sat Nov 22 03:24:28 2008
>> @@ -228,6 +228,25 @@
>>      public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long 
>> startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException;
>>      public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int 
>> count, long endTime, int maxRetry, Object... context) throws GenericServiceException;
>>
>> +    /**
>> +     * Schedule a service to run asynchronously at a specific start time.
>> +     * @param jobName Name of the job
>> +     * @param poolName Name of the service pool to send to.
>> +     * @param serviceName Name of the service to invoke.
>> +     * @param context The name/value pairs composing the context.
>> +     * @param startTime The time to run this service.
>> +     * @param frequency The frequency of the recurrence (RecurrenceRule.DAILY, etc).
>> +     * @param interval The interval of the frequency recurrence.
>> +     * @param count The number of times to repeat.
>> +     * @param endTime The time in milliseconds the service should expire
>> +     * @param maxRetry The number of times we should retry on failure
>> +     * @param ownLogfile Indicator whether this job uses it's own logfile
>> +     * @throws ServiceAuthException
>> +     * @throws ServiceValidationException
>> +     * @throws GenericServiceException
>> +     */
>> +    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long 
>> startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws 
>> GenericServiceException;
>> +    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int 
>> count, long endTime, int maxRetry, boolean ownLogfile, Object... context) throws GenericServiceException;
>>
>>      /**
>>       * Schedule a service to run asynchronously at a specific start time.
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java Sat Nov 22 03:24:28 2008
>> @@ -38,6 +38,8 @@
>>
>>  import javax.servlet.http.HttpServletRequest;
>>  import javax.transaction.Transaction;
>> +
>> +import java.io.File;
>>  import java.sql.Timestamp;
>>  import java.util.*;
>>
>> @@ -420,7 +422,14 @@
>>                          boolean beganTx2 = false;
>>                          try {
>>                              beganTx2 = TransactionUtil.begin();
>> +                            String logLocation = job.getString("logLocation");
>> +
>>                              job.remove();
>> +
>> +                            if (logLocation != null) {
>> +                                File logfile = new File(job.getString("logLocation"));
>> +                                logfile.delete();
>> +                            }
>
> File?  No, absolutely no.  FlexibleLocation, at the very least.
>
>
>>                              runtimeToDelete.add(runtimeId);
>>                          } catch (GenericEntityException e) {
>>                              Debug.logInfo("Cannot remove job data for ID: " + jobId, module);
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java Sat Nov 22 03:24:28 2008
>> @@ -38,7 +38,9 @@
>>
>>      private String service = null;
>>      private Map<String, Object> context = null;
>> -
>> +
>> +    private String logLocation = null;
>> +
>>      public GenericServiceJob(DispatchContext dctx, String jobId, String jobName, String service, Map<String, Object> context, 
>> GenericRequester req) {
>>          super(jobId, jobName);
>>          this.dctx = dctx;
>> @@ -61,12 +63,18 @@
>>       */
>>      public void exec() throws InvalidJobException {
>>          init();
>> -
>> +        String appenderName = module;
>>          // no transaction is necessary since runSync handles this
>>          try {
>>              // get the dispatcher and invoke the service via runSync -- will run all ECAs
>>              LocalDispatcher dispatcher = dctx.getDispatcher();
>> -            Map<String, Object> result = dispatcher.runSync(getServiceName(), getContext());
>> +
>> +            if (this.logLocation != null) {
>> +                Debug
>> +                .registerCurrentThreadGroupLogger(this.logLocation,
>> +                        appenderName);
>> +            }
>> +            Map result = dispatcher.runSync(getServiceName(), getContext());
>>
>>              // check for a failure
>>              boolean isError = ModelService.RESPOND_ERROR.equals(result.get(ModelService.RESPONSE_MESSAGE));
>> @@ -87,6 +95,8 @@
>>
>>              // call the failed method
>>              this.failed(t);
>> +        }finally{
>> +            Debug.unregisterCurrentThreadGroupLogger(appenderName);
>>          }
>>
>>          // call the finish method
>> @@ -131,5 +141,13 @@
>>       */
>>      protected String getServiceName() throws InvalidJobException {
>>          return service;
>> -    }
>> +    }
>> +
>> +    /**
>> +     * Sets the logfile name
>> +     * @param logLocation
>> +     */
>> +    protected void setLogLocation(String logLocation) {
>> +        this.logLocation = logLocation;
>> +    }
>>  }
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java Sat Nov 22 03:24:28 2008
>> @@ -19,6 +19,7 @@
>>  package org.ofbiz.service.job;
>>
>>  import java.util.Date;
>> +import java.util.Random;
>>
>>  import org.ofbiz.service.config.ServiceConfigUtil;
>>  import org.ofbiz.base.util.Debug;
>> @@ -60,11 +61,11 @@
>>
>>          // service dispatcher delegator name (for thread name)
>>          String delegatorName = jp.getManager().getDelegator().getDelegatorName();
>> -
>> +
>>          // get a new thread
>> -        this.thread = new Thread(this);
>> +        this.thread = new Thread(new ThreadGroup("JobInvoker" + this.hashCode()), this);
>>          this.name = delegatorName + "-invoker-" + this.thread.getName();
>> -
>> +
>>          this.thread.setDaemon(false);
>>          this.thread.setName(this.name);
>>
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java Sat Nov 22 03:24:28 2008
>> @@ -28,6 +28,7 @@
>>  import javolution.util.FastMap;
>>
>>  import org.ofbiz.base.util.Debug;
>> +import org.ofbiz.base.util.FileUtil;
>>  import org.ofbiz.base.util.GeneralRuntimeException;
>>  import org.ofbiz.base.util.UtilDateTime;
>>  import org.ofbiz.base.util.UtilMisc;
>> @@ -310,7 +311,7 @@
>>       *@param endTime The time in milliseconds the service should expire
>>       */
>>      public void schedule(String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int 
>> frequency, int interval, int count, long endTime) throws JobManagerException {
>> -        schedule(null, null, serviceName, context, startTime, frequency, interval, count, endTime, -1);
>> +        schedule(null, null, serviceName, context, startTime, frequency, interval, count, endTime, -1, false);
>>      }
>>
>>      /**
>> @@ -324,9 +325,10 @@
>>       *@param interval The interval of the frequency recurrence
>>       *@param count The number of times to repeat
>>       *@param endTime The time in milliseconds the service should expire
>> +     *@param ownLogfile Indicator whether this job uses it's own logfile
>>       *@param maxRetry The max number of retries on failure (-1 for no max)
>>       */
>> -    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long 
>> startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws JobManagerException {
>> +    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long 
>> startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws JobManagerException {
>>          if (delegator == null) {
>>              Debug.logWarning("No delegator referenced; cannot schedule job.", module);
>>              return;
>> @@ -348,7 +350,7 @@
>>          }
>>
>>          // schedule the job
>> -        schedule(jobName, poolName, serviceName, dataId, startTime, frequency, interval, count, endTime, maxRetry);
>> +        schedule(jobName, poolName, serviceName, dataId, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
>>      }
>>
>>      /**
>> @@ -359,7 +361,7 @@
>>       *@param startTime The time in milliseconds the service should run
>>       */
>>      public void schedule(String poolName, String serviceName, String dataId, long startTime) throws JobManagerException {
>> -        schedule(null, poolName, serviceName, dataId, startTime, -1, 0, 1, 0, -1);
>> +        schedule(null, poolName, serviceName, dataId, startTime, -1, 0, 1, 0, -1, false);
>>      }
>>
>>      /**
>> @@ -374,8 +376,9 @@
>>       *@param count The number of times to repeat
>>       *@param endTime The time in milliseconds the service should expire
>>       *@param maxRetry The max number of retries on failure (-1 for no max)
>> +     *@param ownLogfile Indicator whether this job uses it's own logfile
>>       */
>> -    public void schedule(String jobName, String poolName, String serviceName, String dataId, long startTime, int frequency, int 
>> interval, int count, long endTime, int maxRetry) throws JobManagerException {
>> +    public void schedule(String jobName, String poolName, String serviceName, String dataId, long startTime, int frequency, int 
>> interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws JobManagerException {
>>          if (delegator == null) {
>>              Debug.logWarning("No delegator referenced; cannot schedule job.", module);
>>              return;
>> @@ -411,6 +414,12 @@
>>
>>          // set the max retry
>>          jFields.put("maxRetry", Long.valueOf(maxRetry));
>> +
>> +        if (ownLogfile) {
>> +            jFields.put("ownLogfile", "Y");
>> +        } else {
>> +            jFields.put("ownLogfile", "N");
>> +        }
>>
>>          // create the value and store
>>          GenericValue jobV;
>>
>> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java (original)
>> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java Sat Nov 22 03:24:28 2008
>> @@ -174,6 +174,24 @@
>>              throw new RuntimeException(e.getMessage());
>>          }
>>          if (Debug.infoOn()) Debug.logInfo(this.toString() + "[" + getJobId() + "] -- Next runtime: " + new Date(nextRecurrence), 
>> module);
>> +
>> +        //set the location of the logfile if this was desired
>> +        if ("Y".equals(job.get("ownLogfile"))) {
>> +            String logLocation = System.getProperty("ofbiz.log.dir", "runtime/logs") + "/"
>> +            + getServiceName()
>> +            + "_"
>> +            + UtilDateTime.getTimestamp(System.currentTimeMillis())
>> +                    .toString().trim().replace(" ", "_") + ".html";
>> +            this.setLogLocation(logLocation);
>> +            job.set("logLocation", logLocation);
>> +            try {
>> +                job.store();
>> +            } catch (GenericEntityException e) {
>> +                Debug.logError(e, "Cannot update the job [" + getJobId() + "] sandbox", module);
>> +            }
>> +        }
>> +
>> +
>>      }
>>
>>      private void createRecurrence(GenericValue job, long next) throws GenericEntityException {
>>
>> Modified: ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java (original)
>> +++ ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java Sat Nov 22 03:24:28 2008
>> @@ -216,6 +216,11 @@
>>          String serviceIntr = (String) params.remove("SERVICE_INTERVAL");
>>          String serviceCnt = (String) params.remove("SERVICE_COUNT");
>>          String retryCnt = (String) params.remove("SERVICE_MAXRETRY");
>> +
>> +        boolean ownLogfile = false;
>> +        if ("Y".equals(params.get("OWN_LOGFILE"))) {
>> +            ownLogfile = true;
>> +        }
>>
>>          // the frequency map
>>          Map<String, Integer> freqMap = FastMap.newInstance();
>> @@ -409,7 +414,7 @@
>>              if(null!=request.getParameter("_RUN_SYNC_") && request.getParameter("_RUN_SYNC_").equals("Y")){
>>                  syncServiceResult = dispatcher.runSync(serviceName, serviceContext);
>>              }else{
>> -                dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, 
>> endTime, maxRetry);
>> +                dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, 
>> endTime, maxRetry, ownLogfile);
>>              }
>>          } catch (GenericServiceException e) {
>>              String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_dispatcher_exception", 
>> locale);
>>
>> Modified: ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml (original)
>> +++ ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml Sat Nov 22 03:24:28 2008
>> @@ -1204,6 +1204,10 @@
>>          <value xml:lang="th">รายชื่องาน</value>
>>          <value xml:lang="zh">任务列表</value>
>>      </property>
>> +    <property key="WebtoolsJobLog">
>> +        <value xml:lang="en">Logfile</value>
>> +        <value xml:lang="de">Logdatei</value>
>> +    </property>
>>      <property key="WebtoolsLHSMapName">
>>          <value xml:lang="en">LHS map name</value>
>>          <value xml:lang="it">Nome mappa LHS</value>
>> @@ -1744,6 +1748,10 @@
>>          <value xml:lang="th">Output Directory</value>
>>          <value xml:lang="zh">输出目录</value>
>>      </property>
>> +    <property key="WebtoolsOwnLogfile">
>> +        <value xml:lang="en">Use seperate logfile</value>
>> +        <value xml:lang="de">Eigene Logdatei</value>
>> +    </property>
>>      <property key="WebtoolsParameterName">
>>          <value xml:lang="en">Parameter Name</value>
>>          <value xml:lang="it">Nome Parametro</value>
>>
>> Modified: ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy (original)
>> +++ ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy Sat Nov 22 03:24:28 2008
>> @@ -19,6 +19,13 @@
>>
>>  import org.ofbiz.base.util.FileUtil;
>>
>> +if (parameters.jobId!=null) {
>> +    value = delegator.findByPrimaryKey("JobSandbox", [jobId:parameters.jobId]);
>> +    if (value.getString("logLocation") != null) {
>> +        context.logFileName = value.getString("logLocation");
>> +        logFileName = value.getString("logLocation");
>> +    }
>> +}
>>  sb = null;
>>  try {
>>      sb = FileUtil.readTextFile(logFileName, true);
>>
>> Modified: ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml
>> URL: 
>> http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml?rev=719836&r1=719835&r2=719836&view=diff
>> ==============================================================================
>> --- ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml (original)
>> +++ ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml Sat Nov 22 03:24:28 2008
>> @@ -43,6 +43,7 @@
>>          <field name="SERVICE_INTERVAL" title="${uiLabelMap.WebtoolsInterval}" 
>> tooltip="${uiLabelMap.WebtoolsMessage8}"><text/></field>
>>          <field name="SERVICE_COUNT" title="${uiLabelMap.WebtoolsCount}" tooltip="${uiLabelMap.WebtoolsMessage9}"><text 
>> default-value="1"/></field>
>>          <field name="SERVICE_MAXRETRY" title="${uiLabelMap.WebtoolsMaxRetry}" 
>> tooltip="${uiLabelMap.WebtoolsMessage10}"><text/></field>
>> +        <field name="OWN_LOGFILE" title="${uiLabelMap.WebtoolsOwnLogfile}"><check/></field>
>>          <field name="submitButton" title="${uiLabelMap.CommonSubmit}"><submit button-type="button"/></field>
>>      </form>
>>
>> @@ -72,6 +73,9 @@
>>      <form name="JobList" title="" target="" type="list" list-name="jobs"
>>          paginate-target="jobList" override-list-size="${jobListSize}"
>>          odd-row-style="alternate-row" default-table-style="basic-table hover-bar">
>> +        <row-actions>
>> +            <set field="ownLogfile" from-field="ownLogfile" default-value="N"/>
>> +        </row-actions>
>>          <field name="jobName" title="${uiLabelMap.WebtoolsJob}"><display/></field>
>>          <field name="jobId" title="${uiLabelMap.CommonId}"><display/></field>
>>          <field name="poolId" title="${uiLabelMap.WebtoolsPool}"><display/></field>
>> @@ -82,8 +86,11 @@
>>          </field>
>>          <field name="statusId" title="${uiLabelMap.CommonStatus}"><display-entity entity-name="StatusItem" 
>> description="${description}"/></field>
>>          <field name="cancelDateTime" title="${uiLabelMap.CommonEndDateTime}"><display/></field>
>> +        <field name="ownLogfile" title="${uiLabelMap.WebtoolsOwnLogfile}"><display/></field>
>> +        <field name="logFile" use-when="logLocation != null" title="${uiLabelMap.WebtoolsJobLog}"><hyperlink 
>> target="LogView?jobId=${jobId}" description="${uiLabelMap.WebtoolsJobLog}"/></field>
>> +        <field name="logFile" use-when="logLocation == null" title="${uiLabelMap.WebtoolsJobLog}"><display/></field>
>>          <field name="cancelButton" title="${uiLabelMap.CommonEmptyHeader}" 
>> use-when="startDateTime==null&amp;&amp;finishDateTime==null&amp;&amp;cancelDateTime==null" widget-style="buttontext">
>>              <hyperlink also-hidden="false" description="${uiLabelMap.WebtoolsCancelJob}" target="cancelJob?jobId=${jobId}"/>
>>          </field>
>>      </form>
>> -</forms>
>> \ No newline at end of file
>> +</forms>
>>
>>
> 


Re: svn commit: r719836 - in /ofbiz/trunk/framework: base/src/org/ofbiz/base/util/ service/entitydef/ service/src/org/ofbiz/service/ service/src/org/ofbiz/service/job/ webapp/src/org/ofbiz/webapp/event/ webtools/config/ webtools/webapp/webtools/WEB-INF/act...

Posted by Adam Heath <do...@brainfood.com>.
jleroux@apache.org wrote:
> Author: jleroux
> Date: Sat Nov 22 03:24:28 2008
> New Revision: 719836
> 
> URL: http://svn.apache.org/viewvc?rev=719836&view=rev
> Log:
> A patch from Philipp Hoppen " Individual logfiles for scheduled jobs" (https://issues.apache.org/jira/browse/OFBIZ-2042) - OFBIZ-2042

Sorry for the late reply, but please revert this patch, it has several
issues; see comments inline.

As a general comment, this should use an InheritableThreadLocal, not a
string-key-managed map.


> Modified:
>     ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java
>     ofbiz/trunk/framework/service/entitydef/entitymodel.xml
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java
>     ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java
>     ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java
>     ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml
>     ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy
>     ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml
> 
> Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java (original)
> +++ ofbiz/trunk/framework/base/src/org/ofbiz/base/util/Debug.java Sat Nov 22 03:24:28 2008
> @@ -25,6 +25,7 @@
>  import java.util.Enumeration;
>  import java.util.HashMap;
>  import java.util.Map;
> +import java.util.Vector;
>  
>  import org.apache.avalon.util.exception.ExceptionHelper;
>  import org.apache.log4j.Level;
> @@ -70,6 +71,8 @@
>      protected static final boolean useLevelOnCache = true;
>      
>      protected static Logger root = Logger.getRootLogger();
> +    
> +    private static Map<String, Vector<Appender>> activeThreadGroupLoggerMap = new HashMap<String, Vector<Appender>>();

This variable should not be using a Vector; yes, a single call to a
Vector's method is synchronized, but multiple calls in series are *not*.

In addition, activeThreadGroupLoggerMap is not synchronized at all either.

Plus, there are memory leaks with this code, as when are items removed
from the map?

>  
>      static {
>          levelStringMap.put("verbose", Debug.VERBOSE);
> @@ -128,6 +131,108 @@
>              return root;
>          }
>      }
> +    
> +    /**
> +     * Checks if a logger exists and if it is in activeThreadGroupLoggerMap
> +     * @param threadGroupId a thread group id
> +     * @return
> +     */
> +    private static boolean hasActiveThreadLogger(String threadGroupId) {
> +        //used so entries are not doubled in rootAppenders
> +        Logger threadGroupLogger = org.apache.log4j.LogManager
> +                .exists(threadGroupId);
> +        return threadGroupLogger != null
> +                && (activeThreadGroupLoggerMap.get(threadGroupId) != null && activeThreadGroupLoggerMap
> +                        .get(threadGroupId).size() > 0);
> +    }
> +
> +    private static Appender getAppender(String threadGroupId,
> +            String appenderName) {
> +        Vector<Appender> appenders = activeThreadGroupLoggerMap
> +                .get(threadGroupId);
> +        if (appenders != null) {
> +            for (Appender appender : appenders) {
> +                if (appender != null && appender.getName().equals(appenderName)) {
> +                    return appender;
> +                }
> +            }
> +        }
> +        return null;
> +    }
> +
> +    private static void addAppenderToThreadGroupMap(String threadGroupId,
> +            Appender appender) {
> +        Vector<Appender> appenders = activeThreadGroupLoggerMap.get(threadGroupId);
> +        if (appenders == null) {
> +            appenders = new Vector<Appender>();
> +        }
> +        appenders.add(appender);
> +        activeThreadGroupLoggerMap.put(threadGroupId, appenders);
> +    }
> +
> +    private static void removeAppenderFromThreadGroupMap(String threadGroupId,
> +            Appender appender) {
> +        Vector<Appender> appenders = activeThreadGroupLoggerMap.get(threadGroupId);
> +        if (appenders != null && appenders.contains(appender)) {
> +            appenders.remove(appender);
> +            activeThreadGroupLoggerMap.put(threadGroupId, appenders);
> +        }
> +    }
> +
> +    private static String currentThreadGroupId() {
> +        return "" + Thread.currentThread().getThreadGroup().hashCode();
> +    }

Integer.toString(), not "" + int.


> +
> +    public static void registerCurrentThreadGroupLogger(String logFile, String appenderName) {
> +        String pattern = "<div class=%p>%d (%t) [%24F:%-3L:%-5p]%x %m </div>%n";
> +        registerThreadAppender(getNewFileAppender(appenderName,
> +                logFile, 0, 0, pattern));
> +    }
> +
> +    public static void registerThreadAppender(Appender appender) {
> +        String threadGroupId = currentThreadGroupId();
> +        
> +        if (threadGroupId != null && threadGroupId.length() > 0) {
> +            Logger theLogger = getLogger(threadGroupId);
> +            if (theLogger != null) {
> +                theLogger.setAdditivity(false);
> +                theLogger.addAppender(appender);
> +                addAppenderToThreadGroupMap(threadGroupId, appender);
> +            }
> +        }
> +    }
> +
> +    public static void unregisterCurrentThreadGroupLogger(String appenderName) {
> +        String threadGroupId = currentThreadGroupId();
> +        Appender foundAppender = getAppender(threadGroupId, appenderName);
> +        unregisterThreadAppender(foundAppender);
> +    }
> +
> +    public static void unregisterThreadAppender(Appender appender) {
> +        String threadGroupId = currentThreadGroupId();
> +        if (threadGroupId != null && threadGroupId.length() > 0
> +                && appender != null) {
> +            Logger theLogger = getLogger(threadGroupId);
> +            theLogger.removeAppender(appender);
> +            removeAppenderFromThreadGroupMap(threadGroupId, appender);
> +        }
> +    }
> +
> +    /**
> +     * Thread-specific logging
> +     */
> +    private static void logThreadGroup(int level, Throwable t, String msg, String module, 
> +            String callingClass) {
> +        String threadGroupId = currentThreadGroupId();
> +        if (hasActiveThreadLogger(threadGroupId)) {
> +            Logger grplogger = getLogger(threadGroupId + "." + module);
> +            if (SYS_DEBUG != null) {
> +                grplogger.setLevel(Level.DEBUG);
> +            }
> +            grplogger.log(callingClass, levelObjs[level], msg, t);
> +        }
> +    }
> +    
>  
>      /** Gets an Integer representing the level number from a String representing the level name; will return null if not found */
>      public static Integer getLevelFromString(String levelName) {
> @@ -164,6 +269,7 @@
>                      logger.setLevel(Level.DEBUG);
>                  }
>                  logger.log(callingClass, levelObjs[level], msg, t);
> +                logThreadGroup(level, t, msg, module, callingClass);
>              } else {
>                  StringBuilder prefixBuf = new StringBuilder();
>  
> 
> Modified: ofbiz/trunk/framework/service/entitydef/entitymodel.xml
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/entitydef/entitymodel.xml?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/entitydef/entitymodel.xml (original)
> +++ ofbiz/trunk/framework/service/entitydef/entitymodel.xml Sat Nov 22 03:24:28 2008
> @@ -64,6 +64,8 @@
>          <field name="startDateTime" type="date-time"></field>
>          <field name="finishDateTime" type="date-time"></field>
>          <field name="cancelDateTime" type="date-time"></field>
> +        <field name="ownLogfile" type="indicator"></field>
> +        <field name="logLocation" type="long-varchar"></field>
>          <prim-key field="jobId"/>
>          <relation type="one" fk-name="JOB_SNDBX_RECINFO" rel-entity-name="RecurrenceInfo">
>              <key-map field-name="recurrenceInfoId"/>
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/GenericAbstractDispatcher.java Sat Nov 22 03:24:28 2008
> @@ -59,8 +59,18 @@
>       * @see org.ofbiz.service.LocalDispatcher#schedule(java.lang.String, java.lang.String, java.lang.String, java.util.Map, long, int, int, int, long, int)
>       */
>      public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException {
> +        schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry, false);
> +    }
> +    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, Object... context) throws GenericServiceException {
> +        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, endTime, maxRetry);
> +    }
> +    
> +    /**
> +     * @see org.ofbiz.service.LocalDispatcher#schedule(java.lang.String, java.lang.String, java.lang.String, java.util.Map, long, int, int, int, long, int, boolean)
> +     */
> +    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws GenericServiceException {
>          try {
> -            getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry);
> +            getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
>                  
>              if (Debug.verboseOn()) {
>                  Debug.logVerbose("[LocalDispatcher.schedule] : Current time : " + (new Date()).getTime(), module);
> @@ -70,15 +80,16 @@
>                  Debug.logVerbose("[LocalDispatcher.schedule] : Count        : " + count, module);
>                  Debug.logVerbose("[LocalDispatcher.schedule] : EndTime      : " + endTime, module);
>                  Debug.logVerbose("[LocalDispatcher.schedule] : MazRetry     : " + maxRetry, module);
> +                Debug.logVerbose("[LocalDispatcher.schedule] : OwnLogfile     : " + ownLogfile, module);
>              }
>              
>          } catch (JobManagerException e) {
>              throw new GenericServiceException(e.getMessage(), e);
>          }
>      }
> -
> -    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, Object... context) throws GenericServiceException {
> -        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, endTime, maxRetry);
> +    
> +    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile, Object... context) throws GenericServiceException {
> +        schedule(jobName, poolName, serviceName, ServiceUtil.makeContext(context), startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
>      }
>  
>      /**
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/LocalDispatcher.java Sat Nov 22 03:24:28 2008
> @@ -228,6 +228,25 @@
>      public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException;
>      public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, Object... context) throws GenericServiceException;
>  
> +    /**
> +     * Schedule a service to run asynchronously at a specific start time.
> +     * @param jobName Name of the job
> +     * @param poolName Name of the service pool to send to.
> +     * @param serviceName Name of the service to invoke.
> +     * @param context The name/value pairs composing the context.
> +     * @param startTime The time to run this service.
> +     * @param frequency The frequency of the recurrence (RecurrenceRule.DAILY, etc).
> +     * @param interval The interval of the frequency recurrence.
> +     * @param count The number of times to repeat.
> +     * @param endTime The time in milliseconds the service should expire
> +     * @param maxRetry The number of times we should retry on failure
> +     * @param ownLogfile Indicator whether this job uses it's own logfile
> +     * @throws ServiceAuthException
> +     * @throws ServiceValidationException
> +     * @throws GenericServiceException
> +     */
> +    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws GenericServiceException;
> +    public void schedule(String jobName, String poolName, String serviceName, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile, Object... context) throws GenericServiceException;
>  
>      /**
>       * Schedule a service to run asynchronously at a specific start time.
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceUtil.java Sat Nov 22 03:24:28 2008
> @@ -38,6 +38,8 @@
>  
>  import javax.servlet.http.HttpServletRequest;
>  import javax.transaction.Transaction;
> +
> +import java.io.File;
>  import java.sql.Timestamp;
>  import java.util.*;
>  
> @@ -420,7 +422,14 @@
>                          boolean beganTx2 = false;
>                          try {
>                              beganTx2 = TransactionUtil.begin();
> +                            String logLocation = job.getString("logLocation");
> +                            
>                              job.remove();
> +                            
> +                            if (logLocation != null) {
> +                                File logfile = new File(job.getString("logLocation"));
> +                                logfile.delete();
> +                            }

File?  No, absolutely no.  FlexibleLocation, at the very least.


>                              runtimeToDelete.add(runtimeId);
>                          } catch (GenericEntityException e) {
>                              Debug.logInfo("Cannot remove job data for ID: " + jobId, module);
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/GenericServiceJob.java Sat Nov 22 03:24:28 2008
> @@ -38,7 +38,9 @@
>  
>      private String service = null;
>      private Map<String, Object> context = null;
> -
> +    
> +    private String logLocation = null;
> +        
>      public GenericServiceJob(DispatchContext dctx, String jobId, String jobName, String service, Map<String, Object> context, GenericRequester req) {
>          super(jobId, jobName);
>          this.dctx = dctx;
> @@ -61,12 +63,18 @@
>       */
>      public void exec() throws InvalidJobException {
>          init();
> -
> +        String appenderName = module;
>          // no transaction is necessary since runSync handles this
>          try {
>              // get the dispatcher and invoke the service via runSync -- will run all ECAs
>              LocalDispatcher dispatcher = dctx.getDispatcher();
> -            Map<String, Object> result = dispatcher.runSync(getServiceName(), getContext());
> +            
> +            if (this.logLocation != null) {
> +                Debug
> +                .registerCurrentThreadGroupLogger(this.logLocation,
> +                        appenderName);
> +            }
> +            Map result = dispatcher.runSync(getServiceName(), getContext());
>  
>              // check for a failure
>              boolean isError = ModelService.RESPOND_ERROR.equals(result.get(ModelService.RESPONSE_MESSAGE));
> @@ -87,6 +95,8 @@
>  
>              // call the failed method
>              this.failed(t);
> +        }finally{
> +            Debug.unregisterCurrentThreadGroupLogger(appenderName);
>          }
>  
>          // call the finish method
> @@ -131,5 +141,13 @@
>       */
>      protected String getServiceName() throws InvalidJobException {
>          return service;
> -    }        
> +    }
> +    
> +    /**
> +     * Sets the logfile name
> +     * @param logLocation
> +     */
> +    protected void setLogLocation(String logLocation) {
> +        this.logLocation = logLocation;
> +    }
>  }
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobInvoker.java Sat Nov 22 03:24:28 2008
> @@ -19,6 +19,7 @@
>  package org.ofbiz.service.job;
>  
>  import java.util.Date;
> +import java.util.Random;
>  
>  import org.ofbiz.service.config.ServiceConfigUtil;
>  import org.ofbiz.base.util.Debug;
> @@ -60,11 +61,11 @@
>  
>          // service dispatcher delegator name (for thread name)
>          String delegatorName = jp.getManager().getDelegator().getDelegatorName();
> -
> +        
>          // get a new thread
> -        this.thread = new Thread(this);
> +        this.thread = new Thread(new ThreadGroup("JobInvoker" + this.hashCode()), this);
>          this.name = delegatorName + "-invoker-" + this.thread.getName();
> -
> +        
>          this.thread.setDaemon(false);
>          this.thread.setName(this.name);
>  
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/JobManager.java Sat Nov 22 03:24:28 2008
> @@ -28,6 +28,7 @@
>  import javolution.util.FastMap;
>  
>  import org.ofbiz.base.util.Debug;
> +import org.ofbiz.base.util.FileUtil;
>  import org.ofbiz.base.util.GeneralRuntimeException;
>  import org.ofbiz.base.util.UtilDateTime;
>  import org.ofbiz.base.util.UtilMisc;
> @@ -310,7 +311,7 @@
>       *@param endTime The time in milliseconds the service should expire
>       */
>      public void schedule(String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime) throws JobManagerException {
> -        schedule(null, null, serviceName, context, startTime, frequency, interval, count, endTime, -1);
> +        schedule(null, null, serviceName, context, startTime, frequency, interval, count, endTime, -1, false);
>      }
>  
>      /**
> @@ -324,9 +325,10 @@
>       *@param interval The interval of the frequency recurrence
>       *@param count The number of times to repeat
>       *@param endTime The time in milliseconds the service should expire
> +     *@param ownLogfile Indicator whether this job uses it's own logfile
>       *@param maxRetry The max number of retries on failure (-1 for no max)
>       */
> -    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws JobManagerException {
> +    public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws JobManagerException {
>          if (delegator == null) {
>              Debug.logWarning("No delegator referenced; cannot schedule job.", module);
>              return;
> @@ -348,7 +350,7 @@
>          }
>  
>          // schedule the job
> -        schedule(jobName, poolName, serviceName, dataId, startTime, frequency, interval, count, endTime, maxRetry);
> +        schedule(jobName, poolName, serviceName, dataId, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
>      }
>  
>      /**
> @@ -359,7 +361,7 @@
>       *@param startTime The time in milliseconds the service should run
>       */
>      public void schedule(String poolName, String serviceName, String dataId, long startTime) throws JobManagerException {
> -        schedule(null, poolName, serviceName, dataId, startTime, -1, 0, 1, 0, -1);
> +        schedule(null, poolName, serviceName, dataId, startTime, -1, 0, 1, 0, -1, false);
>      }
>  
>      /**
> @@ -374,8 +376,9 @@
>       *@param count The number of times to repeat
>       *@param endTime The time in milliseconds the service should expire
>       *@param maxRetry The max number of retries on failure (-1 for no max)
> +     *@param ownLogfile Indicator whether this job uses it's own logfile
>       */
> -    public void schedule(String jobName, String poolName, String serviceName, String dataId, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws JobManagerException {
> +    public void schedule(String jobName, String poolName, String serviceName, String dataId, long startTime, int frequency, int interval, int count, long endTime, int maxRetry, boolean ownLogfile) throws JobManagerException {
>          if (delegator == null) {
>              Debug.logWarning("No delegator referenced; cannot schedule job.", module);
>              return;
> @@ -411,6 +414,12 @@
>  
>          // set the max retry
>          jFields.put("maxRetry", Long.valueOf(maxRetry));
> +        
> +        if (ownLogfile) {
> +            jFields.put("ownLogfile", "Y");
> +        } else {
> +            jFields.put("ownLogfile", "N");
> +        }
>  
>          // create the value and store
>          GenericValue jobV;
> 
> Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java (original)
> +++ ofbiz/trunk/framework/service/src/org/ofbiz/service/job/PersistedServiceJob.java Sat Nov 22 03:24:28 2008
> @@ -174,6 +174,24 @@
>              throw new RuntimeException(e.getMessage());
>          }
>          if (Debug.infoOn()) Debug.logInfo(this.toString() + "[" + getJobId() + "] -- Next runtime: " + new Date(nextRecurrence), module);
> +    
> +        //set the location of the logfile if this was desired
> +        if ("Y".equals(job.get("ownLogfile"))) {
> +            String logLocation = System.getProperty("ofbiz.log.dir", "runtime/logs") + "/"
> +            + getServiceName()
> +            + "_"
> +            + UtilDateTime.getTimestamp(System.currentTimeMillis())
> +                    .toString().trim().replace(" ", "_") + ".html";
> +            this.setLogLocation(logLocation);
> +            job.set("logLocation", logLocation);
> +            try {
> +                job.store();
> +            } catch (GenericEntityException e) {
> +                Debug.logError(e, "Cannot update the job [" + getJobId() + "] sandbox", module);
> +            }
> +        }
> +        
> +    
>      }
>  
>      private void createRecurrence(GenericValue job, long next) throws GenericEntityException {
> 
> Modified: ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java (original)
> +++ ofbiz/trunk/framework/webapp/src/org/ofbiz/webapp/event/CoreEvents.java Sat Nov 22 03:24:28 2008
> @@ -216,6 +216,11 @@
>          String serviceIntr = (String) params.remove("SERVICE_INTERVAL");
>          String serviceCnt = (String) params.remove("SERVICE_COUNT");
>          String retryCnt = (String) params.remove("SERVICE_MAXRETRY");
> +        
> +        boolean ownLogfile = false;
> +        if ("Y".equals(params.get("OWN_LOGFILE"))) {
> +            ownLogfile = true;
> +        }
>  
>          // the frequency map
>          Map<String, Integer> freqMap = FastMap.newInstance();
> @@ -409,7 +414,7 @@
>              if(null!=request.getParameter("_RUN_SYNC_") && request.getParameter("_RUN_SYNC_").equals("Y")){
>                  syncServiceResult = dispatcher.runSync(serviceName, serviceContext);
>              }else{
> -                dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, endTime, maxRetry);
> +                dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, endTime, maxRetry, ownLogfile);
>              }
>          } catch (GenericServiceException e) {
>              String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_dispatcher_exception", locale);
> 
> Modified: ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml (original)
> +++ ofbiz/trunk/framework/webtools/config/WebtoolsUiLabels.xml Sat Nov 22 03:24:28 2008
> @@ -1204,6 +1204,10 @@
>          <value xml:lang="th">รายชื่องาน</value>
>          <value xml:lang="zh">任务列表</value>
>      </property>
> +    <property key="WebtoolsJobLog">
> +        <value xml:lang="en">Logfile</value>
> +        <value xml:lang="de">Logdatei</value>
> +    </property>
>      <property key="WebtoolsLHSMapName">
>          <value xml:lang="en">LHS map name</value>
>          <value xml:lang="it">Nome mappa LHS</value>
> @@ -1744,6 +1748,10 @@
>          <value xml:lang="th">Output Directory</value>
>          <value xml:lang="zh">输出目录</value>
>      </property>
> +    <property key="WebtoolsOwnLogfile">
> +        <value xml:lang="en">Use seperate logfile</value>
> +        <value xml:lang="de">Eigene Logdatei</value>
> +    </property>
>      <property key="WebtoolsParameterName">
>          <value xml:lang="en">Parameter Name</value>
>          <value xml:lang="it">Nome Parametro</value>
> 
> Modified: ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy (original)
> +++ ofbiz/trunk/framework/webtools/webapp/webtools/WEB-INF/actions/log/LogView.groovy Sat Nov 22 03:24:28 2008
> @@ -19,6 +19,13 @@
>  
>  import org.ofbiz.base.util.FileUtil;
>  
> +if (parameters.jobId!=null) {
> +    value = delegator.findByPrimaryKey("JobSandbox", [jobId:parameters.jobId]);
> +    if (value.getString("logLocation") != null) {
> +        context.logFileName = value.getString("logLocation");
> +        logFileName = value.getString("logLocation");
> +    }
> +}
>  sb = null;
>  try {
>      sb = FileUtil.readTextFile(logFileName, true);
> 
> Modified: ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml?rev=719836&r1=719835&r2=719836&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml (original)
> +++ ofbiz/trunk/framework/webtools/webapp/webtools/service/ServiceForms.xml Sat Nov 22 03:24:28 2008
> @@ -43,6 +43,7 @@
>          <field name="SERVICE_INTERVAL" title="${uiLabelMap.WebtoolsInterval}" tooltip="${uiLabelMap.WebtoolsMessage8}"><text/></field>
>          <field name="SERVICE_COUNT" title="${uiLabelMap.WebtoolsCount}" tooltip="${uiLabelMap.WebtoolsMessage9}"><text default-value="1"/></field>
>          <field name="SERVICE_MAXRETRY" title="${uiLabelMap.WebtoolsMaxRetry}" tooltip="${uiLabelMap.WebtoolsMessage10}"><text/></field>
> +        <field name="OWN_LOGFILE" title="${uiLabelMap.WebtoolsOwnLogfile}"><check/></field>
>          <field name="submitButton" title="${uiLabelMap.CommonSubmit}"><submit button-type="button"/></field> 
>      </form>
>  
> @@ -72,6 +73,9 @@
>      <form name="JobList" title="" target="" type="list" list-name="jobs"
>          paginate-target="jobList" override-list-size="${jobListSize}"
>          odd-row-style="alternate-row" default-table-style="basic-table hover-bar">
> +        <row-actions>
> +            <set field="ownLogfile" from-field="ownLogfile" default-value="N"/>
> +        </row-actions>
>          <field name="jobName" title="${uiLabelMap.WebtoolsJob}"><display/></field>
>          <field name="jobId" title="${uiLabelMap.CommonId}"><display/></field>
>          <field name="poolId" title="${uiLabelMap.WebtoolsPool}"><display/></field>
> @@ -82,8 +86,11 @@
>          </field>
>          <field name="statusId" title="${uiLabelMap.CommonStatus}"><display-entity entity-name="StatusItem" description="${description}"/></field>
>          <field name="cancelDateTime" title="${uiLabelMap.CommonEndDateTime}"><display/></field>
> +        <field name="ownLogfile" title="${uiLabelMap.WebtoolsOwnLogfile}"><display/></field>
> +        <field name="logFile" use-when="logLocation != null" title="${uiLabelMap.WebtoolsJobLog}"><hyperlink target="LogView?jobId=${jobId}" description="${uiLabelMap.WebtoolsJobLog}"/></field>
> +        <field name="logFile" use-when="logLocation == null" title="${uiLabelMap.WebtoolsJobLog}"><display/></field>
>          <field name="cancelButton" title="${uiLabelMap.CommonEmptyHeader}" use-when="startDateTime==null&amp;&amp;finishDateTime==null&amp;&amp;cancelDateTime==null" widget-style="buttontext">
>              <hyperlink also-hidden="false" description="${uiLabelMap.WebtoolsCancelJob}" target="cancelJob?jobId=${jobId}"/>
>          </field>
>      </form>
> -</forms>
> \ No newline at end of file
> +</forms>
> 
>