loic911 / Rest-api-doc

MIT License
21 stars 32 forks source link

File not found exception restapidoc.json when app is deployed as war #12

Open sunilinteti opened 9 years ago

sunilinteti commented 9 years ago

Hi, I have deployed the grails application war. I have put the restapidoc.json file in the exploded war root directory. When I try to hit the url for the documentation it gives filenotfound error. It works fine locally when I start grails from grails run-app. Could you please point out if I am missing some thing. Thank you.

URL : http://dev.mathmagicians.dk:8080/mo/restApiDoc/?doc_url=http://dev.mathmagicians.dk:8080/mo/restApiDoc/api

{"status":500,"message":"restapidoc.json (The system cannot find the file specified)","trace":"java.io.FileNotFoundException: restapidoc.json (The system cannot find the file specified)\r\n\tat java.io.FileInputStream.(FileInputStream.java:146)\r\n\tat org.restapidoc.RestApiDocController.api(RestApiDocController.groovy:20)\r\n\tat grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)\r\n\tat grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)\r\n\tat com.odobo.grails.plugin.springsecurity.rest.RestTokenValidationFilter.processFilterChain(RestTokenValidationFilter.groovy:88)\r\n\tat com.odobo.grails.plugin.springsecurity.rest.RestTokenValidationFilter.doFilter(RestTokenValidationFilter.groovy:66)\r\n\tat grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter.doFilter(GrailsAnonymousAuthenticationFilter.java:53)\r\n\tat dk.mathmagicians.mo.springsecurity.rest.CustomRestAuthenticationFilter.doFilter(CustomRestAuthenticationFilter.groovy:110)\r\n\tat grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter.doFilter(RequestHolderAuthenticationFilter.java:49)\r\n\tat grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter.doFilter(MutableLogoutFilter.java:82)\r\n\tat dk.mathmagicians.mo.springsecurity.rest.CustomRestLogoutFilter.doFilter(CustomRestLogoutFilter.groovy:73)\r\n\tat com.brandseye.cors.CorsFilter.doFilter(CorsFilter.java:82)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n"}

loic911 commented 9 years ago

Hi, The json file must be in your app root directory. It depends on your config/container (for my tomcat, its: /var/lib/tomcat7/restapidoc.json)

You should try:

  1. add this line in the init method from bootstrap.groovy println "resapidoc.json must be located in "+new File( 'restapidoc.json' ).absolutePath
  2. check your log file/console for this println and check if the restapidoc file is there

I will try to simplify this step in the next release ;-)

mgeis commented 9 years ago

The problem here is that the plugin expects to find it in the root of the app server, but the file is actually at ${root}/mo/restapidoc.json . The generated HTML will also look for javascript off the root of the app server, and will not look relative to the deployment context of the application (in this case, "mo").

This looks like a bug. If you repackage your war file to deploy as the root application (grails.app.context="/"), everything will work.

dbk138 commented 9 years ago

I have found that the configuration property 'grails.plugins.restapidoc.outputFile' controls both the location of the document when it gets generated through 'grails rest-api-doc' and the location used by the server to look for the file when deployed as a war file. This becomes an issue (at least for me) when you develop and deploy on separate systems. For example, if I have my property set to 'grails.plugins.restapidoc.outputFile="/home/user/app/restapidoc.json"' then it will generate that file in that location when I execute 'grails rest-api-doc'. However, when I go to deploy the app as a war file, the server will look for that location based on the property in the Config.groovy file and it will return the 500 error if it does not exist or cannot access it.

Based on this, would there be a way to seperate this behavior into two different properties? One to specify the output file when 'grails rest-api-doc' is executed and one for the location of the file on the server?

P.S. I have also tried the above suggestions, neither of which had worked. I am using Fedora 20 and deploying on RedHat 6 using WebSphere.

loic911 commented 9 years ago

dbk138: this will be done in 0.4 (realease soon).

String outputFileGeneration: The path to the file where to store the JSON after generation String outputFileReading: The path to the JSON file to read when looking for the report

You can have different value with different env (dev, prod, test, ...)

raffian commented 9 years ago

@dbk138 Had the same problem, and I agree, we need another property.

For local development, and server deployment using exploded WAR, outputFileGeneration and outputFileReading work fine because the json file is readable using absolute paths and new File({outputFileReading}). When deploying as packaged WAR, this doesn't work, so I introduced a new property:

outputFileClasspathReading

If property is set, json file is read from the WAR's classpath (if not set, defaults to outputFileReading and new File() as before)

The war classpath is WEB-INF/classes, so I added a build event that triggers on grails war, adding the json file to the WAR:

//_Events.groovy
eventCreateWarStart = { warName, stagingDir ->
   def warClasspath = new File('${stagingDir}/WEB-INF/classes')
   ant.copy(todir: warClasspath ) {
      fileset(file:'restapidoc.json')
   }
}

I updated RestApiDocController to check if outputFileClasspathReading is enabled, if so, it uses getResourceAsStream() to access the file, if not, defaults to new File(), as before.

I'm trying to do my first pull request with these changes, but does that make sense? I like this approach as it's flexible for local development and server deployment.

ferasodh commented 9 years ago

I had the same problem guys, but I can't make it work neither in dev nor in prod modes. I printed 'restapidoc.json' file path and it was as expected. Here is my config:

grails.plugins.restapidoc.outputFileGeneration = "restapidoc.json"
grails.plugins.restapidoc.outputFileReading = "restapidoc.json"
grails.plugins.restapidoc.basePath = "http://localhost:8080/osp"

grails.plugins.restapidoc.defaultObjectFields = [
[name:"id", type:"Long"]
]
grails.plugins.restapidoc.defaultErrorAll = [
"400": "Bad Request: missing parameters or bad message format",
"401": "Unauthorized: must be auth",
"403": "Forbidden: role error",
"404": "Object not found"
]
grails.plugins.restapidoc.defaultErrorGet = [
"400": "Bad Request: missing parameters or bad message format",
"401": "Unauthorized: must be auth",
"403": "Forbidden: role error",
"404": "Object not found"
]
grails.plugins.restapidoc.defaultErrorPost = [
"409": "Object already exist"
]
grails.plugins.restapidoc.defaultErrorPut = [
"409": "Object already exist"
]

and this is my restapidoc.json file:

{
   "basePath": "http://localhost:8080/osp",
   "apis": [{
      "jsondocId": "af2bdcb8-03ea-4cf2-90cf-ff7020b31e50",
      "methods": [{
         "headers": [],
         "pathparameters": [{
            "jsondocId": "c4098614-a661-4d88-9f74-e17509bb9717",
            "name": "id",
            "format": "",
            "description": "Location id",
            "type": "long",
            "required": "true",
            "allowedvalues": []
         }],
         "queryparameters": [],
         "verb": "GET",
         "description": "Gets a Location",
         "methodName": "show",
         "jsondocId": "01a5724b-6ffe-45cb-9d78-411a359c6a75",
         "bodyobject": null,
         "apierrors": [
            {
               "jsondocId": "000cd55e-07a7-4dee-989b-eab1e00430f1",
               "code": "400",
               "description": "Bad Request: missing parameters or bad message format"
            },
            {
               "jsondocId": "da98e143-90d2-45a8-a344-79ad21792695",
               "code": "401",
               "description": "Unauthorized: must be auth"
            },
            {
               "jsondocId": "118d5820-a96a-4530-a8a7-4c490be3d3c4",
               "code": "403",
               "description": "Forbidden: role error"
            },
            {
               "jsondocId": "65e41c2a-e223-48f8-ae00-e4309fa8c21b",
               "code": "404",
               "description": "Object not found"
            }
         ],
         "path": "/api/coupons/{CouponId}locations/{id}",
         "response": {
            "jsondocId": "f378f3a9-5d7e-45d8-9011-18ae1934551d",
            "mapValueObject": "",
            "mapKeyObject": "",
            "object": "location"
         },
         "produces": ["application/json"],
         "consumes": []
      }],
      "name": "Location services",
      "description": "Methods for managing Locations"
   }],
   "objects": [],
   "version": "0.1.1"
}

Can you please help me solve this?

loic911 commented 9 years ago

Could you locate the restapidoc.json file on your server (absolute path)?

With grails.plugins.restapidoc.outputFileReading = "restapidoc.json", the server will look on the "current" directory for this file. It should be great if you can print the absolute path of the expected file: In RestApiDocController.groovy, add the line:

println docFile.absolutePath

between these 2 lines:

File docFile = new File(grailsApplication.mergedConfig.grails.plugins.restapidoc.outputFileReading)
render(docFile.text)
TonyVeigel commented 9 years ago

@raffian That is a needed feature. You should issue that pull request.

stacysimpson commented 9 years ago

As opposed to introducing another setting, an alternative approach is to use the servletContext. This approach will work for both development and production.

Example settings: grails.plugins.restapidoc.outputFileGeneration = "web-app/WEB-INF/restapidoc.json" grails.plugins.restapidoc.outputFileReading = "/WEB-INF/restapidoc.json"

Here is the pull request: https://github.com/loic911/Rest-api-doc/pull/52

raffian commented 9 years ago

Nice, how’s that working out for you in both local and prod evironments?

From: Stacy Simpson [mailto:notifications@github.com] Sent: Tuesday, May 19, 2015 8:11 PM To: loic911/Rest-api-doc Cc: Raffi Basmajian Subject: Re: [Rest-api-doc] File not found exception restapidoc.json when app is deployed as war (#12)

As opposed to introducing another setting, an alternative approach is to use the servletContext. This approach will work for both development and production.

Example settings: grails.plugins.restapidoc.outputFileGeneration = "web-app/WEB-INF/restapidoc.json" grails.plugins.restapidoc.outputFileReading = "/WEB-INF/restapidoc.json"

Here is the pull request: #52 https://github.com/loic911/Rest-api-doc/pull/52

— Reply to this email directly or view it on GitHub https://github.com/loic911/Rest-api-doc/issues/12#issuecomment-103702421 . https://github.com/notifications/beacon/AENS2AlJZmt-HbQZBZPRX8MektqmEMC2ks5oK8iLgaJpZM4CTGp2.gif

stacysimpson commented 9 years ago

Works fine. I tested on both Linux (development) and Windows (production).