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