Wednesday 19 August 2015

Symfony 2 Production Environment Issue - 405

[Note: This post is my personal experience with Symfony 2 and some strange issues I faced in production environment. Error messages were misleading to some extent. I was stuck with "405 Method Not Allowed" and  "POST request was becoming GET request"  whenever I was trying POST request but GET request was working without any hassle and it was happening only in prod env not in dev]

Here we will be talking about Symfony 2.6 (I believe it applies to other versions as well). Before proceeding, let me shed some light on what exactly happened with me and how did I solve it. I developed some API on my development machine (personal macbook pro) and everything worked perfectly fine. Taking "localhost" example and project "myTestAPI"; So when I was testing in dev environment, my URL was

http://localhost/myTestAPI/web/app_dev.php/myTestRoute
params sent : param1=value1

Above request is POST request and it worked perfectly fine with dev environment. Lets try same in prod environment on localhost itself (I hope you are familiar with environment in symfony see here)

In prod environment, api address became :

http://localhost/myTestAPI/web/myTestRoute
params sent : param1=value1

At first time, it did not work and famous issue was cache clear. I had to run clear cache command in prod environment.

php app/console cache:clear -env=prod

Here we go, it worked like charm. Now it is time to take our awesome API to server, we chose AWS ec2 free service to test our API. Simply installed LAMP stack there and making some changes in httpd.conf as asked for Symfony:


AllowOverride All (in www folder) and we are almost ready here.

Assuming host becomes "ec2-87-52-XX-YY-aws.amazon.com"

So, I tried a GET request after initial set up 

http://ec2-87-52-XX-YY-aws.amazon.com/myTestAPI/web/app_dev.php/myTestRoute  in dev environment and

http://ec2-87-52-XX-YY-aws.amazon.com/myTestAPI/web/app.php/myTestRoute  in prod environment and believe me it worked in first try. I drank a coffee of satisfaction.

But things are not that simple for programmers,  

I tried implementing my first POST api and boom, to my surprise it threw an error with http code "405 - Method not allowed" and I was wondering what exactly happened. On my localhost same code is running perfectly fine but here on ec2 server it was not and here our actual story starts :

First thing came in my mind was routing, so i cross checked routing and no doubt it was fine. I was not sure what to google even, tried few terms

"Symfony -  405 error"  (No luck here)
"Symfony - POST method becomes GET"  (even i was ashamed searching this but no luck here as well)
and few more and finally could not find any solution of this issue.


So this was time to dig down into logs and code myself. Started with Symfony logs and it says:

"request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException: "No route found for "GET /api/v1/user/test": Method Not Allowed (Allow: POST)" at /var/www/html/testAPI/app/cache/prod/classes.php line 2834 {"exception":"[object] (Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException(code: 0): No route found for \"GET /api/v1/user/test\": Method Not Allowed (Allow: POST) at /var/www/html/testAPI/app/cache/prod/classes.php:2834, Symfony\\Component\\Routing\\Exception\\MethodNotAllowedException(code: 0):  at /var/www/html/testAPI/app/cache/prod/appProdUrlMatcher.php:330)"} []"

In short, Symfony was doing its work perfectly saying "you are requesting this api with method GET but routing says POST, so I can not allow you to go further."

But the question arises, we are using POST request then how on earth it is becoming GET. So I thought to check Apache logs and not to my surprise it did same what I was expecting: REDIRECT.

I could see in apache logs that whenever I am making any request in prod environment, it is redirecting it. Point to note here is that it was not about POST or GET but because of 301 redirect and "PRG" (Post Redirect Get) my next request was becoming GET automatically and hence Symfony routing was failed. 

What was causing it : Actually I was using app_dev.php in my path in dev environment and app.php in path for prod environment. There is some rewrite rule in Symfony which redirects to same path without app.php.

SO if you make any request to 

http://{host}/{application}/web/app.php/{route}

Symfony will redirect it to 

http://{host}/{application}/web/{route}

and thus request was transforming from POST to GET. So in essence, do not use app.php in path when you intent to use prod environment.

At last, I believe instead of choosing 301 for this purpose, Symfony must have chosen 307. Feel free to correct if anything you believe is misleading here,

"happy coding"







Tuesday 11 August 2015

Activity Life Cycle - Twist


To understand this blog basic necessity is knowledge of Activity Life Cycle. Does not it sound weird!!! We are here to learn lifecycle and basic necessity is lifecycle itself. Yes it is, but you will understand in no time :) 

Lets put it this way, this blog puts light on Activity lifecycle in abnormal circumstances. Wait Wait!!! What exactly we mean by this. Don't worry you will understand soon.

Everyone who is working in Android knows this basic diagram , make sure you understand it before you move further :



This is Activity lifecycle in happy environment; when things are in your favour and your requirement is served with normal flags.

But software is not that easy, we get strange requirements all the time. In this post we will be specifically talking about these abnormal situations :

a) Android is running low on memory and it kills your activity
b) You are using FLAG CLEAR TOP while starting Activity OR singleTop in Manifest
c) You are dealing with point b and point a occurs (booooom!!!)

Most of you must be familiar with case a where Android is running low on memory and to make sure device performs well it keeps on clearing memory as soon as possible. So how does it impacts us as developer ?
Documentation of this particular case is pretty clear on the web. So not wasting much time of yours, Android provides you two more lifecycle methods in such scenarios.

i) onSaveInstanceState
ii) onRestoreInstanceState

As name suggests, these methods are simply plugged into your normal lifecycle and system gives you chance to preserve user state for best UX. It is like saying, "Hey dude, I need your space for time being; Could you please pick your belongings for now and when I return you, you can re arrange your items as they were before"

This task is done in onSaveInstanceState method which is called after onPause when Activity is going to destroy which is followed by expected onStop method. We have to fill data (which we want to preserve) in Bundle which is passed to us in parameters. 

Assuming Android is running out of memory and gonna kill your activity, So after onStop method System may Or may not call onDestroy method (However I have seen this method is being called all the time) and woossssss our activity is gone.

Now when user navigates back to our Activity, he expects to see the same state where he left. Nobody like filling those big forms Or even a small email id field if they have already done for you. In this case when system recreates our Activity it simply goes through normal lifecycle which is shown above in diagram with two modifications :

i) It passes the bundle back to us in onCreate method; this is the bundle which we filled in onSaveInstanceState. So we have our data back.
ii) It calls another method named "onRestoreInstanceState" after inStart is called. It too contains same bundle for us to preserve state back for user.


onCreate
onStart
onRestoreInstanceState
OnResume



And at the end of day, we have happy user :)



 - FLAG CLEAR TOP while starting Activity OR singleTop in Manifest -

There are two possibilities, Activity does not exist in task OR it does. If it does not exist, this is similar to starting a new activity with standard flag. Same lifecycle will be called as shown in above diagram.

If Activity instance is already there in task stack (Suppose it was started with Intent i1), in this case Android will simple call method "onNewIntent" followed by "onRestart" and then "onStart" -> "onResume". And from now onwards, getIntent will return you the new Intent which was received recently to start your activity. It sounds simple and believe me it is.


onNewIntent  (with New Intent say i2)
onReStart (i2)
onStart  (i2)
onResume  (i2)


- FLAG CLEAR TOP while starting Activity OR singleTop in Manifest  and Android goes low on memory-


This is the case which was driving me crazy and led me to write this post. Have patience and read, suppose you started an activity A with intent I1 and you navigate to other activity. Assume case a occurs and system is low on  memory and it destroys your activity with normal lifecycle as explained above.

Now if you start another intent (i2) for A1 with either FLAG CLEAR TOP or A1 is singletop; Now as we know our A1 logically exists in backstack but it has been destroyed. So to handle this case, system changes lifecycle a bit:

onCreate (with Intent i1)  - weird
onStart (with Intent i1)  - weird
onRestoreInstanceState   (with Intent i1) - weird
onNewIntent (with Intent i2) and from here onwards, we can show new data as per intent
onResume (with i2)


But the story does not end here, even it goes more complex sometimes. Now we have Activity A1 with Intent i2. Suppose user presses home button and activity goes in background with normal lifecycle.
Assume System is low on memory now, so Android destroys your activity with proper callbacks in onSaveInstanceState (Remember we are dealing with i2).

Now when user navigates back to activity and system re creates activity, it starts Activity with normal lifecycle methods

onCreate  (intent i1)
onStart (intent i1)
onRestoreInstanceState (intent i1)
OnResume (intent i1)

But the strange part here is it calls every thing with Intent i1 (This is intent with which Activity was originally created) But user is expecting to open it with Intent i2 data. 

So in this case, you can utilise onSaveInstance and onRestoreInstanceState to properly fill your data and take actions accordingly.

[NOTE : All this was tested by turning on "Don't keep Activities on" flag in developer options, feel free to correct anything which you believe is misleading]

"happy coding"