1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.model.action;
17
18 import org.fourthline.cling.model.meta.ActionArgument;
19 import org.fourthline.cling.model.meta.LocalService;
20 import org.fourthline.cling.model.profile.RemoteClientInfo;
21 import org.fourthline.cling.model.state.StateVariableAccessor;
22 import org.fourthline.cling.model.types.ErrorCode;
23 import org.seamless.util.Reflections;
24
25 import java.lang.reflect.Constructor;
26 import java.lang.reflect.Method;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.logging.Logger;
31
32
33
34
35
36
37
38
39
40
41
42
43
44 public class MethodActionExecutor extends AbstractActionExecutor {
45
46 private static Logger log = Logger.getLogger(MethodActionExecutor.class.getName());
47
48 protected Method method;
49
50 public MethodActionExecutor(Method method) {
51 this.method = method;
52 }
53
54 public MethodActionExecutor(Map<ActionArgument<LocalService>, StateVariableAccessor> outputArgumentAccessors, Method method) {
55 super(outputArgumentAccessors);
56 this.method = method;
57 }
58
59 public Method getMethod() {
60 return method;
61 }
62
63 @Override
64 protected void execute(ActionInvocation<LocalService> actionInvocation, Object serviceImpl) throws Exception {
65
66
67 Object[] inputArgumentValues = createInputArgumentValues(actionInvocation, method);
68
69
70 if (!actionInvocation.getAction().hasOutputArguments()) {
71 log.fine("Calling local service method with no output arguments: " + method);
72 Reflections.invoke(method, serviceImpl, inputArgumentValues);
73 return;
74 }
75
76 boolean isVoid = method.getReturnType().equals(Void.TYPE);
77
78 log.fine("Calling local service method with output arguments: " + method);
79 Object result;
80 boolean isArrayResultProcessed = true;
81 if (isVoid) {
82
83 log.fine("Action method is void, calling declared accessors(s) on service instance to retrieve ouput argument(s)");
84 Reflections.invoke(method, serviceImpl, inputArgumentValues);
85 result = readOutputArgumentValues(actionInvocation.getAction(), serviceImpl);
86
87 } else if (isUseOutputArgumentAccessors(actionInvocation)) {
88
89 log.fine("Action method is not void, calling declared accessor(s) on returned instance to retrieve ouput argument(s)");
90 Object returnedInstance = Reflections.invoke(method, serviceImpl, inputArgumentValues);
91 result = readOutputArgumentValues(actionInvocation.getAction(), returnedInstance);
92
93 } else {
94
95 log.fine("Action method is not void, using returned value as (single) output argument");
96 result = Reflections.invoke(method, serviceImpl, inputArgumentValues);
97 isArrayResultProcessed = false;
98 }
99
100 ActionArgument<LocalService>[] outputArgs = actionInvocation.getAction().getOutputArguments();
101
102 if (isArrayResultProcessed && result instanceof Object[]) {
103 Object[] results = (Object[]) result;
104 log.fine("Accessors returned Object[], setting output argument values: " + results.length);
105 for (int i = 0; i < outputArgs.length; i++) {
106 setOutputArgumentValue(actionInvocation, outputArgs[i], results[i]);
107 }
108 } else if (outputArgs.length == 1) {
109 setOutputArgumentValue(actionInvocation, outputArgs[0], result);
110 } else {
111 throw new ActionException(
112 ErrorCode.ACTION_FAILED,
113 "Method return does not match required number of output arguments: " + outputArgs.length
114 );
115 }
116
117 }
118
119 protected boolean isUseOutputArgumentAccessors(ActionInvocation<LocalService> actionInvocation) {
120 for (ActionArgument argument : actionInvocation.getAction().getOutputArguments()) {
121
122 if (getOutputArgumentAccessors().get(argument) != null) {
123 return true;
124 }
125 }
126 return false;
127 }
128
129 protected Object[] createInputArgumentValues(ActionInvocation<LocalService> actionInvocation, Method method) throws ActionException {
130
131 LocalService service = actionInvocation.getAction().getService();
132
133 List values = new ArrayList<>();
134 int i = 0;
135 for (ActionArgument<LocalService> argument : actionInvocation.getAction().getInputArguments()) {
136
137 Class methodParameterType = method.getParameterTypes()[i];
138
139 ActionArgumentValue<LocalService> inputValue = actionInvocation.getInput(argument);
140
141
142 if (methodParameterType.isPrimitive() && (inputValue == null || inputValue.toString().length() == 0))
143 throw new ActionException(
144 ErrorCode.ARGUMENT_VALUE_INVALID,
145 "Primitive action method argument '" + argument.getName() + "' requires input value, can't be null or empty string"
146 );
147
148
149 if (inputValue == null) {
150 values.add(i++, null);
151 continue;
152 }
153
154
155 String inputCallValueString = inputValue.toString();
156
157 if (inputCallValueString.length() > 0 && service.isStringConvertibleType(methodParameterType) && !methodParameterType.isEnum()) {
158 try {
159 Constructor<String> ctor = methodParameterType.getConstructor(String.class);
160 log.finer("Creating new input argument value instance with String.class constructor of type: " + methodParameterType);
161 Object o = ctor.newInstance(inputCallValueString);
162 values.add(i++, o);
163 } catch (Exception ex) {
164 log.warning("Error preparing action method call: " + method);
165 log.warning("Can't convert input argument string to desired type of '" + argument.getName() + "': " + ex);
166 throw new ActionException(
167 ErrorCode.ARGUMENT_VALUE_INVALID, "Can't convert input argument string to desired type of '" + argument.getName() + "': " + ex
168 );
169 }
170 } else {
171
172 values.add(i++, inputValue.getValue());
173 }
174 }
175
176 if (method.getParameterTypes().length > 0
177 && RemoteClientInfo.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length-1])) {
178 if (actionInvocation instanceof RemoteActionInvocation &&
179 ((RemoteActionInvocation)actionInvocation).getRemoteClientInfo() != null) {
180 log.finer("Providing remote client info as last action method input argument: " + method);
181 values.add(i, ((RemoteActionInvocation)actionInvocation).getRemoteClientInfo());
182 } else {
183
184 values.add(i, null);
185 }
186 }
187
188 return values.toArray(new Object[values.size()]);
189 }
190
191 }