001    /*
002      Copyright (C) 2002-2003 Renaud Pawlak <renaud@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
017    
018    package org.objectweb.jac.aspects.integrity;
019    
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Set;
023    import java.util.Vector;
024    import org.aopalliance.intercept.MethodInvocation;
025    import org.apache.log4j.Logger;
026    import org.objectweb.jac.core.AspectComponent;
027    import org.objectweb.jac.core.Collaboration;
028    import org.objectweb.jac.core.Wrapper;
029    import org.objectweb.jac.core.rtti.CollectionItem;
030    import org.objectweb.jac.core.rtti.FieldItem;
031    import org.objectweb.jac.core.rtti.MethodItem;
032    import org.objectweb.jac.core.rtti.RttiAC;
033    import org.objectweb.jac.util.Strings;
034    
035    /**
036     * This aspect handle different kinds of data integrity among objects
037     * sets. */
038    
039    public class IntegrityAC extends AspectComponent implements IntegrityConf {
040        static final Logger loggerRep = Logger.getLogger("integrity.repository");
041    
042            HashMap preConditionsFields = new HashMap();
043            HashMap postConditionsFields = new HashMap();
044    
045            RoleWrapper roleWrapper = new RoleWrapper(this);
046    
047            public static final String CONSTRAINTS = "IntegrityAC.CONSTRAINTS";
048            public static final String DISABLE_ROLE_UPDATES =
049                    "IntegrityAC.DISABLE_ROLE_UPDATES";
050    
051            public static final String DISABLE_ADD_TO_REPOSITORY =
052                    "IntegrityAC.DISABLE_ADD_TO_REPOSITORY";
053    
054            public void declareRepository(
055                    String repositoryName,
056                    CollectionItem collection,
057                    FieldItem field) 
058        {
059                    RepositoryWrapper rw =
060                            new RepositoryWrapper(
061                                    this,
062                                    repositoryName,
063                                    collection,
064                                    field,
065                                    RepositoryWrapper.ADDER);
066                    if (field instanceof CollectionItem) {
067                            pointcut(
068                                    "ALL",
069                                    field.getParent().getName(),
070                                    "ADDERS(" + field.getName() + ")",
071                                    rw,
072                                    null);
073                    } else {
074                            pointcut(
075                                    "ALL",
076                                    field.getParent().getName(),
077                                    "SETTERS(" + field.getName() + ")",
078                                    rw,
079                                    null);
080                    }
081    
082                    // This pointcut adds an attribute in context so that we can
083                    // avoid adding an object twice in the repository
084                    pointcut(repositoryName,collection.getParent().getName(),
085                             "ADDERS("+collection.getName()+")",
086                     new AntiDupWrapper(this,collection),null);
087            }
088    
089        class AntiDupWrapper extends Wrapper {
090            public AntiDupWrapper(AspectComponent ac, CollectionItem collection) {
091                super(ac);
092                this.collection = collection;
093            }
094            CollectionItem collection;
095            public Object invoke(MethodInvocation invocation) throws Throwable {
096                disableAddToRepository(collection);
097                loggerRep.debug("doAddToRepository " + collection);
098                try {
099                    return proceed(invocation);
100                } finally {
101                    enableAddToRepository(collection);
102                }
103            }
104        }
105    
106            public void declareConstraint(
107                    FieldItem relation,
108                    FieldItem target,
109                    String constraint) 
110        {
111                    List constraints = (List) target.getAttributeAlways(CONSTRAINTS);
112                    if (constraints == null) {
113                            constraints = new Vector();
114                            target.setAttribute(CONSTRAINTS, constraints);
115                    }
116                    int c;
117                    if (constraint.compareToIgnoreCase("DELETE_CASCADE") == 0) {
118                            warning("DELETE_CASCADE is not implemented yet");
119                            c = Constraint.DELETE_CASCADE;
120                    } else if (constraint.compareToIgnoreCase("SET_NULL") == 0) {
121                            c = Constraint.SET_NULL;
122                    } else if (constraint.compareToIgnoreCase("FORBIDDEN") == 0) {
123                            c = Constraint.FORBIDDEN;
124                    } else {
125                            throw new RuntimeException("Unknown constraint type " + constraint);
126                    }
127                    constraints.add(new Constraint(relation, c));
128                    if (target instanceof CollectionItem) {
129                            pointcut(
130                                    "ALL",
131                                    target.getParent().getName(),
132                                    "REMOVERS(" + target.getName() + ")",
133                                    new RepositoryWrapper(
134                                            this,
135                                            "",
136                                            (CollectionItem) target,
137                                            null,
138                                            RepositoryWrapper.REMOVER),
139                                    null);
140                    } else {
141                            pointcut(
142                                    "ALL",
143                                    target.getParent().getName(),
144                                    "SETTERS(" + target.getName() + ")",
145                                    new RepositoryWrapper(
146                                            this,
147                                            "",
148                                            null,
149                                            target,
150                                            RepositoryWrapper.REMOVER),
151                                    null);
152                    }
153            }
154    
155            boolean hasConditions = false;
156    
157            private void addCondition(
158                    FieldItem field,
159                    MethodItem constraint,
160                    Object[] params,
161                    String errorMsg,
162                    HashMap conditionsList) 
163        {
164                    hasConditions = true;
165                    field.setAttribute(RttiAC.CONSTRAINTS, Boolean.TRUE);
166    
167                    Vector vect = (Vector) conditionsList.get(field);
168                    if (vect == null)
169                            vect = new Vector();
170    
171                    ConstraintDescriptor obj =
172                            new ConstraintDescriptor(constraint, params, errorMsg);
173                    vect.add(obj);
174                    conditionsList.put(field, vect);
175            }
176    
177            public void addPostCondition(
178                    FieldItem field,
179                    MethodItem constraint,
180                    Object[] params,
181                    String errorMsg) 
182        {
183                    addCondition(
184                            field,
185                            constraint,
186                            params,
187                            errorMsg,
188                            postConditionsFields);
189            }
190    
191            public void addPreCondition(
192                    FieldItem field,
193                    MethodItem constraint,
194                    Object[] params,
195                    String errorMsg) 
196        {
197                    addCondition(
198                            field,
199                            constraint,
200                            params,
201                            errorMsg,
202                            preConditionsFields);
203            }
204    
205            /**
206             * Activates constraints checking.
207             *
208             * <p>This method must be called once after all calls to
209             * <code>addPreCondition</code> and <code>addPostCondition</code>
210             * to effectively add pointcuts for these methods.</p>
211             *
212             * @see #addPreCondition(FieldItem,MethodItem,Object[],String)
213             * @see #addPostCondition(FieldItem,MethodItem,Object[],String)
214             */
215            protected void doCheck() {
216                    ConstraintWrapper cw = new ConstraintWrapper(this);
217                    pointcut(
218                            "ALL",
219                            "ALL",
220                            "SETTERS(<" + RttiAC.CONSTRAINTS + ">)",
221                            cw,
222                            null);
223            }
224    
225            /**
226             * Activates primary keys checking.
227             *
228             * <p>This method must be called once in the file integrity.acc to
229             * effectively add pointcuts for primary key checking methods.</p>
230             *
231             * @see org.objectweb.jac.core.rtti.RttiAC#definePrimaryKey(ClassItem,String,String[])
232             */
233            void doPrimaryKeyCheck() {
234                    PrimaryKeyWrapper pw = new PrimaryKeyWrapper(this);
235                    pointcut(
236                            "ALL",
237                            "ALL",
238                            "ADDERS(<" + RttiAC.PRIMARY_KEY + ">)",
239                            pw,
240                            null);
241            }
242    
243        boolean updateAssociations = false;
244        public void updateAssociations() {
245            this.updateAssociations = true;
246        }
247    
248            public void whenConfigured() {
249            if (!updateAssociations)
250                return;
251                    if (hasConditions) {
252                            doCheck();
253                    }
254                    doPrimaryKeyCheck();
255            Set classes = ((RttiAC)getAC("rtti")).getClassesWithAssociations();
256            if (!classes.isEmpty()) {
257                String classExpr = Strings.join(classes, " || ");
258                pointcut(
259                    "ALL",
260                    classExpr,
261                    "ADDERS(<" + RttiAC.OPPOSITE_ROLE + ">) "
262                                    + "|| REMOVERS(<" + RttiAC.OPPOSITE_ROLE + ">) "
263                                    + "|| SETTERS(<" + RttiAC.OPPOSITE_ROLE      + ">) "
264                                    + "|| CONSTRUCTORS",
265                    roleWrapper,
266                    null);
267                //pointcut("ALL",classExpr,"CONSTRUCTORS",
268                    //         roleWrapper,null);
269            }
270        }
271    
272        /**
273         * Tells wether adding objects to a repository collection is enabled or not.
274         * @param collection the repository collection to test
275         */
276        public static boolean isAddToRepositoryEnabled(CollectionItem collection) {
277            List disabled = getDisabled();
278            loggerRep.debug("isAddToRepositoryEnabled "+collection+" (disabled="+disabled+") ?");
279            return !disabled.contains(collection);
280        }
281    
282        /**
283         * Disables adding objects to a repository collection. 
284         * @param collection the repository collection to disable
285         */
286        public static void disableAddToRepository(CollectionItem collection) {
287            Collaboration collab = Collaboration.get();
288            List disabled = getDisabled();
289            loggerRep.debug("disableAddToRepository "+collection+
290                            " (disabled="+disabled+")");
291            disabled.add(collection);
292        }
293    
294        /**
295         * Enables adding objects to a repository collection. 
296         * @param collection the repository collection to enable
297         */
298        public static void enableAddToRepository(CollectionItem collection) {
299            loggerRep.debug("enableAddToRepository "+collection);
300            List disabled = getDisabled();
301            disabled.remove(collection);
302        }
303    
304        /**
305         * Gets the list of disabled repository
306         */
307        protected static List getDisabled() {
308            List disabled = 
309                (List)Collaboration.get().getAttribute(
310                    IntegrityAC.DISABLE_ADD_TO_REPOSITORY);
311            if (disabled==null) {
312                disabled = new Vector();
313                Collaboration.get().addAttribute(
314                    IntegrityAC.DISABLE_ADD_TO_REPOSITORY, 
315                    disabled);
316            }
317            return disabled;
318        }
319    
320        public static void disableRoleUpdates() {
321            Collaboration.get().addAttribute(DISABLE_ROLE_UPDATES,Boolean.TRUE);
322        }
323        public static void enableRoleUpdates() {
324            Collaboration.get().removeAttribute(DISABLE_ROLE_UPDATES);
325        }
326    }