001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library 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 GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 * $Id: ActionService.java 28460 2008-01-03 15:34:05Z sfermigier $
018 */
019
020package org.nuxeo.ecm.platform.actions;
021
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.List;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
029import org.nuxeo.runtime.model.ComponentContext;
030import org.nuxeo.runtime.model.ComponentInstance;
031import org.nuxeo.runtime.model.ComponentName;
032import org.nuxeo.runtime.model.DefaultComponent;
033
034/**
035 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
036 */
037public class ActionService extends DefaultComponent implements ActionManager {
038
039    public static final ComponentName ID = new ComponentName("org.nuxeo.ecm.platform.actions.ActionService");
040
041    private static final long serialVersionUID = -5256555810901945824L;
042
043    private static final Log log = LogFactory.getLog(ActionService.class);
044
045    private ActionContributionHandler actions;
046
047    private FilterContributionHandler filters;
048
049    @Override
050    public void activate(ComponentContext context) {
051        filters = new FilterContributionHandler();
052        actions = new ActionContributionHandler(filters);
053    }
054
055    @Override
056    public void deactivate(ComponentContext context) {
057        actions = null;
058        filters = null;
059    }
060
061    /**
062     * Return the action registry
063     *
064     * @deprecated since 5.5: use interface methods on ActionManager instead of public methods on ActionService.
065     */
066    @Deprecated
067    public final ActionRegistry getActionRegistry() {
068        return actions.getRegistry();
069    }
070
071    /**
072     * Return the action filter registry
073     *
074     * @deprecated since 5.5: use interface methods on ActionManager instead of public methods on ActionService.
075     */
076    @Deprecated
077    public final ActionFilterRegistry getFilterRegistry() {
078        return filters.getRegistry();
079    }
080
081    private void applyFilters(ActionContext context, List<Action> actions) {
082        Iterator<Action> it = actions.iterator();
083        while (it.hasNext()) {
084            Action action = it.next();
085            if (!checkFilters(context, action)) {
086                it.remove();
087            }
088        }
089    }
090
091    private boolean checkFilters(ActionContext context, Action action) {
092        if (action == null) {
093            return false;
094        }
095        if (log.isDebugEnabled()) {
096            log.debug(String.format("Checking access for action '%s'...", action.getId()));
097        }
098
099        boolean granted = checkFilters(action, action.getFilterIds(), context);
100        if (granted) {
101            if (log.isDebugEnabled()) {
102                log.debug(String.format("Granting access for action '%s'", action.getId()));
103            }
104        } else {
105            if (log.isDebugEnabled()) {
106                log.debug(String.format("Denying access for action '%s'", action.getId()));
107            }
108        }
109        return granted;
110    }
111
112    @Override
113    public List<Action> getActions(String category, ActionContext context) {
114        return getActions(category, context, true);
115    }
116
117    @Override
118    public List<Action> getAllActions(String category) {
119        return getActionRegistry().getActions(category);
120    }
121
122    @Override
123    public List<Action> getActions(String category, ActionContext context, boolean hideUnavailableActions) {
124        List<Action> actions = getActionRegistry().getActions(category);
125        if (hideUnavailableActions) {
126            applyFilters(context, actions);
127            return actions;
128        } else {
129            List<Action> allActions = new ArrayList<Action>();
130            allActions.addAll(actions);
131            applyFilters(context, actions);
132
133            for (Action a : allActions) {
134                a.setAvailable(actions.contains(a));
135            }
136            return allActions;
137        }
138    }
139
140    @Override
141    public Action getAction(String actionId, ActionContext context, boolean hideUnavailableAction) {
142        Action action = getActionRegistry().getAction(actionId);
143        if (action != null) {
144            if (hideUnavailableAction) {
145                if (!checkFilters(context, action)) {
146                    return null;
147                }
148            } else {
149                if (!checkFilters(context, action)) {
150                    action.setAvailable(false);
151                }
152            }
153        }
154        return action;
155    }
156
157    @Override
158    public Action getAction(String actionId) {
159        return getActionRegistry().getAction(actionId);
160    }
161
162    @Override
163    public boolean isRegistered(String actionId) {
164        return getActionRegistry().getAction(actionId) != null;
165    }
166
167    @Override
168    public boolean isEnabled(String actionId, ActionContext context) {
169        Action action = getActionRegistry().getAction(actionId);
170        if (action != null) {
171            return isEnabled(action, context);
172        }
173        return false;
174    }
175
176    public boolean isEnabled(Action action, ActionContext context) {
177        ActionFilterRegistry filterReg = getFilterRegistry();
178        for (String filterId : action.getFilterIds()) {
179            ActionFilter filter = filterReg.getFilter(filterId);
180            if (filter != null && !filter.accept(action, context)) {
181                return false;
182            }
183        }
184        return true;
185    }
186
187    @Override
188    public ActionFilter[] getFilters(String actionId) {
189        Action action = getActionRegistry().getAction(actionId);
190        if (action == null) {
191            return null;
192        }
193        ActionFilterRegistry filterReg = getFilterRegistry();
194        List<String> filterIds = action.getFilterIds();
195        if (filterIds != null && !filterIds.isEmpty()) {
196            ActionFilter[] filters = new ActionFilter[filterIds.size()];
197            for (int i = 0; i < filters.length; i++) {
198                String filterId = filterIds.get(i);
199                filters[i] = filterReg.getFilter(filterId);
200            }
201            return filters;
202        }
203        return null;
204    }
205
206    @Override
207    public boolean checkFilter(String filterId, ActionContext context) {
208        ActionFilterRegistry filterReg = getFilterRegistry();
209        ActionFilter filter = filterReg.getFilter(filterId);
210        if (filter == null) {
211            return false;
212        }
213        return filter.accept(null, context);
214    }
215
216    @Override
217    public boolean checkFilters(List<String> filterIds, ActionContext context) {
218        return checkFilters(null, filterIds, context);
219    }
220
221    protected boolean checkFilters(Action action, List<String> filterIds, ActionContext context) {
222        ActionFilterRegistry filterReg = getFilterRegistry();
223        for (String filterId : filterIds) {
224            ActionFilter filter = filterReg.getFilter(filterId);
225            if (filter == null) {
226                continue;
227            }
228            if (!filter.accept(action, context)) {
229                // denying filter found => ignore following filters
230                if (log.isDebugEnabled()) {
231                    log.debug(String.format("Filter '%s' denied access", filterId));
232                }
233                return false;
234            }
235            if (log.isDebugEnabled()) {
236                log.debug(String.format("Filter '%s' granted access", filterId));
237            }
238        }
239        return true;
240    }
241
242    @Override
243    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
244        if ("actions".equals(extensionPoint)) {
245            actions.addContribution((Action) contribution);
246        } else if ("filters".equals(extensionPoint)) {
247            if (contribution.getClass() == FilterFactory.class) {
248                registerFilterFactory((FilterFactory) contribution);
249            } else {
250                filters.addContribution((DefaultActionFilter) contribution);
251            }
252        } else if ("typeCompatibility".equals(extensionPoint)) {
253            actions.getRegistry().getTypeCategoryRelations().add((TypeCompatibility) contribution);
254        }
255    }
256
257    @Override
258    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
259        if ("actions".equals(extensionPoint)) {
260            actions.removeContribution((Action) contribution);
261        } else if ("filters".equals(extensionPoint)) {
262            if (contribution.getClass() == FilterFactory.class) {
263                unregisterFilterFactory((FilterFactory) contribution);
264            } else {
265                filters.removeContribution((DefaultActionFilter) contribution);
266            }
267        }
268    }
269
270    /**
271     * @deprecated seems not used in Nuxeo - should be removed - and anyway the merge is not done
272     * @param ff
273     */
274    @Deprecated
275    protected void registerFilterFactory(FilterFactory ff) {
276        getFilterRegistry().removeFilter(ff.id);
277        try {
278            ActionFilter filter = (ActionFilter) Thread.currentThread().getContextClassLoader().loadClass(ff.className).newInstance();
279            filter.setId(ff.id);
280            getFilterRegistry().addFilter(filter);
281        } catch (ReflectiveOperationException e) {
282            log.error("Failed to create action filter", e);
283        }
284    }
285
286    /**
287     * @deprecated seems not used in Nuxeo - should be removed - and anyway the merge is not done
288     * @param ff
289     */
290    @Deprecated
291    public void unregisterFilterFactory(FilterFactory ff) {
292        getFilterRegistry().removeFilter(ff.id);
293    }
294
295    @Override
296    public void remove() {
297    }
298
299}