001/**
002 * Copyright 2014 Tampere University of Technology, Pori Department
003 * 
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 *   http://www.apache.org/licenses/LICENSE-2.0
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package service.tut.pori.contentanalysis;
017
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.List;
021import java.util.Map;
022import java.util.Map.Entry;
023
024import org.apache.commons.lang3.StringUtils;
025import org.apache.commons.lang3.tuple.Pair;
026import org.apache.log4j.Logger;
027import org.springframework.beans.factory.annotation.Autowired;
028import org.springframework.jdbc.core.JdbcTemplate;
029import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
030import org.springframework.transaction.TransactionStatus;
031import org.springframework.transaction.support.TransactionCallback;
032
033import service.tut.pori.contentanalysis.AbstractTaskDetails.TaskParameters;
034import service.tut.pori.contentanalysis.AsyncTask.TaskStatus;
035import service.tut.pori.contentanalysis.AsyncTask.TaskType;
036import core.tut.pori.dao.SQLDAO;
037import core.tut.pori.dao.clause.SQLClause.SQLType;
038import core.tut.pori.http.parameters.DataGroups;
039import core.tut.pori.http.parameters.Limits;
040import core.tut.pori.users.UserIdentity;
041
042/**
043 * Base class for task DAOs
044 * 
045 * Note that this class is <i>NOT</i> abstract, and it can be used to implement core task details ({@link service.tut.pori.contentanalysis.AbstractTaskDetails}),
046 * but the class is not guaranteed to function with extended capabilities provided by inherited classes. Use the service specific implementation when needed.
047 * 
048 */
049public class TaskDAO extends SQLDAO {
050  private static final Logger LOGGER = Logger.getLogger(TaskDAO.class);
051  private static final String METADATA_TASK_PARAMETER_CLASS = "METADATA_TP_CLS";
052  /* tables */
053  private static final String TABLE_TASKS = DATABASE +".ca_tasks";
054  private static final String TABLE_TASK_BACKENDS = DATABASE +".ca_tasks_backends";
055  private static final String TABLE_TASK_METADATA = DATABASE +".ca_tasks_metadata";
056  /* columns */
057  /** default column name for status messages */
058  protected static final String COLUMN_MESSAGE = "message";
059  /** default column name for task ids */
060  protected static final String COLUMN_TASK_ID = "task_id";
061  /** default column name for task type */
062  protected static final String COLUMN_TASK_TYPE = "task_type";
063  private static final String[] SQL_COLUMNS_INSERT_TASK = new String[]{COLUMN_TASK_TYPE, COLUMN_ROW_CREATED, COLUMN_USER_ID};
064
065  /* sql strings */
066  private static final String SQL_CHECK_TASK_BACKEND = "SELECT "+COLUMN_COUNT+" FROM "+TABLE_TASK_BACKENDS+" WHERE "+Definitions.COLUMN_BACKEND_ID+"=? AND "+COLUMN_TASK_ID+"=? LIMIT 1";
067  private static final int[] SQL_CHECK_TASK_BACKEND_TYPES = new int[]{SQLType.INTEGER.toInt(), SQLType.LONG.toInt()};
068  
069  private static final String SQL_GET_BACKEND_STATUSES = "SELECT "+Definitions.COLUMN_BACKEND_ID+", "+Definitions.COLUMN_STATUS+", "+COLUMN_MESSAGE+" FROM "+TABLE_TASK_BACKENDS+" WHERE "+COLUMN_TASK_ID+"=?";
070  private static final int[] SQL_GET_BACKEND_STATUSES_SQL_TYPES = new int[]{SQLType.LONG.toInt()};
071
072  private static final String SQL_INSERT_TASK_BACKEND = "INSERT INTO "+TABLE_TASK_BACKENDS+" ("+COLUMN_TASK_ID+", "+Definitions.COLUMN_BACKEND_ID+", "+Definitions.COLUMN_STATUS+", "+COLUMN_MESSAGE+", "+COLUMN_ROW_CREATED+") VALUES (?,?,?,?,NOW())";
073  private static final int[] SQL_INSERT_TASK_BACKEND_TYPES = new int[]{SQLType.LONG.toInt(), SQLType.INTEGER.toInt(), SQLType.INTEGER.toInt(), SQLType.STRING.toInt()};
074
075  private static final String SQL_INSERT_TASK_METADATA = "INSERT INTO "+TABLE_TASK_METADATA+" ("+COLUMN_TASK_ID+", "+Definitions.COLUMN_NAME+", "+Definitions.COLUMN_VALUE+", "+COLUMN_ROW_CREATED+") VALUES (?,?,?,NOW())";
076  private static final int[] SQL_INSERT_TASK_METADATA_TYPES = new int[]{SQLType.LONG.toInt(), SQLType.STRING.toInt(), SQLType.STRING.toInt()};
077
078  private static final String SQL_SELECT_BACKEND_STATUS_BY_BACKEND_ID = "SELECT "+Definitions.COLUMN_STATUS+", "+COLUMN_MESSAGE+", "+Definitions.COLUMN_BACKEND_ID+" FROM "+TABLE_TASK_BACKENDS+" WHERE "+Definitions.COLUMN_BACKEND_ID+"=? AND "+COLUMN_TASK_ID+"=? LIMIT 1";
079  private static final int[] SQL_SELECT_BACKEND_STATUS_BY_BACKEND_ID_TYPES = new int[]{SQLType.INTEGER.toInt(), SQLType.LONG.toInt()};
080
081  private static final String SQL_SELECT_BACKEND_STATUS_BY_TASK_ID = "SELECT "+Definitions.COLUMN_STATUS+", "+COLUMN_MESSAGE+", "+Definitions.COLUMN_BACKEND_ID+" FROM "+TABLE_TASK_BACKENDS+" WHERE "+COLUMN_TASK_ID+"=?";
082  private static final int[] SQL_SELECT_BACKEND_STATUS_BY_TASK_ID_TYPES = new int[]{SQLType.LONG.toInt()};
083
084  private static final String SQL_SELECT_BACKEND_STATUS_BY_TASK_STATUS = "SELECT "+Definitions.COLUMN_STATUS+", "+COLUMN_MESSAGE+", "+Definitions.COLUMN_BACKEND_ID+" FROM "+TABLE_TASK_BACKENDS+" WHERE "+Definitions.COLUMN_STATUS+"=? AND "+COLUMN_TASK_ID+"=?";
085  private static final int[] SQL_SELECT_BACKEND_STATUS_BY_TASK_STATUS_TYPES = new int[]{SQLType.INTEGER.toInt(), SQLType.LONG.toInt()};
086
087  private static final String SQL_SELECT_TASK_METADATA = "SELECT "+Definitions.COLUMN_NAME+", "+Definitions.COLUMN_VALUE+" FROM "+TABLE_TASK_METADATA+" WHERE "+COLUMN_TASK_ID+"=?";
088  private static final int[] SQL_SELECT_TASK_METADATA_TYPES = new int[]{SQLType.LONG.toInt()};
089
090  private static final String SQL_UPDATE_TASK_STATUS = "UPDATE "+TABLE_TASK_BACKENDS+" SET "+Definitions.COLUMN_STATUS+"=?, "+COLUMN_MESSAGE+"=? WHERE "+COLUMN_TASK_ID+"=? AND "+Definitions.COLUMN_BACKEND_ID+"=? LIMIT 1";
091  private static final int[] SQL_UPDATE_TASK_STATUS_TYPES = new int[]{SQLType.INTEGER.toInt(), SQLType.STRING.toInt(), SQLType.LONG.toInt(), SQLType.INTEGER.toInt()};
092  
093  private static final String SQL_SELECT_TASK_TYPE = "SELECT "+COLUMN_TASK_TYPE+", "+COLUMN_USER_ID+" FROM "+TABLE_TASKS+" WHERE "+COLUMN_TASK_ID+"=?";
094  private static final int[] SQL_SELECT_TASK_TYPE_TYPES = new int[]{SQLType.LONG.toInt()};
095
096  private static final String SQL_SELECT_TASK_TYPE_BY_BACKEND_ID = "SELECT "+TABLE_TASKS+"."+COLUMN_TASK_TYPE+", "+COLUMN_USER_ID+" FROM "+TABLE_TASKS+" INNER JOIN "+TABLE_TASK_BACKENDS+" ON "+TABLE_TASKS+"."+COLUMN_TASK_ID+"="+TABLE_TASK_BACKENDS+"."+COLUMN_TASK_ID+" WHERE "+TABLE_TASK_BACKENDS+"."+Definitions.COLUMN_BACKEND_ID+"=? AND "+TABLE_TASK_BACKENDS+"."+COLUMN_TASK_ID+"=? LIMIT 1";
097  private static final int[] SQL_SELECT_TASK_TYPE_BY_BACKEND_ID_TYPES = new int[]{SQLType.INTEGER.toInt(), SQLType.LONG.toInt()};
098
099  @Autowired
100  private BackendDAO _backendDAO = null;
101
102  /**
103   * 
104   * @param taskId
105   * @param taskStatus optional status filter, if null all back-ends matching the given id will be returned
106   * @return list of back-ends associated with the given taskId
107   */
108  public BackendStatusList getBackendStatus(Long taskId, TaskStatus taskStatus){
109    List<Map<String, Object>> rows = null;
110    if(taskStatus == null){
111      rows = getJdbcTemplate().queryForList(SQL_SELECT_BACKEND_STATUS_BY_TASK_ID, new Object[]{taskId}, SQL_SELECT_BACKEND_STATUS_BY_TASK_ID_TYPES);
112    }else{
113      rows = getJdbcTemplate().queryForList(SQL_SELECT_BACKEND_STATUS_BY_TASK_STATUS, new Object[]{taskStatus.toInt(), taskId}, SQL_SELECT_BACKEND_STATUS_BY_TASK_STATUS_TYPES);
114    }
115
116    if(rows.isEmpty()){
117      return null;
118    }else{
119      BackendStatusList list = new BackendStatusList();
120      for(Iterator<Map<String,Object>> iter = rows.iterator(); iter.hasNext();){
121        BackendStatus backendStatus = extractBackendStatus(iter.next());
122        if(backendStatus == null){
123          LOGGER.warn("Ignored non-existing back-end.");
124        }else{
125          list.setBackendStatus(backendStatus);
126        }
127      }
128      if(BackendStatusList.isEmpty(list)){
129        LOGGER.warn("No valid backends for task, id: "+taskId);
130        return null;
131      }else{
132        return list;
133      } // else 
134    } // else
135  }
136
137  /**
138   * extract new BackendStatus from the given row
139   * 
140   * @param row
141   * @return status extracted from the given row map
142   */
143  private BackendStatus extractBackendStatus(Map<String, Object> row){
144    BackendStatus s = new BackendStatus();
145    for(Entry<String, Object> e : row.entrySet()){
146      switch (e.getKey()) {
147        case Definitions.COLUMN_BACKEND_ID:
148          AnalysisBackend end = _backendDAO.getBackend((Integer)e.getValue());
149          if(end == null){
150            LOGGER.warn("Detected non-existent backend.");
151            return null;
152          }
153          s.setBackend(end);
154          break;
155        case Definitions.COLUMN_STATUS:
156          s.setStatus(TaskStatus.fromInt((Integer)e.getValue()));
157          break;
158        case COLUMN_MESSAGE:
159          s.setMessage((String) e.getValue());
160          break;
161        case COLUMN_GUID:   // valid column, but not handled by extractor
162        case COLUMN_TASK_ID:  // valid column, but not handled by extractor
163          break;
164        default:
165          throw new IllegalArgumentException("Unhandeled column: "+e.getKey());
166      }
167    } // for
168    return s;
169  }
170  
171  /**
172   * 
173   * @param backendId if null, match is made simply by the task id. The parameter can be used to check whether the given back-end is associated with the task id.
174   * @param taskId
175   * @return the task type and task's owner/creator or null if the given task does not exists or if the backend is not set for the task 
176   */
177  protected Pair<TaskType, UserIdentity> getTaskType(Integer backendId, Long taskId) {
178    List<Map<String, Object>> rows = null;
179    if(backendId == null){
180      LOGGER.debug("Retrieving task type without backend id filter.");
181      rows = getJdbcTemplate().queryForList(SQL_SELECT_TASK_TYPE, new Object[]{taskId}, SQL_SELECT_TASK_TYPE_TYPES);
182    }else{
183      rows = getJdbcTemplate().queryForList(SQL_SELECT_TASK_TYPE_BY_BACKEND_ID, new Object[]{backendId, taskId}, SQL_SELECT_TASK_TYPE_BY_BACKEND_ID_TYPES);
184    }
185    if(rows.isEmpty()){
186      LOGGER.warn("Task, id: "+taskId+" was not found for backend, id: "+backendId);
187      return null;
188    }
189    Map<String, Object> row = rows.iterator().next();
190    Integer taskType = (Integer) row.get(COLUMN_TASK_TYPE);
191    if(taskType == null){
192      LOGGER.warn("Task, id: "+taskId+" was not found for backend, id: "+backendId);
193      return null;
194    }else{
195      Long userId = (Long) row.get(COLUMN_USER_ID);
196      return Pair.of(TaskType.fromInt(taskType), (userId == null ? null : new UserIdentity(userId)));
197    }
198  }
199
200  
201  /**
202   * 
203   * @param details
204   */
205  protected void insertTaskBackends(AbstractTaskDetails details){
206    BackendStatusList statuses = details.getBackends();
207    if(statuses == null){
208      LOGGER.debug("No backendStatusList, creating a new one...");
209      statuses = new BackendStatusList();
210    }
211
212    Integer backendId = details.getBackendId();
213    if(backendId != null){
214      BackendStatus s = statuses.getBackendStatus(backendId);
215      if(s == null){  // make sure the back-end of the task is in the status list
216        LOGGER.debug("BackendId was given, inserting backend to the list of target backends with TaskStatus: "+TaskStatus.NOT_STARTED.name());
217        s = new BackendStatus(new AnalysisBackend(backendId), TaskStatus.NOT_STARTED);
218        statuses.setBackendStatus(s);
219      }
220    }
221
222    Object[] values = new Object[]{details.getTaskId(), null, null, null};
223
224    if(BackendStatusList.isEmpty(statuses)){
225      LOGGER.warn("No backends given, the task may not start properly. Task, id: "+values[0]);
226      return;
227    }
228
229    JdbcTemplate t = getJdbcTemplate();
230    for(BackendStatus s : statuses.getBackendStatuses()){
231      values[1] = s.getBackendId();
232      values[2] = s.getStatus().toInt();
233      values[3] = s.getMessage();
234      t.update(SQL_INSERT_TASK_BACKEND, values, SQL_INSERT_TASK_BACKEND_TYPES);
235    }
236  }
237  
238  /**
239   * 
240   * @param details
241   */
242  protected void getBackendStatusList(AbstractTaskDetails details) {
243    Long taskId = details.getTaskId();
244    List<Map<String, Object>> rows = getJdbcTemplate().queryForList(SQL_GET_BACKEND_STATUSES, new Object[]{taskId}, SQL_GET_BACKEND_STATUSES_SQL_TYPES);
245    if(rows.isEmpty()){
246      LOGGER.warn("No back-end for task, id: "+taskId);
247    }else{
248      BackendStatusList statuses = new BackendStatusList();
249      for(Map<String, Object> row : rows){
250        statuses.setBackendStatus(extractBackendStatus(row));
251      }
252      details.setBackends(statuses);
253    }
254  }
255  
256  /**
257   * Update the given status list for the given task.
258   * 
259   * @param status
260   * @param taskId
261   */
262  public void updateTaskStatus(BackendStatusList status, Long taskId){
263    if(BackendStatusList.isEmpty(status)){
264      LOGGER.debug("Status list was empty for task, id: "+taskId);
265      return;
266    }
267    for(BackendStatus s : status.getBackendStatuses()){
268      updateTaskStatus(s, taskId);
269    }
270  }
271  
272  /**
273   * 
274   * @param backendId
275   * @param taskId
276   * @return the task status for the given backend for the given task, or null if no such task is given for the backend
277   */
278  public BackendStatus getBackendStatus(Integer backendId, Long taskId){
279    List<Map<String, Object>> rows = getJdbcTemplate().queryForList(SQL_SELECT_BACKEND_STATUS_BY_BACKEND_ID, new Object[]{backendId, taskId}, SQL_SELECT_BACKEND_STATUS_BY_BACKEND_ID_TYPES);
280    if(rows.isEmpty()){
281      return null;
282    }else{
283      return extractBackendStatus(rows.get(0));
284    }
285  }
286  
287  /**
288   * @param backendId
289   * @param dataGroups optional dataGroups filter, if not given, default backend-specific datagroups will be used
290   * @param limits optional limits filter
291   * @param taskId
292   * @return the task or null if not found
293   * @throws IllegalArgumentException on bad values
294   */
295  public AbstractTaskDetails getTask(Integer backendId, DataGroups dataGroups, Limits limits, Long taskId) throws IllegalArgumentException{
296    Pair<TaskType, UserIdentity> type = getTaskType(backendId, taskId);
297    if(type == null){
298      LOGGER.warn("Failed to resolve task type.");
299      return null;
300    }
301
302    if(backendId == null){
303      LOGGER.debug("No backend id given, will not check data groups.");
304    }else if(DataGroups.isEmpty(dataGroups)){
305      LOGGER.debug("No datagroups given, retrieving default data groups.");
306      AnalysisBackend backend = _backendDAO.getBackend(backendId);
307      if(backend == null){
308        throw new IllegalArgumentException("Backend, id: "+backendId+" does not exist.");
309      }
310      dataGroups = backend.getDefaultTaskDataGroups();
311    }
312
313    AbstractTaskDetailsImpl details = new AbstractTaskDetailsImpl(type.getLeft());
314    details.setBackendId(backendId);
315    details.setTaskId(taskId);
316    details.setUserId(type.getRight());
317
318    getTaskMetadata(details);
319
320    if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_BACKEND_STATUS, dataGroups)){
321      getBackendStatusList(details);
322    }
323
324    return details;
325  }
326  
327  /**
328   * Update the given status for the given task. If the status does not previously exist for this backend, new database entry is automatically created.
329   * 
330   * @param status
331   * @param taskId
332   */
333  public void updateTaskStatus(final BackendStatus status, final Long taskId){
334    getTransactionTemplate().execute(new TransactionCallback<Void>() {
335
336      @Override
337      public Void doInTransaction(TransactionStatus s) {
338        JdbcTemplate t = getJdbcTemplate();
339        Integer backendId = status.getBackendId();
340        if(t.queryForObject(SQL_CHECK_TASK_BACKEND, new Object[]{backendId, taskId}, SQL_CHECK_TASK_BACKEND_TYPES, Long.class) > 0){ // already exists
341          if(t.update(SQL_UPDATE_TASK_STATUS, new Object[]{status.getStatus().toInt(), status.getMessage(), taskId, status.getBackendId()}, SQL_UPDATE_TASK_STATUS_TYPES) != 1){
342            LOGGER.debug("Nothing updated for task, id: "+taskId+", backend, id: "+status.getBackendId());
343          }
344        }else{ // add new status
345          t.update(SQL_INSERT_TASK_BACKEND, new Object[]{backendId, status.getStatus().toInt(), status.getMessage()}, SQL_INSERT_TASK_BACKEND_TYPES);
346        }
347        return null;
348      }
349    });
350  } // updateTaskStatus
351  
352  /**
353   * This will create the basic task and insert metadata and back-ends
354   * 
355   * @param details
356   * @return created row id or null on failure
357   * @throws IllegalArgumentException
358   */
359  public Long insertTask(AbstractTaskDetails details) throws IllegalArgumentException{
360    TaskType type = details.getTaskType();
361    if(type == null){
362      throw new IllegalArgumentException("No task type given.");
363    }
364    SimpleJdbcInsert taskInsert = new SimpleJdbcInsert(getJdbcTemplate());
365    taskInsert.withTableName(TABLE_TASKS);
366    taskInsert.setGeneratedKeyName(COLUMN_TASK_ID);
367    taskInsert.usingColumns(SQL_COLUMNS_INSERT_TASK);
368    taskInsert.withoutTableColumnMetaDataAccess();
369
370    HashMap<String, Object> parameters = new HashMap<>(SQL_COLUMNS_INSERT_TASK.length);
371    parameters.put(COLUMN_TASK_TYPE, type.toInt());
372    parameters.put(COLUMN_ROW_CREATED, null);
373    parameters.put(COLUMN_USER_ID, details.getUserIdValue());
374    Number key = taskInsert.executeAndReturnKey(parameters);
375    if(key == null){
376      LOGGER.error("Failed to add new task.");
377      return null;
378    }
379
380    Long taskId = key.longValue();
381    details.setTaskId(taskId);
382
383    insertTaskMetadata(details);
384    insertTaskParameters(details);
385    insertTaskBackends(details);
386
387    return taskId;
388  }
389  
390  /**
391   * 
392   * @param details
393   */
394  protected void insertTaskParameters(AbstractTaskDetails details){
395    TaskParameters params = details.getTaskParameters();
396    if(params == null){
397      LOGGER.debug("No task parameters.");
398      return;
399    }
400    
401    Long taskId = details.getTaskId();
402    Map<String, String> metadata = params.toMetadata();
403    if(metadata == null || metadata.isEmpty()){
404      LOGGER.debug("No task parameter metadata for task, id: "+taskId);
405    }else{
406      metadata.put(METADATA_TASK_PARAMETER_CLASS, params.getClass().getName());
407      insertTaskMetadata(metadata, taskId);
408    }
409  }
410
411  /**
412   * 
413   * @param details
414   */
415  protected void insertTaskMetadata(AbstractTaskDetails details){
416    Long taskId = details.getTaskId();
417    Map<String, String> metadata = details.getMetadata();
418    if(metadata == null || metadata.isEmpty()){
419      LOGGER.debug("No metadata for task, id: "+taskId);
420    }else{
421      insertTaskMetadata(metadata, taskId);
422    }
423  }
424  
425  /**
426   * 
427   * @param metadata
428   * @param taskId
429   */
430  private void insertTaskMetadata(Map<String, String> metadata, Long taskId) {
431    JdbcTemplate t = getJdbcTemplate();
432    Object[] ob = {taskId, null, null};
433    for(Entry<String, String> e : metadata.entrySet()){
434      ob[1] = e.getKey();
435      ob[2] = e.getValue();
436      t.update(SQL_INSERT_TASK_METADATA, ob, SQL_INSERT_TASK_METADATA_TYPES);
437    }
438  }
439  
440  /**
441   * Retrieves (and sets) the task metadata for the given details.
442   * 
443   * This will also retrieve task parameters {@link service.tut.pori.contentanalysis.AbstractTaskDetails#getTaskParameters()} as they are in the default implementation stored in the metadata table.
444   * 
445   * @param details
446   * @see #getTaskParameters(AbstractTaskDetails, Map)
447   */
448  protected void getTaskMetadata(AbstractTaskDetails details){
449    Long taskId = details.getTaskId();
450    List<Map<String,Object>> rows = getJdbcTemplate().queryForList(SQL_SELECT_TASK_METADATA, new Object[]{taskId}, SQL_SELECT_TASK_METADATA_TYPES);
451    int count = rows.size();
452    if(count < 1){
453      LOGGER.debug("No metadata for task, id: "+taskId);
454      return;
455    }
456
457    HashMap<String, String> metadata = new HashMap<>(count);  
458    for(Map<String, Object> row : rows){
459      metadata.put((String) row.get(Definitions.COLUMN_NAME), (String) row.get(Definitions.COLUMN_VALUE));
460    } // for
461    details.setMetadata(metadata);
462    
463    getTaskParameters(details, metadata);
464  }
465  
466  /**
467   * 
468   * @param details
469   * @param metadata
470   * @throws IllegalArgumentException on invalid class name
471   */
472  protected void getTaskParameters(AbstractTaskDetails details, Map<String, String> metadata) throws IllegalArgumentException {
473    String clsName = metadata.get(METADATA_TASK_PARAMETER_CLASS);
474    if(StringUtils.isBlank(clsName)){
475      LOGGER.debug("No task parameters for task, id: "+details.getTaskId());
476      return;
477    }
478    
479    try {
480      TaskParameters params = (TaskParameters) Class.forName(clsName).newInstance();
481      params.initialize(metadata);
482      details.setTaskParameters(params);
483    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
484      LOGGER.error(ex, ex);
485      throw new IllegalArgumentException("Unknown parameter class : "+clsName);
486    }
487  }
488  
489  /**
490   * Basic implementation for Task details
491   *
492   */
493  private class AbstractTaskDetailsImpl extends AbstractTaskDetails {
494    private TaskParameters _taskParameters = null;
495    
496    /**
497     * 
498     * @param taskType
499     */
500    public AbstractTaskDetailsImpl(TaskType taskType) {
501      setTaskType(taskType);
502    }
503
504    @Override
505    public TaskParameters getTaskParameters() {
506      return _taskParameters;
507    }
508
509    @Override
510    public void setTaskParameters(TaskParameters parameters) {
511      _taskParameters = parameters;
512    }
513  } // class AbstractTaskDetailsImpl
514}