{"action":"closed","issue":{"url":"https://api.github.com/repos/ServiceStack/ServiceStack.Java/issues/15","labels_url":"https://api.github.com/repos/ServiceStack/ServiceStack.Java/issues/15/labels{/name}","comments_url":"https://api.github.com/repos/ServiceStack/ServiceStack.Java/issues/15/comments","events_url":"https://api.github.com/repos/ServiceStack/ServiceStack.Java/issues/15/events","html_url":"https://github.com/ServiceStack/ServiceStack.Java/issues/15","id":126067558,"number":15,"title":"Please change the RetentionPolicy of the Route attribute to Runtime, and use the Path attribute in JsonServiceClient","user":{"login":"bramley-turner-stride","id":12552699,"avatar_url":"https://avatars.githubusercontent.com/u/12552699?v=3","gravatar_id":"","url":"https://api.github.com/users/bramley-turner-stride","html_url":"https://github.com/bramley-turner-stride","followers_url":"https://api.github.com/users/bramley-turner-stride/followers","following_url":"https://api.github.com/users/bramley-turner-stride/following{/other_user}","gists_url":"https://api.github.com/users/bramley-turner-stride/gists{/gist_id}","starred_url":"https://api.github.com/users/bramley-turner-stride/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bramley-turner-stride/subscriptions","organizations_url":"https://api.github.com/users/bramley-turner-stride/orgs","repos_url":"https://api.github.com/users/bramley-turner-stride/repos","events_url":"https://api.github.com/users/bramley-turner-stride/events{/privacy}","received_events_url":"https://api.github.com/users/bramley-turner-stride/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-01-12T00:09:00Z","updated_at":"2016-01-12T00:18:48Z","closed_at":"2016-01-12T00:18:48Z","body":"Please change the `RetentionPolicy` of the `Route` attribute to `RUNTIME`, like this:\r\n    @Retention(RetentionPolicy.RUNTIME)\r\n    public @interface Route \r\n\r\nThe problem with the current `/json/reply/<ClassName>` method of generating URLs is it is venerable to code being renamed on the server. In theory, renaming request classes in the API shouldn't matter, because the external definition of the API should be just the `Route` attribute defined in C#.\r\n\r\nI want to be able to use the following code to generate the URL myself, but I can't at the moment because the Route attribute is not retained at runtime. If you just set the RetentionPolicy, I can do this (extending `JsonServiceClient` ):\r\n\r\n    static String routeName(Object o){\r\n        Route routeAnnotation=o.getClass().getAnnotation(Route.class);\r\n        if(routeAnnotation!=null && routeAnnotation.Path().length()>0)){\r\n            if(routeAnnotation.Path().startsWith(\"/\"))\r\n                return routeAnnotation.Path().substring(1,routeAnnotation.Path().length());\r\n            return routeAnnotation.Path();\r\n        }        \r\n        return \"json/reply/\" + o.getClass().getSimpleName();\r\n    }\r\n\r\n    public String createUrl(Object requestDto, Map<String,String> query){\r\n        String route = routeName(requestDto);\r\n               \r\n        StringBuilder sb = new StringBuilder();\r\n        try {\r\n            for (Field f : Utils.getSerializableFields(requestDto.getClass())) {\r\n                Object val = f.get(requestDto);\r\n                String replaceFormat = String.format(\"{%s}\", f.getName());\r\n                if(route.contains(replaceFormat)){\r\n                    //fill in format strings in the route (eg /category/{taxon} )\r\n                    route=route.replace(replaceFormat, URLEncoder.encode(val==null?\"null\":val.toString(), \"UTF-8\"));\r\n                    continue;\r\n                }\r\n                if (val == null)\r\n                    continue;\r\n\r\n                sb.append(sb.length() == 0 ? \"?\" : \"&\");\r\n                sb.append(URLEncoder.encode(f.getName(), \"UTF-8\"));\r\n                sb.append(\"=\");\r\n                sb.append(URLEncoder.encode(val.toString(), \"UTF-8\"));\r\n            }\r\n\r\n            if (query != null) {\r\n                for (Map.Entry<String, String> entry : query.entrySet()) {\r\n                    String key = entry.getKey();\r\n                    String val = entry.getValue();\r\n\r\n                    sb.append(sb.length() == 0 ? \"?\" : \"&\");\r\n                    sb.append(URLEncoder.encode(key, \"UTF-8\"));\r\n                    sb.append(\"=\");\r\n                    if (val != null) {\r\n                        sb.append(URLEncoder.encode(val, \"UTF-8\"));\r\n                    }\r\n                }\r\n            }\r\n        } catch (IllegalAccessException | UnsupportedEncodingException e) {\r\n            throw new RuntimeException(e);\r\n        }\r\n        \r\n        String requestUrl = this.baseUrl + route; \r\n        return requestUrl + sb.toString();\r\n    }"}}