cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject [4/6] CLOUDSTACK-6003 fixing plus refactoring dispatcher
Date Tue, 04 Feb 2014 12:38:35 GMT
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/447430c3/server/src/com/cloud/api/dispatch/DispatchChainFactory.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/dispatch/DispatchChainFactory.java b/server/src/com/cloud/api/dispatch/DispatchChainFactory.java
new file mode 100644
index 0000000..f3c6973
--- /dev/null
+++ b/server/src/com/cloud/api/dispatch/DispatchChainFactory.java
@@ -0,0 +1,68 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.api.dispatch;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import com.cloud.user.AccountManager;
+
+public class DispatchChainFactory {
+
+    @Inject
+    protected AccountManager _accountMgr = null;
+
+    @Inject
+    protected ParamGenericValidationWorker paramGenericValidationWorker = null;
+
+    @Inject
+    protected ParamProcessWorker paramProcessWorker = null;
+
+    @Inject
+    protected ParamSemanticValidationWorker paramSemanticValidationWorker = null;
+
+    @Inject
+    protected CommandCreationWorker commandCreationWorker = null;
+
+    protected DispatchChain standardDispatchChain = null;
+
+    protected DispatchChain asyncCreationDispatchChain = null;
+
+    @PostConstruct
+    public void setup() {
+        standardDispatchChain = new DispatchChain().
+                add(paramGenericValidationWorker).
+                add(paramProcessWorker).
+                add(paramSemanticValidationWorker);
+
+        asyncCreationDispatchChain = new DispatchChain().
+                add(paramGenericValidationWorker).
+                add(paramProcessWorker).
+                add(paramSemanticValidationWorker).
+                add(commandCreationWorker);
+
+    }
+
+    public DispatchChain getStandardDispatchChain() {
+        return standardDispatchChain;
+    }
+
+    public DispatchChain getAsyncCreationDispatchChain() {
+        return asyncCreationDispatchChain;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/447430c3/server/src/com/cloud/api/dispatch/DispatchWorker.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/dispatch/DispatchWorker.java b/server/src/com/cloud/api/dispatch/DispatchWorker.java
new file mode 100644
index 0000000..5f2b592
--- /dev/null
+++ b/server/src/com/cloud/api/dispatch/DispatchWorker.java
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.api.dispatch;
+
+import java.util.Map;
+
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.ServerApiException;
+
+public interface DispatchWorker {
+
+    /**
+    *
+    * @return false to stop the chain of responsibility, true
+    * to continue the chain with the next worker
+    */
+    public boolean handle(BaseCmd cmd, Map<String, Object> params)
+            throws ServerApiException;
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/447430c3/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java b/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java
new file mode 100644
index 0000000..edc1510
--- /dev/null
+++ b/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java
@@ -0,0 +1,95 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.api.dispatch;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+
+/**
+ * This worker validates parameters in a generic way, by using annotated
+ * restrictions without involving the {@Link BaseCmd}. This worker doesn't
+ * know or care about the meaning of the parameters and that's why we can
+ * have it out of the {@Link BaseCmd}
+ *
+ * @author afornie
+ */
+public class ParamGenericValidationWorker implements DispatchWorker {
+
+    protected static Logger s_logger = Logger.getLogger(ParamGenericValidationWorker.class.getName());
+
+    protected static List<String> defaultParamNames = new ArrayList<String>();
+
+    static {
+        defaultParamNames.add(ApiConstants.CTX_START_EVENT_ID);
+        defaultParamNames.add(ApiConstants.COMMAND);
+        defaultParamNames.add(ApiConstants.USERNAME);
+        defaultParamNames.add(ApiConstants.USER_ID);
+        defaultParamNames.add(ApiConstants.PASSWORD);
+        defaultParamNames.add(ApiConstants.DOMAIN);
+        defaultParamNames.add(ApiConstants.DOMAIN_ID);
+        defaultParamNames.add(ApiConstants.DOMAIN__ID);
+        defaultParamNames.add(ApiConstants.SESSIONKEY);
+        defaultParamNames.add(ApiConstants.RESPONSE);
+        defaultParamNames.add(ApiConstants.PAGE);
+        defaultParamNames.add(ApiConstants.PAGE_SIZE);
+        defaultParamNames.add(ApiConstants.HTTPMETHOD);
+        defaultParamNames.add("_");
+    }
+
+    protected static final String ERROR_MSG_PREFIX = "Unknown parameters :";
+
+    @Override
+    public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
+        final List<String> expectedParamNames = getParamNamesForCommand(cmd);
+
+        final StringBuilder errorMsg = new StringBuilder(ERROR_MSG_PREFIX);
+        boolean foundUnknownParam = false;
+        for (final String paramName : params.keySet()) {
+            if (!expectedParamNames.contains(paramName)) {
+                errorMsg.append(" ").append(paramName);
+                foundUnknownParam= true;
+            }
+        }
+
+        if (foundUnknownParam) {
+            s_logger.warn(String.format("Received unkown parameters for command %s. %s",
cmd.getCommandName(), errorMsg));
+        }
+
+        return true;
+    }
+
+    protected List<String> getParamNamesForCommand(final BaseCmd cmd) {
+        final List<String> paramNames = new ArrayList<String>();
+        // The expected param names are all the specific for the current command class ...
+        for (final Field field : cmd.getParamFields()) {
+            final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
+            paramNames.add(parameterAnnotation.name());
+        }
+        // ... plus the default ones
+        paramNames.addAll(defaultParamNames);
+        return paramNames;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/447430c3/server/src/com/cloud/api/dispatch/ParamProcessWorker.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/dispatch/ParamProcessWorker.java b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java
new file mode 100644
index 0000000..6c058c3
--- /dev/null
+++ b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java
@@ -0,0 +1,429 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.api.dispatch;
+
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+
+import java.lang.reflect.Field;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.InfrastructureEntity;
+import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.cloudstack.api.ACL;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.api.InternalIdentity;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.BaseCmd.CommandType;
+import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd;
+import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd;
+import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd;
+import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd;
+import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.db.EntityManager;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class ParamProcessWorker implements DispatchWorker {
+
+    private static final Logger s_logger = Logger.getLogger(ParamProcessWorker.class.getName());
+
+    @Inject
+    protected AccountManager _accountMgr = null;
+
+    @Inject
+    protected EntityManager _entityMgr = null;
+
+    @Override
+    public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
+        processParameters(cmd, params);
+        return true;
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public void processParameters(final BaseCmd cmd, final Map<String, Object> params)
{
+        final Map<Object, AccessType> entitiesToAccess = new HashMap<Object, AccessType>();
+
+        final List<Field> cmdFields = cmd.getParamFields();
+
+        for (final Field field : cmdFields) {
+            final Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
+            final Object paramObj = params.get(parameterAnnotation.name());
+            if (paramObj == null) {
+                if (parameterAnnotation.required()) {
+                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute
API command " +
+                        cmd.getCommandName().substring(0, cmd.getCommandName().length() -
8) +
+                        " due to missing parameter " + parameterAnnotation.name());
+                }
+                continue;
+            }
+
+            // marshall the parameter into the correct type and set the field value
+            try {
+                setFieldValue(field, cmd, paramObj, parameterAnnotation);
+            } catch (final IllegalArgumentException argEx) {
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("Unable to execute API command " + cmd.getCommandName()
+ " due to invalid value " + paramObj + " for parameter " +
+                            parameterAnnotation.name());
+                }
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute
API command " +
+                    cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)
+ " due to invalid value " + paramObj + " for parameter " +
+                    parameterAnnotation.name());
+            } catch (final ParseException parseEx) {
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("Invalid date parameter " + paramObj + " passed to command
" + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
+                }
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to parse date
" + paramObj + " for command " +
+                    cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)
+ ", please pass dates in the format mentioned in the api documentation");
+            } catch (final InvalidParameterValueException invEx) {
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute
API command " +
+                    cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)
+ " due to invalid value. " + invEx.getMessage());
+            } catch (final CloudRuntimeException cloudEx) {
+                s_logger.error("CloudRuntimeException", cloudEx);
+                // FIXME: Better error message? This only happens if the API command is not
executable, which typically
+                //means
+                // there was
+                // and IllegalAccessException setting one of the parameters.
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Internal error
executing API command " +
+                    cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8));
+            }
+
+            //check access on the resource this field points to
+            try {
+                final ACL checkAccess = field.getAnnotation(ACL.class);
+                final CommandType fieldType = parameterAnnotation.type();
+
+                if (checkAccess != null) {
+                    // Verify that caller can perform actions in behalf of vm owner
+                    //acumulate all Controlled Entities together.
+
+                    //parse the array of resource types and in case of map check access on
key or value or both as specified in @acl
+                    //implement external dao for classes that need findByName
+                    //for maps, specify access to be checkd on key or value.
+
+                    // find the controlled entity DBid by uuid
+                    if (parameterAnnotation.entityType() != null) {
+                        final Class<?>[] entityList = parameterAnnotation.entityType()[0].getAnnotation(EntityReference.class).value();
+
+                        for (final Class entity : entityList) {
+                            // Check if the parameter type is a single
+                            // Id or list of id's/name's
+                            switch (fieldType) {
+                                case LIST:
+                                    final CommandType listType = parameterAnnotation.collectionType();
+                                    switch (listType) {
+                                        case LONG:
+                                        case UUID:
+                                            final List<Long> listParam = (List<Long>)field.get(cmd);
+                                            for (final Long entityId : listParam) {
+                                                final Object entityObj = _entityMgr.findById(entity,
entityId);
+                                                entitiesToAccess.put(entityObj, checkAccess.accessType());
+                                            }
+                                            break;
+                                        /*
+                                         * case STRING: List<String> listParam =
+                                         * new ArrayList<String>(); listParam =
+                                         * (List)field.get(cmd); for(String
+                                         * entityName: listParam){
+                                         * ControlledEntity entityObj =
+                                         * (ControlledEntity
+                                         * )daoClassInstance(entityId);
+                                         * entitiesToAccess.add(entityObj); }
+                                         * break;
+                                         */
+                                        default:
+                                            break;
+                                    }
+                                    break;
+                                case LONG:
+                                case UUID:
+                                    final Object entityObj = _entityMgr.findById(entity,
(Long)field.get(cmd));
+                                    entitiesToAccess.put(entityObj, checkAccess.accessType());
+                                    break;
+                                default:
+                                    break;
+                            }
+
+                            if (ControlledEntity.class.isAssignableFrom(entity)) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("ControlledEntity name is:" + entity.getName());
+                                }
+                            }
+
+                            if (InfrastructureEntity.class.isAssignableFrom(entity)) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("InfrastructureEntity name is:" + entity.getName());
+                                }
+                            }
+                        }
+
+                    }
+
+                }
+
+            } catch (final IllegalArgumentException e) {
+                s_logger.error("Error initializing command " + cmd.getCommandName() + ",
field " + field.getName() + " is not accessible.");
+                throw new CloudRuntimeException("Internal error initializing parameters for
command " + cmd.getCommandName() + " [field " + field.getName() +
+                    " is not accessible]");
+            } catch (final IllegalAccessException e) {
+                s_logger.error("Error initializing command " + cmd.getCommandName() + ",
field " + field.getName() + " is not accessible.");
+                throw new CloudRuntimeException("Internal error initializing parameters for
command " + cmd.getCommandName() + " [field " + field.getName() +
+                    " is not accessible]");
+            }
+
+        }
+
+        doAccessChecks(cmd, entitiesToAccess);
+    }
+
+
+    private void doAccessChecks(final BaseCmd cmd, final Map<Object, AccessType> entitiesToAccess)
{
+        final Account caller = CallContext.current().getCallingAccount();
+        final Account owner = _accountMgr.getActiveAccountById(cmd.getEntityOwnerId());
+
+        if (cmd instanceof BaseAsyncCreateCmd) {
+            //check that caller can access the owner account.
+            _accountMgr.checkAccess(caller, null, true, owner);
+        }
+
+        if (!entitiesToAccess.isEmpty()) {
+            //check that caller can access the owner account.
+            _accountMgr.checkAccess(caller, null, true, owner);
+            for (final Object entity : entitiesToAccess.keySet()) {
+                if (entity instanceof ControlledEntity) {
+                    _accountMgr.checkAccess(caller, entitiesToAccess.get(entity), true, (ControlledEntity)entity);
+                } else if (entity instanceof InfrastructureEntity) {
+                    //FIXME: Move this code in adapter, remove code from Account manager
+                }
+            }
+        }
+    }
+
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object paramObj,
final Parameter annotation) throws IllegalArgumentException, ParseException {
+        try {
+            field.setAccessible(true);
+            final CommandType fieldType = annotation.type();
+            switch (fieldType) {
+                case BOOLEAN:
+                    field.set(cmdObj, Boolean.valueOf(paramObj.toString()));
+                    break;
+                case DATE:
+                    // This piece of code is for maintaining backward compatibility
+                    // and support both the date formats(Bug 9724)
+                    // Do the date messaging for ListEventsCmd only
+                    if (cmdObj instanceof ListEventsCmd || cmdObj instanceof DeleteEventsCmd
|| cmdObj instanceof ArchiveEventsCmd ||
+                        cmdObj instanceof ArchiveAlertsCmd || cmdObj instanceof DeleteAlertsCmd)
{
+                        final boolean isObjInNewDateFormat = isObjInNewDateFormat(paramObj.toString());
+                        if (isObjInNewDateFormat) {
+                            final DateFormat newFormat = BaseCmd.NEW_INPUT_FORMAT;
+                            synchronized (newFormat) {
+                                field.set(cmdObj, newFormat.parse(paramObj.toString()));
+                            }
+                        } else {
+                            final DateFormat format = BaseCmd.INPUT_FORMAT;
+                            synchronized (format) {
+                                Date date = format.parse(paramObj.toString());
+                                if (field.getName().equals("startDate")) {
+                                    date = messageDate(date, 0, 0, 0);
+                                } else if (field.getName().equals("endDate")) {
+                                    date = messageDate(date, 23, 59, 59);
+                                }
+                                field.set(cmdObj, date);
+                            }
+                        }
+                    } else {
+                        final DateFormat format = BaseCmd.INPUT_FORMAT;
+                        format.setLenient(false);
+                        synchronized (format) {
+                            field.set(cmdObj, format.parse(paramObj.toString()));
+                        }
+                    }
+                    break;
+                case FLOAT:
+                    // Assuming that the parameters have been checked for required before
now,
+                    // we ignore blank or null values and defer to the command to set a default
+                    // value for optional parameters ...
+                    if (paramObj != null && isNotBlank(paramObj.toString())) {
+                        field.set(cmdObj, Float.valueOf(paramObj.toString()));
+                    }
+                    break;
+                case INTEGER:
+                    // Assuming that the parameters have been checked for required before
now,
+                    // we ignore blank or null values and defer to the command to set a default
+                    // value for optional parameters ...
+                    if (paramObj != null && isNotBlank(paramObj.toString())) {
+                        field.set(cmdObj, Integer.valueOf(paramObj.toString()));
+                    }
+                    break;
+                case LIST:
+                    final List listParam = new ArrayList();
+                    final StringTokenizer st = new StringTokenizer(paramObj.toString(), ",");
+                    while (st.hasMoreTokens()) {
+                        final String token = st.nextToken();
+                        final CommandType listType = annotation.collectionType();
+                        switch (listType) {
+                            case INTEGER:
+                                listParam.add(Integer.valueOf(token));
+                                break;
+                            case UUID:
+                                if (token.isEmpty())
+                                    break;
+                                final Long internalId = translateUuidToInternalId(token,
annotation);
+                                listParam.add(internalId);
+                                break;
+                            case LONG: {
+                                listParam.add(Long.valueOf(token));
+                            }
+                                break;
+                            case SHORT:
+                                listParam.add(Short.valueOf(token));
+                            case STRING:
+                                listParam.add(token);
+                                break;
+                        }
+                    }
+                    field.set(cmdObj, listParam);
+                    break;
+                case UUID:
+                    if (paramObj.toString().isEmpty())
+                        break;
+                    final Long internalId = translateUuidToInternalId(paramObj.toString(),
annotation);
+                    field.set(cmdObj, internalId);
+                    break;
+                case LONG:
+                    field.set(cmdObj, Long.valueOf(paramObj.toString()));
+                    break;
+                case SHORT:
+                    field.set(cmdObj, Short.valueOf(paramObj.toString()));
+                    break;
+                case STRING:
+                    if ((paramObj != null) && paramObj.toString().length() > annotation.length())
{
+                        s_logger.error("Value greater than max allowed length " + annotation.length()
+ " for param: " + field.getName());
+                        throw new InvalidParameterValueException("Value greater than max
allowed length " + annotation.length() + " for param: " + field.getName());
+                    }
+                    field.set(cmdObj, paramObj.toString());
+                    break;
+                case TZDATE:
+                    field.set(cmdObj, DateUtil.parseTZDateString(paramObj.toString()));
+                    break;
+                case MAP:
+                default:
+                    field.set(cmdObj, paramObj);
+                    break;
+            }
+        } catch (final IllegalAccessException ex) {
+            s_logger.error("Error initializing command " + cmdObj.getCommandName() + ", field
" + field.getName() + " is not accessible.");
+            throw new CloudRuntimeException("Internal error initializing parameters for command
" + cmdObj.getCommandName() + " [field " + field.getName() +
+                " is not accessible]");
+        }
+    }
+
+    private boolean isObjInNewDateFormat(final String string) {
+        final Matcher matcher = BaseCmd.newInputDateFormat.matcher(string);
+        return matcher.matches();
+    }
+
+    private Date messageDate(final Date date, final int hourOfDay, final int minute, final
int second) {
+        final Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
+        cal.set(Calendar.MINUTE, minute);
+        cal.set(Calendar.SECOND, second);
+        return cal.getTime();
+    }
+
+    private Long translateUuidToInternalId(final String uuid, final Parameter annotation)
{
+        if (uuid.equals("-1")) {
+            // FIXME: This is to handle a lot of hardcoded special cases where -1 is sent
+            // APITODO: Find and get rid of all hardcoded params in API Cmds and service
layer
+            return -1L;
+        }
+        Long internalId = null;
+        // If annotation's empty, the cmd existed before 3.x try conversion to long
+        final boolean isPre3x = annotation.since().isEmpty();
+        // Match against Java's UUID regex to check if input is uuid string
+        final boolean isUuid = uuid.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
+        // Enforce that it's uuid for newly added apis from version 3.x
+        if (!isPre3x && !isUuid)
+            return null;
+        // Allow both uuid and internal id for pre3x apis
+        if (isPre3x && !isUuid) {
+            try {
+                internalId = Long.parseLong(uuid);
+            } catch (final NumberFormatException e) {
+                internalId = null;
+            }
+            if (internalId != null)
+                return internalId;
+        }
+        // There may be multiple entities defined on the @EntityReference of a Response.class
+        // UUID CommandType would expect only one entityType, so use the first entityType
+        final Class<?>[] entities = annotation.entityType()[0].getAnnotation(EntityReference.class).value();
+        // Go through each entity which is an interface to a VO class and get a VO object
+        // Try to getId() for the object using reflection, break on first non-null value
+        for (final Class<?> entity : entities) {
+            // For backward compatibility, we search within removed entities and let service
layer deal
+            // with removed ones, return empty response or error
+            final Object objVO = _entityMgr.findByUuidIncludingRemoved(entity, uuid);
+            if (objVO == null) {
+                continue;
+            }
+            // Invoke the getId method, get the internal long ID
+            // If that fails hide exceptions as the uuid may not exist
+            try {
+                internalId = ((InternalIdentity)objVO).getId();
+            } catch (final IllegalArgumentException e) {
+            } catch (final NullPointerException e) {
+            }
+            // Return on first non-null Id for the uuid entity
+            if (internalId != null)
+                break;
+        }
+        if (internalId == null) {
+            if (s_logger.isDebugEnabled())
+                s_logger.debug("Object entity uuid = " + uuid + " does not exist in the database.");
+            throw new InvalidParameterValueException("Invalid parameter " + annotation.name()
+ " value=" + uuid +
+                " due to incorrect long value format, or entity does not exist or due to
incorrect parameter annotation for the field in api cmd class.");
+        }
+        return internalId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/447430c3/server/src/com/cloud/api/dispatch/ParamSemanticValidationWorker.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/dispatch/ParamSemanticValidationWorker.java b/server/src/com/cloud/api/dispatch/ParamSemanticValidationWorker.java
new file mode 100644
index 0000000..4733dcc
--- /dev/null
+++ b/server/src/com/cloud/api/dispatch/ParamSemanticValidationWorker.java
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.api.dispatch;
+
+import java.util.Map;
+
+import org.apache.cloudstack.api.BaseCmd;
+
+
+/**
+ * This worker validates parameters in a semantic way, that is of
+ * course specific for each {@link BaseCmd}, so actually it delegates
+ * the validation on the {@link BaseCmd} itself
+ *
+ * @author afornie
+ */
+public class ParamSemanticValidationWorker implements DispatchWorker {
+
+    @Override
+    public boolean handle(final BaseCmd cmd, final Map<String, Object> params) {
+        cmd.validateSpecificParameters(params);
+        return true;
+    }
+
+}


Mime
View raw message