001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: */
013: package org.pentaho.plugin.quartz;
014:
015: import java.text.SimpleDateFormat;
016: import java.util.ArrayList;
017: import java.util.Date;
018: import java.util.Iterator;
019: import java.util.List;
020:
021: import org.pentaho.core.repository.IContentItem;
022: import org.pentaho.core.repository.IContentRepository;
023: import org.pentaho.core.repository.ISolutionRepository;
024: import org.pentaho.core.runtime.IBackgroundExecution;
025: import org.pentaho.core.session.IPentahoSession;
026: import org.pentaho.core.session.UserSession;
027: import org.pentaho.core.solution.IActionSequence;
028: import org.pentaho.core.solution.IOutputHandler;
029: import org.pentaho.core.solution.IParameterProvider;
030: import org.pentaho.core.system.PentahoSystem;
031: import org.pentaho.messages.Messages;
032: import org.pentaho.repository.HibernateUtil;
033: import org.pentaho.repository.content.ContentRepository;
034: import org.pentaho.core.repository.content.ContentRepositoryOutputHandler;
035: import org.pentaho.util.UUIDUtil;
036: import org.pentaho.util.logging.Logger;
037: import org.quartz.JobDataMap;
038: import org.quartz.JobDetail;
039: import org.quartz.JobExecutionContext;
040: import org.quartz.JobExecutionException;
041: import org.quartz.JobListener;
042: import org.quartz.Scheduler;
043: import org.quartz.SimpleTrigger;
044: import org.quartz.Trigger;
045:
046: public class QuartzBackgroundExecutionHelper implements
047: IBackgroundExecution {
048:
049: public static final String DEFAULT_JOB_NAME = "bgExecution"; //$NON-NLS-1$
050:
051: public static final String DEFAULT_TRIGGER_NAME = "bgTrigger"; //$NON-NLS-1$
052:
053: public static final String DEFAULT_BACKGROUND_LOCATION = "background"; //$NON-NLS-1$
054:
055: public static final String BACKGROUND_USER_NAME_STR = "background_user_name"; //$NON-NLS-1$
056:
057: public static final String BACKGROUND_CONTENT_GUID_STR = "background_output_content_guid"; //$NON-NLS-1$
058:
059: public static final String BACKGROUND_CONTENT_LOCATION_STR = "background_output_location"; //$NON-NLS-1$
060:
061: public static final String BACKGROUND_CONTENT_COOKIE_PREFIX = "pentaho_background_content"; //$NON-NLS-1$
062:
063: public static final String BACKGROUND_EXECUTION_FLAG = "backgroundExecution"; //$NON-NLS-1$
064:
065: /*
066: *****************************
067: * Methods from the Interface
068: *****************************
069: */
070:
071: public String backgroundExecuteAction(IPentahoSession userSession,
072: IParameterProvider parameterProvider) {
073: try {
074: Scheduler sched = QuartzSystemListener
075: .getSchedulerInstance();
076:
077: String solutionName = parameterProvider.getStringParameter(
078: "solution", null); //$NON-NLS-1$
079: String actionPath = parameterProvider.getStringParameter(
080: "path", null); //$NON-NLS-1$
081: String actionName = parameterProvider.getStringParameter(
082: "action", null); //$NON-NLS-1$
083: String userName = userSession.isAuthenticated() ? userSession
084: .getName()
085: : IBackgroundExecution.DEFAULT_USER_NAME;
086: String jobGroup = userName;
087:
088: String outputContentGUID = UUIDUtil.getUUIDAsString();
089:
090: JobDetail jobDetail = createDetailFromParameterProvider(
091: parameterProvider, userSession, outputContentGUID,
092: solutionName, actionPath, actionName, jobGroup);
093:
094: trackBackgroundExecution(userSession, outputContentGUID);
095:
096: BackgroundExecuteListener listener = new BackgroundExecuteListener(
097: userSession, outputContentGUID, sched, jobDetail
098: .getName());
099: sched.addJobListener(listener);
100: jobDetail.addJobListener(listener.getName());
101:
102: Trigger bgTrigger = new SimpleTrigger(outputContentGUID,
103: jobGroup, new Date());
104: // bgTrigger.setPriority(someValue);
105:
106: sched.scheduleJob(jobDetail, bgTrigger);
107: // TODO: Fix with properly formatted HTML template for this status message
108: return Messages
109: .getString(
110: "BackgroundExecuteHelper.USER_JOB_SUBMITTED", "UserContent", "if(window.opener) {window.opener.location.href='UserContent'; window.close() } else { return true; }"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
111: } catch (Exception ex) {
112: return ex.getLocalizedMessage();
113: }
114: }
115:
116: public void trackBackgroundExecution(IPentahoSession userSession,
117: String GUID) {
118: IContentRepository repo = ContentRepository
119: .getInstance(userSession);
120: repo.newBackgroundExecutedContentId(userSession, GUID);
121: }
122:
123: public IContentItem getBackgroundContent(String contentGUID,
124: IPentahoSession userSession) {
125: IContentRepository repo = ContentRepository
126: .getInstance(userSession);
127: try {
128: IContentItem item = repo.getContentItemById(contentGUID);
129: return item;
130: } catch (Exception ex) {
131: Logger.error(this .getClass().getName(), ex
132: .getLocalizedMessage(), ex);
133: }
134: return null;
135: }
136:
137: public List getScheduledAndExecutingBackgroundJobs(
138: IPentahoSession userSession) {
139: try {
140: Scheduler sched = QuartzSystemListener
141: .getSchedulerInstance();
142: String userName = userSession.isAuthenticated() ? userSession
143: .getName()
144: : IBackgroundExecution.DEFAULT_USER_NAME;
145: String[] jobNames = sched.getJobNames(userName);
146: List rtn = new ArrayList();
147: if (jobNames != null) {
148: for (int i = 0; i < jobNames.length; i++) {
149: JobDetail jobDetail = sched.getJobDetail(
150: jobNames[i], userName);
151: rtn.add(jobDetail);
152: }
153: }
154: return rtn;
155: } catch (Exception ex) {
156: // TODO: Handle exception properly
157: ex.printStackTrace();
158: }
159: return new ArrayList();
160: }
161:
162: public void removeBackgroundExecutedContentForID(
163: String contentGUID, IPentahoSession userSession) {
164:
165: // First, remove content item from the repo with that GUID.
166: IContentRepository repo = ContentRepository
167: .getInstance(userSession);
168: try {
169: IContentItem item = repo.getContentItemById(contentGUID);
170: if (item != null) {
171: item.makeTransient();
172: } else {
173: return;
174: }
175: } finally {
176: HibernateUtil.commitTransaction();
177: }
178: repo
179: .removeBackgroundExecutedContentId(userSession,
180: contentGUID);
181: }
182:
183: public List getBackgroundExecutedContentList(
184: IPentahoSession userSession) {
185: IContentRepository repo = ContentRepository
186: .getInstance(userSession);
187: ArrayList idList = new ArrayList();
188: List idObjectList = repo
189: .getBackgroundExecutedContentItemsForUser(userSession);
190: if (idObjectList != null) {
191: IContentItem contentItem = null;
192: for (int i = 0; i < idObjectList.size(); i++) {
193: contentItem = (IContentItem) idObjectList.get(i);
194: idList.add(contentItem);
195: }
196: }
197: return idList;
198: }
199:
200: // Helper Utility Methods
201: protected JobDetail createDetailFromParameterProvider(
202: IParameterProvider parameterProvider,
203: IPentahoSession userSession, String outputContentGUID,
204: String solutionName, String actionPath, String actionName,
205: String jobGroup) {
206: String userName = userSession.isAuthenticated() ? userSession
207: .getName() : IBackgroundExecution.DEFAULT_USER_NAME;
208:
209: SimpleDateFormat fmt = new SimpleDateFormat();
210:
211: // GEM - JIRA case BISERVER-231
212: // We are not sure why we were prepending the jobname with the action path and
213: // sequence name, but it resulted in jobnames that exceeded the database column length of
214: // 80 characters. This did not present itself as a problem unitl we implemented Oracle
215: // as the RDBMS repository, where background job execution failed because of the name length.
216: // So we now just use the guid as the job name, and will wait and see if this manifests other problems.
217:
218: // String completeAction = solutionName + "/" + actionPath + "/" + actionName; //$NON-NLS-1$ //$NON-NLS-2$
219: // String jobName = completeAction + "/" + outputContentGUID; //$NON-NLS-1$
220:
221: String jobName = outputContentGUID;
222:
223: JobDetail jobDetail = new JobDetail(jobName, jobGroup,
224: QuartzExecute.class);
225: JobDataMap data = jobDetail.getJobDataMap();
226: Iterator inputNamesIterator = parameterProvider
227: .getParameterNames();
228: String outputLocationGUID = UUIDUtil.getUUIDAsString();
229: while (inputNamesIterator.hasNext()) {
230: String inputName = (String) inputNamesIterator.next();
231: Object inputValue = parameterProvider
232: .getParameter(inputName);
233: data.put(inputName, inputValue);
234: }
235: ISolutionRepository repo = PentahoSystem
236: .getSolutionRepository(userSession);
237: IActionSequence action = repo.getActionSequence(solutionName,
238: actionPath, actionName, repo.getLoggingLevel(),
239: ISolutionRepository.ACTION_EXECUTE);
240: data.put(BACKGROUND_ACTION_NAME_STR, action.getTitle());
241: data.put("processId", this .getClass().getName()); //$NON-NLS-1$
242: data.put(BACKGROUND_USER_NAME_STR, userName);
243: data.put(BACKGROUND_CONTENT_GUID_STR, outputContentGUID);
244: data.put(BACKGROUND_CONTENT_LOCATION_STR,
245: DEFAULT_BACKGROUND_LOCATION + "/" + outputLocationGUID); //$NON-NLS-1$
246: data.put(BACKGROUND_SUBMITTED, fmt.format(new Date()));
247:
248: // This tells our execution component (QuartzExecute) that we're running a background job instead of
249: // a standard quartz execution.
250: data.put(BACKGROUND_EXECUTION_FLAG, "true"); //$NON-NLS-1$
251:
252: return jobDetail;
253:
254: }
255:
256: public IPentahoSession getEffectiveUserSession(String user) {
257: UserSession us = new UserSession(user, null, true);
258: return us;
259: }
260:
261: public static class BackgroundExecuteListener implements
262: JobListener {
263:
264: private IPentahoSession userSession;
265:
266: private String contentGUID;
267:
268: private Scheduler sched;
269:
270: private String jobName;
271:
272: public BackgroundExecuteListener(IPentahoSession session,
273: String contentGUID, Scheduler scheduler, String jobName) {
274: userSession = session;
275: this .contentGUID = contentGUID;
276: this .jobName = jobName;
277: }
278:
279: public String getName() {
280: return contentGUID;
281: }
282:
283: public void jobExecutionVetoed(JobExecutionContext context) {
284: // TODO Auto-generated method stub
285:
286: }
287:
288: public void jobToBeExecuted(JobExecutionContext context) {
289: // TODO Auto-generated method stub
290:
291: }
292:
293: public void jobWasExecuted(JobExecutionContext context,
294: JobExecutionException exception) {
295: // TODO Auto-generated method stub
296: // Update the userSession with the updated content item.
297: JobDetail ctxDetail = context.getJobDetail();
298: if ((ctxDetail != null)
299: && (ctxDetail.getName().equals(this .jobName))) { // Only do if it's for our job...
300: Object contentItemGUID = context
301: .get(BACKGROUND_CONTENT_GUID_STR);
302: if (contentItemGUID != null && userSession != null) {
303: userSession.setBackgroundExecutionAlert(); // Toggle the alert status
304: } else {
305: Logger
306: .warn(
307: this .getClass().getName(),
308: Messages
309: .getString("BackgroundExecuteHelper.WARN_CONTENT_ITEM_NOT_CREATED")); //$NON-NLS-1$
310: }
311: this .userSession = null; // Make sure nothing keeps a handle to the user session.
312: try {
313: sched.removeJobListener(this .getName());
314: } catch (Exception ex) {
315: // TODO: Do something here
316: }
317: }
318: }
319:
320: }
321:
322: public IOutputHandler getContentOutputHandler(String location,
323: String fileName, String solutionName,
324: IPentahoSession userSession,
325: IParameterProvider parameterProvider) {
326: return new ContentRepositoryOutputHandler(location, fileName,
327: solutionName, userSession);
328: }
329:
330: }
|