1   /*
2    * Copyright 2003 - 2013 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.db;
22  
23  import java.sql.SQLException;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.efaps.admin.access.AccessCache;
28  import org.efaps.admin.access.AccessTypeEnums;
29  import org.efaps.admin.datamodel.SQLTable;
30  import org.efaps.admin.datamodel.Type;
31  import org.efaps.admin.event.EventDefinition;
32  import org.efaps.admin.event.EventType;
33  import org.efaps.admin.event.Parameter;
34  import org.efaps.admin.event.Parameter.ParameterValues;
35  import org.efaps.db.store.Resource;
36  import org.efaps.db.transaction.ConnectionResource;
37  import org.efaps.db.wrapper.SQLDelete;
38  import org.efaps.db.wrapper.SQLDelete.DeleteDefintion;
39  import org.efaps.util.EFapsException;
40  
41  /**
42   * The class is used as interface to the eFaps kernel to delete one object.
43   *
44   * @author The eFaps Team
45   * @version $Id$
46   */
47  public class Delete
48  {
49      /**
50       * The instance variable stores the instance for which this update is made.
51       *
52       * @see #getInstance()
53       */
54      private final Instance instance;
55  
56      /**
57       * @param _instance Instance to be deleted
58       */
59      public Delete(final Instance _instance)
60      {
61          this.instance = _instance;
62      }
63  
64      /**
65       * @param _type  type of the Instance to be deleted
66       * @param _id    id of the Instance to be deleted
67       */
68      public Delete(final Type _type,
69                    final String _id)
70      {
71          this.instance = Instance.get(_type, _id);
72      }
73  
74      /**
75       * @param _type  type of the Instance to be deleted
76       * @param _id    id of the Instance to be deleted
77       */
78      public Delete(final Type _type, final long _id)
79      {
80          this.instance = Instance.get(_type, _id);
81      }
82  
83      /**
84       * @param _oid  oid of the Instance to be deleted
85       */
86      public Delete(final String _oid)
87      {
88          this.instance = Instance.get(_oid);
89      }
90  
91      /**
92       * First it is checked if the user has access to delete the eFaps object
93       * defined in {@link #instance}. If no access, an exception is thrown. If
94       * the context user has access. the delete is made with
95       * {@link #executeWithoutAccessCheck}.
96       *
97       * @throws EFapsException if the current context user has no delete access
98       *                        on given eFaps object.
99       * @see #executeWithoutAccessCheck()
100      */
101     public void execute()
102         throws EFapsException
103     {
104         AccessCache.registerUpdate(getInstance());
105         final  boolean hasAccess = this.instance.getType().hasAccess(this.instance,
106                                                                      AccessTypeEnums.DELETE.getAccessType());
107         if (!hasAccess) {
108             throw new EFapsException(getClass(), "execute.NoAccess", this.instance);
109         }
110         executeWithoutAccessCheck();
111     }
112 
113     /**
114      * Executes the delete without checking the access rights (but with
115      * triggers).
116      * <ol>
117      * <li>executes the pre delete trigger (if exists)</li>
118      * <li>executes the delete trigger (if exists)</li>
119      * <li>executes if no delete trigger exists or the delete trigger is not
120      *     executed the update ({@see #executeWithoutTrigger})</li>
121      * <li>executes the post delete trigger (if exists)</li>
122      * </ol>
123      *
124      * @throws EFapsException thrown from {@link #executeWithoutTrigger} or
125      *                        when the status is invalid
126      * @see #executeWithoutTrigger()
127      */
128     public void executeWithoutAccessCheck()
129         throws EFapsException
130     {
131         executeEvents(EventType.DELETE_PRE);
132         if (!executeEvents(EventType.DELETE_OVERRIDE)) {
133             executeWithoutTrigger();
134         }
135         executeEvents(EventType.DELETE_POST);
136     }
137 
138     /**
139      * <p>The executes is done without calling triggers and check of access
140      * rights. The method executes the delete. For the object, a delete is made
141      * in all SQL tables from the type (if the SQL table is not read only!). If
142      * a store is defined for the type, the checked in file is also deleted
143      * (with the help of the store resource implementation; if the store
144      * resource implementation has not implemented the delete, the file is not
145      * deleted!).</p>
146      * <p>It is not checked if the current context user has access to delete
147      * the eFaps object defined in {@link #instance}.</p>
148      *
149      * @see SQLTable#isReadOnly()
150      * @throws EFapsException on error
151      */
152     public void executeWithoutTrigger()
153         throws EFapsException
154     {
155         final Context context = Context.getThreadContext();
156         ConnectionResource con = null;
157         try {
158             con = context.getConnectionResource();
159             // first remove the storeresource, because the information needed from the general
160             // instance to actually delete will be removed in the second step
161             Resource storeRsrc = null;
162             try {
163                 if (getInstance().getType().hasStore()) {
164                     storeRsrc = context.getStoreResource(getInstance(), Resource.StoreEvent.DELETE);
165                     storeRsrc.delete();
166                     storeRsrc.commit();
167                 }
168             } finally {
169                 if ((storeRsrc != null) && storeRsrc.isOpened()) {
170                     storeRsrc.abort();
171                 }
172             }
173             try {
174                 final List<DeleteDefintion> defs = new ArrayList<DeleteDefintion>();
175                 defs.addAll(GeneralInstance.getDeleteDefintion(getInstance(), con.getConnection()));
176                 final SQLTable mainTable = getInstance().getType().getMainTable();
177                 for (final SQLTable curTable : getInstance().getType().getTables()) {
178                     if ((curTable != mainTable) && !curTable.isReadOnly()) {
179                         defs.add(new DeleteDefintion(curTable.getSqlTable(),
180                                         curTable.getSqlColId(), getInstance().getId()));
181                     }
182                 }
183                 defs.add(new DeleteDefintion(mainTable.getSqlTable(), mainTable.getSqlColId(), getInstance().getId()));
184                 final SQLDelete delete = Context.getDbType().newDelete(defs.toArray(new DeleteDefintion[defs.size()]));
185                 delete.execute(con.getConnection());
186             } catch (final SQLException e) {
187                 throw new EFapsException(getClass(),
188                                          "executeWithoutAccessCheck.SQLException", e, this.instance);
189             }
190             con.commit();
191         } finally {
192             if ((con != null) && con.isOpened()) {
193                 con.abort();
194             }
195         }
196     }
197 
198     /**
199      * This is the getter method for instance variable {@link #instance}.
200      *
201      * @return value of instance variable {@link #instance}
202      * @see #instance
203      */
204     public Instance getInstance()
205     {
206         return this.instance;
207     }
208 
209     /**
210      * The method gets all events for the given EventType and executes them in
211      * the given order. If no events are defined, nothing is done. The method
212      * return <i>true</i> if a event was found, otherwise <i>false</i>.
213      *
214      * @param _eventtype    event type to execute
215      * @return <i>true</i> if a trigger was found and executed, otherwise
216      *         <i>false</i>
217      * @throws EFapsException on error
218      */
219     private boolean executeEvents(final EventType _eventtype)
220         throws EFapsException
221     {
222         final boolean ret;
223         final  List<EventDefinition> triggers = getInstance().getType().getEvents(_eventtype);
224         if (triggers == null) {
225             ret = false;
226         } else  {
227             final   Parameter parameter = new Parameter();
228 
229             parameter.put(ParameterValues.INSTANCE, getInstance());
230             for (final EventDefinition evenDef : triggers) {
231                 evenDef.execute(parameter);
232             }
233             ret = true;
234         }
235         return ret;
236     }
237 }