001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.ejb.beans;
034:
035: import com.flexive.core.Database;
036: import com.flexive.core.DatabaseConst;
037: import com.flexive.core.LifeCycleInfoImpl;
038: import com.flexive.shared.FxArrayUtils;
039: import com.flexive.shared.FxContext;
040: import com.flexive.shared.exceptions.*;
041: import com.flexive.shared.interfaces.*;
042: import com.flexive.shared.search.Briefcase;
043: import com.flexive.shared.security.ACL;
044: import com.flexive.shared.security.LifeCycleInfo;
045: import com.flexive.shared.security.UserTicket;
046: import org.apache.commons.lang.ArrayUtils;
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049:
050: import javax.annotation.Resource;
051: import javax.ejb.*;
052: import java.sql.*;
053: import java.util.ArrayList;
054: import java.util.HashSet;
055: import java.util.List;
056: import java.util.Set;
057:
058: /**
059: * Bean handling Briefcases.
060: * <p/>
061: * A briefcase is a object store which may be accessed with flexive SQL
062: * or the API provided by this beans.
063: *
064: * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
065: */
066: @Stateless(name="BriefcaseEngine")
067: @TransactionAttribute(TransactionAttributeType.REQUIRED)
068: @TransactionManagement(TransactionManagementType.CONTAINER)
069: public class BriefcaseEngineBean implements BriefcaseEngine,
070: BriefcaseEngineLocal {
071:
072: private static transient Log LOG = LogFactory
073: .getLog(BriefcaseEngineBean.class);
074: @Resource
075: javax.ejb.SessionContext ctx;
076: @EJB
077: SequencerEngineLocal seq;
078: @EJB
079: ACLEngineLocal acl;
080:
081: /**
082: * {@inheritDoc}
083: */
084: @TransactionAttribute(TransactionAttributeType.REQUIRED)
085: public long create(String name, String description, Long aclId)
086: throws FxApplicationException {
087: final UserTicket ticket = FxContext.get().getTicket();
088:
089: if (description == null) {
090: description = "";
091: }
092: if (name == null || name.trim().length() == 0) {
093: throw new FxInvalidParameterException(
094: "ex.briefcase.nameMissing", "name");
095: }
096: if (aclId != null) {
097: ACL acl;
098: try {
099: acl = new ACLEngineBean().load(aclId);
100: } catch (Throwable t) {
101: throw new FxInvalidParameterException(
102: "ex.briefcase.invalidAcl", "acl");
103: }
104: if (!ticket.mayCreateACL(aclId, ticket.getUserId())) {
105: throw new FxNoAccessException(
106: "ex.briefcase.noCreatePermission", acl
107: .getLabel());
108: }
109:
110: }
111: Connection con = null;
112: PreparedStatement ps = null;
113: String sql;
114: String sourceQuery = "";
115:
116: try {
117: // Obtain a database connection
118: con = Database.getDbConnection();
119:
120: // Obtain a new id
121: long newId = seq.getId(SequencerEngine.System.BRIEFCASE);
122:
123: sql = "INSERT INTO "
124: + DatabaseConst.TBL_BRIEFCASE
125: + "("
126: +
127: //1, 2, 3 , 4 , 5 , 6 7 8 9 , 10 , 11
128: "ID,NAME,DESCRIPTION,SOURCE_QUERY,ACL,CREATED_BY,CREATED_AT,MODIFIED_BY,MODIFIED_AT,MANDATOR,ICON_ID)"
129: + "VALUES (?,?,?,?,?,?,?,?,?,?,1)";
130: final long NOW = System.currentTimeMillis();
131: ps = con.prepareStatement(sql);
132: ps.setLong(1, newId);
133: ps.setString(2, name);
134: ps.setString(3, description);
135: ps.setString(4, sourceQuery);
136: if (aclId != null) {
137: ps.setLong(5, aclId);
138: } else {
139: ps.setNull(5, java.sql.Types.NUMERIC);
140: }
141: ps.setLong(6, ticket.getUserId());
142: ps.setLong(7, NOW);
143: ps.setLong(8, ticket.getUserId());
144: ps.setLong(9, NOW);
145: ps.setLong(10, ticket.getMandatorId());
146: ps.executeUpdate();
147: return newId;
148: } catch (SQLException exc) {
149: final boolean uniqueConstraintViolation = Database
150: .isUniqueConstraintViolation(exc);
151: if (ctx != null)
152: ctx.setRollbackOnly();
153: else
154: try {
155: con.rollback();
156: } catch (SQLException e) {
157: LOG.warn(e.getMessage(), e);
158: }
159: if (uniqueConstraintViolation) {
160: throw new FxEntryExistsException(LOG,
161: "ex.briefcase.nameAlreadyExists", name);
162: } else {
163: throw new FxCreateException(LOG, exc,
164: "ex.briefcase.createFailed");
165: }
166: } finally {
167: Database.closeObjects(BriefcaseEngineBean.class, con, ps);
168: }
169: }
170:
171: /**
172: * {@inheritDoc}
173: */
174: @TransactionAttribute(TransactionAttributeType.REQUIRED)
175: public void modify(long id, String name, String description,
176: Long aclId) throws FxApplicationException {
177:
178: // Anything to do?
179: if (name != null && name.trim().length() == 0) {
180: name = null;
181: }
182: if (name == null && description == null && aclId == null) {
183: return;
184: }
185:
186: // Lookup the briefcase
187: Briefcase br = load(id);
188: if (br == null) {
189: throw new FxNotFoundException("ex.briefcase.notFound",
190: ("#" + id));
191: }
192: // Permission checks
193: checkEditBriefcase(br);
194: // Delete operation
195: Connection con = null;
196: PreparedStatement ps = null;
197: try {
198: // Obtain a database connection
199: con = Database.getDbConnection();
200: String sSql = "update " + DatabaseConst.TBL_BRIEFCASE
201: + " set" + ((name == null) ? "" : " name=?, ")
202: + ((aclId == null) ? "" : " acl=?, ")
203: + ((description == null) ? "" : " description=?, ")
204: + "mandator=mandator where id=" + id;
205: ps = con.prepareStatement(sSql);
206: int pos = 1;
207: if (name != null)
208: ps.setString(pos++, name);
209: if (aclId != null) {
210: if (aclId == -1) {
211: ps.setNull(pos++, java.sql.Types.NUMERIC);
212: } else {
213: ps.setLong(pos++, aclId);
214: }
215: }
216: if (description != null)
217: ps.setString(pos, description);
218: ps.executeUpdate();
219: } catch (SQLException exc) {
220: ctx.setRollbackOnly();
221: throw new FxLoadException(LOG, exc,
222: "ex.briefcase.modifyFailed", br.getName());
223: } finally {
224: Database.closeObjects(BriefcaseEngineBean.class, con, ps);
225: }
226: }
227:
228: private void checkEditBriefcase(Briefcase br)
229: throws FxNotFoundException {
230: final UserTicket ticket = FxContext.get().getTicket();
231: if (!ticket.isGlobalSupervisor()
232: && br.getMandator() != ticket.getMandatorId()) {
233: if (!ticket.mayEditACL((br.getAcl()), br.getLifeCycleInfo()
234: .getCreatorId())) {
235: throw new FxNotFoundException(
236: "ex.briefcase.noEditPermission", br.getName());
237: }
238: }
239: }
240:
241: /**
242: * {@inheritDoc}
243: */
244: @TransactionAttribute(TransactionAttributeType.REQUIRED)
245: public void remove(long id) throws FxApplicationException {
246: // Lookup the briefcase
247: Briefcase br = load(id);
248: if (br == null) {
249: throw new FxNotFoundException("ex.briefcase.notFound",
250: ("#" + id));
251: }
252: // Permission checks
253: final UserTicket ticket = FxContext.get().getTicket();
254: if (!ticket.isGlobalSupervisor()
255: && br.getMandator() != ticket.getMandatorId()) {
256: if (!ticket.mayDeleteACL(br.getAcl(), br.getLifeCycleInfo()
257: .getCreatorId())) {
258: throw new FxNotFoundException(
259: "ex.briefcase.noDeletePermission", br.getName());
260: }
261: }
262: // Delete operation
263: Connection con = null;
264: Statement stmt = null;
265: try {
266: // Obtain a database connection
267: con = Database.getDbConnection();
268: stmt = con.createStatement();
269: stmt.addBatch("delete from "
270: + DatabaseConst.TBL_BRIEFCASE_DATA
271: + " where briefcase_id=" + id);
272: stmt.addBatch("delete from " + DatabaseConst.TBL_BRIEFCASE
273: + " where id=" + id);
274: stmt.executeBatch();
275: } catch (SQLException exc) {
276: throw new FxLoadException(LOG, exc,
277: "ex.briefcase.deleteFailed", br.getName());
278: } finally {
279: Database.closeObjects(BriefcaseEngineBean.class, con, stmt);
280: }
281: }
282:
283: /**
284: * {@inheritDoc}
285: */
286: @TransactionAttribute(TransactionAttributeType.REQUIRED)
287: public List<Briefcase> getList(boolean includeShared)
288: throws FxApplicationException {
289: return getList(null, includeShared);
290: }
291:
292: /**
293: * {@inheritDoc}
294: */
295: @TransactionAttribute(TransactionAttributeType.REQUIRED)
296: public Briefcase load(long id) throws FxApplicationException {
297: List<Briefcase> l = getList(id, true);
298: if (l != null && l.size() > 0) {
299: return l.get(0);
300: } else {
301: throw new FxNotFoundException(LOG,
302: "ex.briefcase.notFound.id", id);
303: }
304: }
305:
306: /**
307: * {@inheritDoc}
308: */
309: @TransactionAttribute(TransactionAttributeType.REQUIRED)
310: public void clear(long id) throws FxApplicationException {
311: Connection con = null;
312: PreparedStatement stmt = null;
313: final Briefcase br = load(id);
314: checkEditBriefcase(br);
315: try {
316: con = Database.getDbConnection();
317: stmt = con.prepareStatement("DELETE FROM "
318: + DatabaseConst.TBL_BRIEFCASE_DATA
319: + " WHERE briefcase_id=?");
320: stmt.setLong(1, id);
321: stmt.executeUpdate();
322: } catch (Exception e) {
323: ctx.setRollbackOnly();
324: throw new FxUpdateException(LOG, e, "ex.briefcase.clear",
325: br.getName(), e);
326: } finally {
327: Database.closeObjects(BriefcaseEngineBean.class, con, stmt);
328: }
329: }
330:
331: /**
332: * {@inheritDoc}
333: */
334: @TransactionAttribute(TransactionAttributeType.REQUIRED)
335: public void addItems(long id, long[] objectIds)
336: throws FxApplicationException {
337: Connection con = null;
338: PreparedStatement stmt = null;
339: final Briefcase br = load(id);
340: checkEditBriefcase(br);
341: try {
342: con = Database.getDbConnection();
343:
344: // keep lookup table of existing items to avoid adding an item twice
345: final Set<Long> existingItems = new HashSet<Long>();
346: final long[] items = getItems(id);
347: for (long item : items) {
348: existingItems.add(item);
349: }
350:
351: stmt = con.prepareStatement("SELECT MAX(pos) FROM "
352: + DatabaseConst.TBL_BRIEFCASE_DATA
353: + " WHERE briefcase_id=?");
354: stmt.setLong(1, id);
355: final ResultSet rs = stmt.executeQuery();
356: int pos = rs.next() ? rs.getInt(1) : 0;
357: stmt.close();
358: stmt = con
359: .prepareStatement("INSERT INTO "
360: + DatabaseConst.TBL_BRIEFCASE_DATA
361: + "(briefcase_id, id, pos, amount) VALUES (?, ?, ?, 1)");
362: stmt.setLong(1, id);
363: for (long objectId : objectIds) {
364: if (!existingItems.contains(objectId)) {
365: stmt.setLong(2, objectId);
366: stmt.setLong(3, ++pos);
367: stmt.addBatch();
368: existingItems.add(objectId);
369: }
370: }
371: stmt.executeBatch();
372: } catch (Exception e) {
373: ctx.setRollbackOnly();
374: throw new FxUpdateException(LOG, e,
375: "ex.briefcase.addItems", br.getName(), e);
376: } finally {
377: Database.closeObjects(BriefcaseEngineBean.class, con, stmt);
378: }
379: }
380:
381: /**
382: * {@inheritDoc}
383: */
384: @TransactionAttribute(TransactionAttributeType.REQUIRED)
385: public void removeItems(long id, long[] objectIds)
386: throws FxApplicationException {
387: if (objectIds == null || objectIds.length == 0) {
388: return;
389: }
390: Connection con = null;
391: PreparedStatement stmt = null;
392: final Briefcase br = load(id);
393: checkEditBriefcase(br);
394: try {
395: con = Database.getDbConnection();
396: stmt = con.prepareStatement("DELETE FROM "
397: + DatabaseConst.TBL_BRIEFCASE_DATA
398: + " WHERE briefcase_id=?" + " AND id IN ("
399: + FxArrayUtils.toSeparatedList(objectIds, ',')
400: + ")");
401: stmt.setLong(1, id);
402: stmt.executeUpdate();
403: } catch (Exception e) {
404: ctx.setRollbackOnly();
405: throw new FxUpdateException(LOG, e,
406: "ex.briefcase.removeItems", br.getName(), e);
407: } finally {
408: Database.closeObjects(BriefcaseEngineBean.class, con, stmt);
409: }
410: }
411:
412: /**
413: * {@inheritDoc}
414: */
415: @TransactionAttribute(TransactionAttributeType.REQUIRED)
416: public void updateItems(long id, long[] addObjectIds,
417: long[] removeObjectIds) throws FxApplicationException {
418: removeItems(id, removeObjectIds);
419: addItems(id, addObjectIds);
420: }
421:
422: /**
423: * {@inheritDoc}
424: */
425: @TransactionAttribute(TransactionAttributeType.REQUIRED)
426: public void setItems(long id, long[] objectIds)
427: throws FxApplicationException {
428: clear(id);
429: addItems(id, objectIds);
430: }
431:
432: /**
433: * {@inheritDoc}
434: */
435: @TransactionAttribute(TransactionAttributeType.REQUIRED)
436: public long[] getItems(long id) throws FxApplicationException {
437: Connection con = null;
438: PreparedStatement stmt = null;
439: final Briefcase br = load(id);
440: try {
441: con = Database.getDbConnection();
442: stmt = con.prepareStatement("SELECT id FROM "
443: + DatabaseConst.TBL_BRIEFCASE_DATA
444: + " WHERE briefcase_id=?");
445: stmt.setLong(1, id);
446: final ResultSet rs = stmt.executeQuery();
447: final List<Long> result = new ArrayList<Long>();
448: while (rs.next()) {
449: result.add(rs.getLong(1));
450: }
451: return ArrayUtils.toPrimitive(result
452: .toArray(new Long[result.size()]));
453: } catch (Exception e) {
454: ctx.setRollbackOnly();
455: throw new FxUpdateException(LOG, e,
456: "ex.briefcase.getItems", br.getName(), e);
457: } finally {
458: Database.closeObjects(BriefcaseEngineBean.class, con, stmt);
459: }
460: }
461:
462: /**
463: * Builds a sql filer that only selects briefcases that the calling user has permissions on.
464: *
465: * @param briefcaseTblAlias the alias of the briefcase table, or null
466: * @param includeShared true if shared briefcases should be included
467: * @param perms the permissions that are needed
468: * @return a sql filter, eg '([briefcaseTblAlias.]CREATED_BY=12 OR ACL IS NOT NULL)'
469: */
470: public static String getSqlAccessFilter(String briefcaseTblAlias,
471: boolean includeShared, ACL.Permission... perms) {
472: final UserTicket ticket = FxContext.get().getTicket();
473: StringBuffer filter = new StringBuffer(1024);
474: if (briefcaseTblAlias == null) {
475: briefcaseTblAlias = "";
476: }
477: if (briefcaseTblAlias.length() > 0) {
478: briefcaseTblAlias = briefcaseTblAlias + ".";
479: }
480: final String colMANDATOR = briefcaseTblAlias + "MANDATOR";
481: final String colACL = briefcaseTblAlias + "ACL";
482: final String colCREATED_BY = briefcaseTblAlias + "CREATED_BY";
483: filter.append("(").append(colCREATED_BY).append("=").append(
484: ticket.getUserId());
485: if (includeShared) {
486: if (ticket.isGlobalSupervisor()) {
487: // add all shared
488: filter.append(" OR ").append(colACL).append(
489: " IS NOT null");
490: } else if (ticket.isMandatorSupervisor()) {
491: // add all shared(match by ACL or mandator)
492: String acls = ticket.getACLsCSV(
493: 0/*owner is irrelevant here*/,
494: ACL.Category.INSTANCE, perms);
495: filter.append(
496: (acls.length() > 0) ? (" OR " + colACL
497: + " IN (" + acls + ") ") : "").append(
498: " OR (").append(colACL).append(
499: " IS NOT null AND ").append(colMANDATOR)
500: .append("=").append(ticket.getMandatorId())
501: .append(")");
502: } else {
503: // add all shared(match by ACL)
504: String acls = ticket.getACLsCSV(
505: 0/*owner is irrelevant here*/,
506: ACL.Category.INSTANCE, perms);
507: if (acls.length() > 0) {
508: filter.append(" OR ").append(colACL)
509: .append(" IN (").append(acls).append(") ");
510: }
511: }
512: }
513: filter.append(")");
514: return filter.toString();
515: }
516:
517: /**
518: * Gets a list of all briefcase for the calling user.
519: *
520: * @param idFilter if set only the pricelist with the given id will be loaded
521: * @param includeShared if enabled shared briefcases will be included, if disabled only
522: * the briefcases created by the calling user will be returned
523: * @return the briefcases
524: * @throws FxApplicationException if the function fails
525: */
526: private List<Briefcase> getList(Long idFilter, boolean includeShared)
527: throws FxApplicationException {
528: Connection con = null;
529: Statement stmt = null;
530: String sql;
531: final ArrayList<Briefcase> result = new ArrayList<Briefcase>(
532: 500);
533: try {
534: // Obtain a database connection
535: con = Database.getDbConnection();
536: sql = "select "
537: +
538: //1, 2, 3 , 4 , 5 , 6 7 8 9 , 10 , 11
539: "ID,NAME,DESCRIPTION,SOURCE_QUERY,ACL,CREATED_BY,CREATED_AT,MODIFIED_BY,MODIFIED_AT,MANDATOR,ICON_ID, "
540: +
541: // 12
542: "(SELECT COUNT(*) FROM "
543: + DatabaseConst.TBL_BRIEFCASE_DATA
544: + " bd WHERE bd.briefcase_id=b.id) AS size "
545: + "from " + DatabaseConst.TBL_BRIEFCASE
546: + " b where ";
547: sql += getSqlAccessFilter(null, includeShared,
548: ACL.Permission.READ);
549: if (idFilter != null) {
550: sql += " and id=" + idFilter;
551: }
552:
553: stmt = con.createStatement();
554: ResultSet rs = stmt.executeQuery(sql);
555: while (rs != null && rs.next()) {
556: final long id = rs.getLong(1);
557: final String name = rs.getString(2);
558: final String desc = rs.getString(3);
559: final String src = rs.getString(4);
560: long acl = rs.getLong(5);
561: if (rs.wasNull()) {
562: acl = -1;
563: }
564: final LifeCycleInfo lc = LifeCycleInfoImpl.load(rs, 6,
565: 7, 8, 9);
566: final long mandator = rs.getLong(10);
567: final long iconId = rs.getLong(11);
568: final int size = rs.getInt(12);
569: result.add(new Briefcase(id, name, mandator, desc, src,
570: acl, lc, iconId, size));
571: }
572: result.trimToSize();
573: return result;
574: } catch (SQLException exc) {
575: throw new FxLoadException(LOG, exc,
576: "ex.briefcase.failedToLoadList", exc.getMessage());
577: } finally {
578: Database.closeObjects(BriefcaseEngineBean.class, con, stmt);
579: }
580: }
581:
582: }
|