Getting timezone information from request in Spring 4.0 - spring-mvc

There is a new feature of getting timezone information in web request object. See 16.9.1 section in http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/mvc.html#mvc-timezone
But I'm unable to resolve that how can we get the timezone information from request or which method should I use to get the timezone information?
After looking & following the source code at https://github.com/spring-projects/spring-framework/blob/v4.0.7.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java#L242, I tried to print it manually
println WebUtils.getSessionAttribute(request, SessionLocaleResolver.class.getName() + ".TIME_ZONE")
Any help?

Simply add a method argument of the type TimeZone to obtain it.
#RequestMapping
public String foo(TimeZone timezone) { ... }
That should do it.
If you really want to do it yourself use the RequestContextUtils.getTimeZone(request) method.
#RequestMapping
public String foo(HttpServletRequest request) {
TimeZone timezone = RequestContextUtils.getTimeZone(request);
...
}

I looked into this a bit and one problem is that there isn't a default TimeZone set, which seems like an oversight; RequestContextUtils.getTimeZone(request) should return the server's time zone if nothing else is available.
That's easy enough to fix; add a dependency injection in BootStrap.groovy for the "localeResolver" bean and some code to set the default time zone in the resolver:
class BootStrap {
def localeResolver
def init = {
if (!localeResolver.defaultTimeZone) {
localeResolver.defaultTimeZone = TimeZone.getDefault()
// or hard-code to a known instance, e.g. TimeZone.getTimeZone('America/New_York')
}
}
}
There's not much documented about working with TimeZones in the Spring reference docs; there's actually a lot more information in the comments of the original feature request from 2005.
Grails overrides the default LocaleResolver implementation of AcceptHeaderLocaleResolver (which has no support for TimeZone) and configures a SessionLocaleResolver instead. This looks for a locale first in the session, then for a default value set on the resolver (by default null - it's not initialized by Grails), and finally it calls request.getLocale(), which checks the Accept-Language header. This is commonly set, as having localized text is a good thing. Additionally a LocaleChangeInterceptor is configured, which looks for a "lang" param to suppport changing the Locale. But there are no conventions for passing time zone information from the client.
The time zone id seems to me like something that you would ask for during user registration and store in the database with other profile information. Then when they authenticate, you can use the stored value if it exists and set it in the session where the LocaleResolver will find it, e.g.
import org.springframework.web.servlet.i18n.SessionLocaleResolver
...
session[SessionLocaleResolver.TIME_ZONE_SESSION_ATTRIBUTE_NAME] =
TimeZone.getTimeZone(user.timeZoneId)
It is possible to get the user's local time and TimeZone in JavaScript and send it to the server via Ajax. For some examples see the links in this answer.

For Spring Boot 2.x.x
#RequestMapping
public String foo(TimeZone timezone) { ... }
It works Perfectly.

Related

BreezeJS modified route not working

My application has two databases with exactly the same schema. Basically, I need to change the DbContext based on what data I'm accessing. Two countries are in one Db and 4 countries in the other. I want the client to decide which context is being used. I tried changing my BreezeWebApiConfig file so that the route looks like this:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{dbName}/{controller}/{action}/{id}",
defaults: new {id=RouteParameter.Optional,dbName="db1"}
);
I added the string to the controller actions:
[HttpGet]
public string Metadata(string dbName="")
{
return _contextProvider.Metadata();
}
And changed the entityManager service Name.
Now when the client spins up, it accesses the corrent metadata action and I get a message:
Error: Metadata query failed for: /breeze/clienthistory/kenya/Metadata. Unable to either parse or import metadata: Type .... already exists in this MetadataStore
When I go to the metadata url from the browser, I get the correct metadata (exactly the same as when I remove the {dbName} segment from the route). If I remove the {dbName} segment from the route I get no error and everything works fine
(I have not started implementing the multiple contexts yet -- I am just trying to make the additional segment work).
Thanks.
I think the problem is that your Breeze client is issuing two separate requests for the same metadata, once under each of the two "serviceNames". Breeze tries to blend them both into the same EntityManager.metadataStore ... and can't because that would mean duplication of EntityType names.
One approach that should work is to begin your application by fetching the metadata immediately upon app start and then adding all the associated "DataServiceNames" to the MetadataStore.
Try something along these lines (pseudo-code):
var manager;
var store = new breeze.MetadataStore();
return store.fetchMetadata(serviceName1)
.then(gotMetadata)
.catch(handleFail);
function gotMetadata() {
// register the existing metadata with each of the other service names
store.addDataService(new breeze.DataService(serviceName2));
... more services as needed ...
manager = new breeze.EntityManager({
dataService: store.getDataService(serviceName1), // service to start
metadataStore: store
});
return true; // return something
}
Alternative
Other approaches to consider don't involve 'db' placeholder in the base URL nor any toying with the Web API routes. Let's assume you stay vanilla in that respect with your basic service name
var serviceName = '/breeze/clienthistory/';
..
For example, you could add an optional parameter to your routes (let's call it db) as needed via a withParameters clause.
Here is a query:
return new breeze.EntityQuery.from('Clients')
.where(...)
.withParameters({db: database1}); // database1 == 'kenya'
.using(manager).execute()
.then(success).catch(failed);
which produces a URL like:
/breeze/clienthistory/Clients/?$filter=...&db=kenya
It makes an implicit first-time-only metadata request that resolves to:
/breeze/clienthistory/Metadata
Your server-side Web API query methods can expect db as an optional parameter:
[HttpGet]
public string Metadata(string db="")
{
... do what is right ...
}
Saves?
I assume that you also want to identify the target database when you save. There are lots of ways you can include that in the save request
in a custom HTTP header via a custom AJAX adapter (you could do this for queries too)
in a query string parameter or hash segment on the saveChanges POST request URL (again via a custom AJAX adapter).
in the tag property of the saveOptions object (easily accessed by the server implementation of SaveChanges)
in the resourceName property of the saveOptions object (see "named save")
You'll want to explore this variety of options on your own to find the best choice for you.

Automatic Localization in symfony2

Please look at the following code:
public function __construct($error_code)
{
$translator = new Translator('en');
$translator->addLoader('yaml', new YamlFileLoader());
$translator->addResource('yaml', dirname(__DIR__).'/Resources/translations/messages.en.yml', 'en');
$this->setErrorCode($translator->trans($error_code));
}
I am new to symfony. I have created a class MyProjectExceptions which extends Exception. Now when I have to throw a custom exception I call this class where I get the $error_code. Now this $error_code is a constant of another class which has its locale in MyBundle/Resources/transalations/messages.en.yml which will be used to throw as exception message.
Now my question are following:
How can I avoid addResource, so it can automatically add it based on Locale and find the string?
How to access serviceContainer in this class so that I can access session to set and get locales OR other services.
Can we set the default Loader as well.
In above code I am creating an instance of Translator class and manually passing 'en'. but it should pick default locale or user set locale.
I tried many solutions but not able to get the desired results.
Any help would be appreciated. Thanks in advance.
You need to register your class as Symfony service. Read the documentation: http://symfony.com/doc/current/book/service_container.html#creating-configuring-services-in-the-container
After that you can inject other services (like Translation) in your constructor. It will use all parameters that you have already set.
If you inject translator service it will pick the parameters that you have already set. For example, if you defined parameters for translator (including default locale) at config.yml, then you overrode this locale with parameter in route, you will get translator service set up with this locale. And it will automatically use resources that are lied in appropriate directories.

Extending Doctrine DBAL DateTimeType in Symfony2 while parent __construct is final

I need to write DateTime to database in UTC and convert it to users timezone upon retrieval. Every user has his timezone set in database.
Solution: Extend the Doctrine\DBAL\Types\DateTimeType as described here so it would always persist in UTC and when needed convert it back to user timezone in the presentation layer via Twig.
Problem: The suggested method converts UTC from the system default timezone. To make it work I need to read the actual user timezone in the extended DateTimeType and make conversions with that. So... I do the usual dependency injection and inject SecurityContext as it holds the User Entity together with the timezone value:
//TreasureForge\CoreBundle\DoctrineExtensions\DBAL\Types
private function __construct(SecurityContext $security)
{
$this->tz = $security->getToken()->getUser()->getTimezone();
}
and
treasure_forge.core_bundle.utc_datetime_extension:
class: TreasureForge\CoreBundle\DoctrineExtensions\DBAL\Types\UTCDateTimeType
arguments: ["#security.context"]
But it throws: Compile Error: Cannot override final method Doctrine\DBAL\Types\Type::__construct()
I've even resorted to the bad, bad methods like:
global $kernel;
$tz = $kernel->getContainer()->get('security.context')->getToken()->getUser()->getTimezone();
This is wrong in every imaginable way but even then for whatever reason getToken() is sometimes set and sometimes not throughout the request so I have serious doubts in the reliability of this.
Any ideas?
Did you try doing that in the Entity?
In your getDate() ( or whatever your getter is called ) you should get the user timezone. and again in the setter ( setDate() ) you should set whatever time you have to UTC.
Please write your Entity code to be able to provide an example.

Saving a toISODate gets deserialized as a non-iso string

I follow the usual recommendations of serializing javascript Date objects to be sent to the server by using the toISODate js function.
In fact Breeze.js is doing this for me which is great. Everything works as expected when the property on my Entity is a DateTime - the date gets saved to the database as a UTC (0 offset date) datetime.
I hit a problem when the property on the Entity is of type string. A date that is sent over the wire as '2013-06-08T23:00:00Z' is being deserialized into the string property on the Entity as '06/08/2013 23:00:00' and this is the same value that is saved into the varchar backing column in the database.
So the date is being deserialized into a 'en-US' formatted date (MM/dd/yyyy HH:mm:ss). I'm stuck as to why this is happening or how to change things so that the string remains intact as it's deserialized into a string property.
A few technical notes:
I confirmed the deserialized value in the property by wiring up a BeforeSaveEntitiesDelegate to the EFContextProvider and inspected the Entity instance in the debugger just before it was saved
when inspecting the entity in the BeforeSaveEntitiesDelegate method on the server, I noted that the Thread.CurrentThread.CurrentCulture and CurrentUICulture were both 'en-GB'
for technical reasons I need to use a string property rather than a DateTime (or DateTimeOffset) - basically the property could receive any type of data so string is the universal format that will fit everything.
Help would be most welcome!
Thanks
Christian Crowhurst
For a .NET server, Breeze uses JSON.net to serialize/deserialize json. Breeze allows you to configure this by automatically detecting any 'custom' implementations of the 'BreezeConfig' class.
This means that you can customize Breeze's use of JSON.Net's serialization behavior by implementing a subclass of BreezeConfig. This might look something like this within your server project.
using Breeze.ContextProvider;
using Breeze.WebApi2;
using Newtonsoft.Json;
namespace Sample_WebApi2 {
public class CustomBreezeConfig : BreezeConfig {
/// <summary>
/// Override to use a specialized JsonSerializer implementation.
/// </summary>
protected override JsonSerializerSettings CreateJsonSerializerSettings() {
// get the breeze default settings.
var baseSettings = base.CreateJsonSerializerSettings();
// Not sure if this is the setting you want but...
baseSettings.DateParseHandling = DateParseHandling.None;
return baseSettings;
}
}

Is it possible to make a webservice per user?

I have a webservice (.asmx) and I want it to retain a value on a per-user basis. Is this possible?
ie (pseudo-code)
MyWebservice
{
object perUserVariable = something;
[WebMethod]
public void myMethod()
{
if (something == null)
{
something = doBigExpensivedatabaseCall();
}
return something;
}
}
You can use ASP.NET's session mechanism.
Change your WebMethod attribute, so that it will look like that:
[WebMethod(EnableSession = true)]
This is normally achieved by cookies, or by sending the session id in the query string (both ways are completely handled by ASP.NET). The former is the default, to achieve the latter, just set cookieless="true" in your config.web file.
Yes, you would need to pass in some kind of user identifier to specify who the user is, do your operation and instead of storing it in something you will to use a durable or semi-durable store such as Cache or Session. Then look that value up from the Cache or Session instead of a local member.
Also fwiw the way you have that configured the fact something isn't static means it would be null on every single request because it would be newly initialized. Making it static however would then server the individual instance of something to each and every request there after.
This is why you need to use a store that can differentiate on the user such as Cache[userid+"something"] or the Session["something"] instead.
Your choices seem to be using some type of caching system like session or memcache.
Session will require a session id passed as a cookie to the requests. Other caching providers could probably key off of a post value like the userid.

Resources