View Javadoc

1   /* This file is part of COPAL (COntext Provisioning for All).
2    *
3    * COPAL is a part of SM4All (Smart hoMes for All) project.
4    *
5    * COPAL is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * COPAL is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with COPAL. If not, see <http://www.gnu.org/licenses/>.
17   */
18  package at.ac.tuwien.infosys.sm4all.copal.api.processor;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.text.MessageFormat;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.HashSet;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Set;
29  import org.apache.log4j.Level;
30  import org.apache.log4j.Logger;
31  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEvent;
32  import at.ac.tuwien.infosys.sm4all.copal.api.event.CurrentAction;
33  import at.ac.tuwien.infosys.sm4all.copal.api.util.Name;
34  
35  /**
36   * Wrapper class that implements {@link ContextProcessor} around an
37   * {@link Object} which uses annotations to define its {@link Name} and
38   * {@link Actions} {@link Method}s.
39   * 
40   * @author sanjin
41   */
42  public class AnnotatedProcessor extends BaseProcessor {
43  
44      private static final Logger LOGGER = Logger.getLogger(AnnotatedProcessor.class);
45  
46      private final List<ActionsMethod> methods;
47  
48      /**
49       * Creates instance of wrapper {@link ContextProcessor} that wraps around
50       * specified annotated processor.
51       * 
52       * @param processor the annotated processor.
53       * @throws NullPointerException if specified annotated processor is
54       *         <code>null</code>.
55       * @throws IllegalArgumentException if specified annotated processor has no
56       *         {@link Name} annotation or there are no methods with
57       *         {@link Action} or {@link Actions} annotations.
58       */
59      public AnnotatedProcessor(final Object processor) {
60          super(getName(processor), getActions(processor));
61  
62          this.methods = getMethods(processor);
63      }
64  
65      /**
66       * Invokes all {@link Actions} {@link Method}s of the underlying annotated
67       * processor that can handle specified {@link ContextEvent}. Results of all
68       * invocations are merged and returned.
69       * 
70       * @param action the {@link ProcessorAction} (ignored and instead the
71       *        {@link CurrentAction} of specified {@link ContextEvent} is used).
72       * @param event the {@link ContextEvent}.
73       * @return the resulting {@link ContextEvent}s of the underlying
74       *         {@link Actions} {@link Method}.
75       */
76      @Override
77      public ContextEvent[] process(final ProcessorAction action,
78              final ContextEvent event) {
79          final String eventType = event.getType().getName();
80          final Set<ContextEvent> result = new HashSet<ContextEvent>();
81  
82          for (final ActionsMethod method : this.methods) {
83              if (method.canBeInvokedWith(action, event)) {
84                  try {
85                      result.addAll(Arrays.asList(method.invoke(action, event)));
86                  } catch (final InvocationTargetException ex) {
87                      if (LOGGER.isEnabledFor(Level.ERROR)) {
88                          LOGGER.error(
89                                  MessageFormat.format(
90                                          "Failed Actions method ''{0}'' for event ''{1}'' in processor ''{2}''!",
91                                          method.getName(), eventType, getName()),
92                                  ex);
93                      }
94                  } catch (final IllegalAccessException ex) {
95                      if (LOGGER.isEnabledFor(Level.ERROR)) {
96                          LOGGER.error(
97                                  MessageFormat.format(
98                                          "Cannot invoke Actions method ''{0}'' for event ''{1}'' in processor ''{2}''!",
99                                          method.getName(), eventType, getName()),
100                                 ex);
101                     }
102                 }
103             }
104         }
105 
106         return result.toArray(new ContextEvent[result.size()]);
107     }
108 
109     private static ProcessorAction[] getActions(final Object processor) {
110         if (null == processor) {
111             throw new IllegalArgumentException("Processor cannot be null.");
112         }
113 
114         final List<ActionsMethod> methods = getMethods(processor);
115         final Set<ProcessorAction> result = new HashSet<ProcessorAction>();
116 
117         for (final ActionsMethod method : methods) {
118             result.addAll(Arrays.asList(method.getActions()));
119         }
120 
121         if (result.isEmpty()) {
122             throw new IllegalArgumentException("No Actions methods.");
123         }
124 
125         return result.toArray(new ProcessorAction[result.size()]);
126     }
127 
128     private static List<ActionsMethod> getMethods(final Object processor) {
129         if (null == processor) {
130             throw new IllegalArgumentException("Processor cannot be null.");
131         }
132 
133         final String name = getName(processor);
134         final List<ActionsMethod> result = new LinkedList<ActionsMethod>();
135 
136         for (final Method method : processor.getClass().getMethods()) {
137             if ((null != method.getAnnotation(Action.class))
138                     || (null != method.getAnnotation(Actions.class))) {
139                 try {
140                     result.add(new ActionsMethod(processor, method));
141                 } catch (final IllegalArgumentException ex) {
142                     if (LOGGER.isEnabledFor(Level.WARN)) {
143                         LOGGER.warn(
144                                 MessageFormat.format(
145                                         "Malformed Actions method ''{0}'' in processor ''{1}''!",
146                                         method.getName(), name), ex);
147                     }
148                 }
149             }
150         }
151 
152         return Collections.unmodifiableList(result);
153     }
154 
155     private static String getName(final Object processor) {
156         if (null == processor) {
157             throw new IllegalArgumentException("Processor cannot be null.");
158         }
159 
160         final Name name = processor.getClass().getAnnotation(Name.class);
161         if (null == name) {
162             throw new IllegalArgumentException(
163                     "Specified processor is not annotated with Name.");
164         }
165 
166         return name.value();
167     }
168 }