My Flex app uses local SharedObjects. There have been incidents of the Flash cookie getting corrupt, for example, due to a plugin crash. In this case SharedObjects.getLocal will throw an exception (#2006).
My client wants the app to recover gracefully: if the cookie is corrupt, I should replace it with an empty one.
The problem is, if SharedObject.getLocal doesn't return an instance of SharedObject, I've nothing to call clear() on.
How can I delete or replace such a cookie?
Many thanks!
EDIT:
There isn't much code to show - I access the local cookie, and I can easily catch the exception. But how can I create a fresh shared object at the same location once I caught the exception?
try {
localStorage = SharedObject.getLocal("heywoodsApp");
} catch (err:Error) {
// what do I do here?
}
The error is easily reproduced by damaging the binary content of a Flash cookie with an editor.
I'm not really sure why you'd be getting a range error - esp if you report that can find it. My only guess for something like this is there is a possibility of crossing boundries with respect to the cross-domain policy. Assuming IT has control over where the server is hosted, if the sub-domain ever changed or even access type (from standard to https) this can cause issues especially if the application is ongoing (having been through several releases). I would find it rather hard to believe that you are trying to retrieve a named SO that has already been named by another application - essentially a name collision. In this regard many of us still uses the reverse-dns style naming convention even on these things.
If you can catch the error it should be relatively trivial to recover from: - just declare the variable outside the scope of the try so it's accessible to catch as well. [edit]: Since it's a static method, you may need to create a postfix to essentially start over with a new identifier.
var mySO:SharedObject;
....
catch(e:Error)
{
mySO = SharedObject.getLocal('my.reversedns.so_name_temp_name');
//might want to dispatch an error event or rethrow a specific exception
//to alert the user their "preferences" were reset.
}
You need to be testing for the length of SharedObject and recreate if it's 0. Also, always use flush to write to the object. Here's a function we use to count the number of times our software is launched:
private function usageNumber():void {
usage = SharedObject.getLocal("usage");
if (usage.size > 0) {
var usageStr:String = usage.data.usage;
var usageNum:Number = parseInt(usageStr);
usageNum = usageNum + 1;
usageStr = usageNum.toString();
usage.data.usage = usageStr;
usage.flush();
countService.send();
} else {
usage.data.usage = "1";
usage.flush();
countService.send();
}
}
It's important to note that if the object isn't available it will automatically be recreated. That's the confusing part about SharedObjects.
All we're doing is declaring the variable globally:
public var usage:SharedObject;
And then calling it in the init() function:
usage = SharedObject.getLocal("usage");
If it's not present, then it gets created.
Related
I use console.groupCollapsed() to hide functions I don't generally need to review, but may occasionally want to dig into. One downside of this is that if I use console.warn or console.error inside that collapsed group, I may not notice it or it may be very hard to find. So when I encounter an error, I would like to force the collapsed group open to make it easy to spot the warning/error.
Is there any way to use JS to force the current console group (or just all blindly) to open?
Some way to jump directly to warnings/errors in Chrome debugger? Filtering just to warnings/errors does not work, as they remain hidden inside collapsed groups.
Or perhaps some way to force Chrome debugger to open all groups at once? <alt/option>-clicking an object shows all levels inside it, but there does not appear to be a similar command to open all groups in the console. This would be a simple and probably ideal solution.
There is no way to do this currently, nor am I aware of any plans to introduce such functionality, mainly because I don't think enough developers are actively using the feature to enough of a degree to create demand for this.
You can achieve what you're trying to do, but you need to write your own logging library. First thing you'll need to do is override the console API. Here is an example of what I do:
const consoleInterceptorKeysStack: string[][] = [];
export function getCurrentlyInterceptedConsoleKeys () { return lastElement(consoleInterceptorKeysStack); }
export function interceptConsole (keys: string[] = ['trace', 'debug', 'log', 'info', 'warn', 'error']) {
consoleInterceptorKeysStack.push(keys);
const backup: any = {};
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
const _log = console[key];
backup[key] = _log;
console[key] = (...args: any[]) => {
const frame = getCurrentLogFrame();
if (isUndefined(frame)) return _log(...args);
frame.children.push({ type: 'console', key, args });
frame.hasLogs = true;
frame.expand = true;
_log(...args);
};
}
return function restoreConsole () {
consoleInterceptorKeysStack.pop();
for (const key in backup) {
console[key] = backup[key];
}
};
}
You'll notice a reference to a function getCurrentLogFrame(). Your logging framework will require the use of a global array that represents an execution stack. When you make a call, push details of the call onto the stack. When you leave the call, pop the stack. As you can see, when logging to the console, I'm not immediately writing the logs to the console. Instead, I'm storing them in the stack I'm maintaining. Elsewhere in the framework, when I enter and leave calls, I'm augmenting the existing stack frames with references to stack frames for child calls that were made before I pop the child frame from the stack.
By the time the entire execution stack finishes, I've captured a complete log of everything that was called, who called it, what the return value was (if any), and so on. And at that time, I can then pass the root stack frame to a function that prints the entire stack out to the console, now with the full benefit of hindsight on every call that was made, allowing me to decide what the logs should actually look like. If deeper in the stack there was (for example) a console.debug statement or an error thrown, I can choose to use console.group instead of console.groupCollapsed. If there was a return value, I could print that as a tail argument of the console.group statement. The possibilities are fairly extensive. Here's a screenshot of what my console logs look like:
Note that you will have to architect your application in a way that allows for logging to be deeply integrated into your code, otherwise your code will get very messy. I use a visitor pattern for this. I have a suite of standard interface types that do almost everything of significance in my system's architecture. Each interface method includes a visitor object, which has properties and methods for every interface type in use in my system. Rather than calling interface methods directly, I use the visitor to do it. I have a standard visitor implementation that simply forwards calls to interface methods directly (i.e. the visitor doesn't do anything much on its own), but I then have a subclassed visitor type that references my logging framework internally. For every call, it tells the logging framework that we're entering a new execution frame. It then calls the default visitor internally to make the actual call, and when the call returns, the visitor tells the logging framework to exit the current call (i.e. to pop the stack and finalize any references to child calls, etc.). By having different visitor types, it means you can use your slow, expensive, logging visitor in development, and your fast, forwarding-only, default visitor in production.
I have a problem that I can solve reasonably easy with classic imperative programming using state: I'm writing a co-browsing app that shares URL's between several nodes. The program has a module for communication that I call link and for browser handling that I call browser. Now when a URL arrives in link i use the browser module to tell the
actual web browser to start loading the URL.
The actual browser will trigger the navigation detection that the incoming URL has started to load, and hence will immediately be presented as a candidate for sending to the other side. That must be avoided, since it would create an infinite loop of link-following to the same URL, along the line of the following (very conceptualized) pseudo-code (it's Javascript, but please consider that a somewhat irrelevant implementation detail):
actualWebBrowser.urlListen.gotURL(function(url) {
// Browser delivered an URL
browser.process(url);
});
link.receivedAnURL(function(url) {
actualWebBrowser.loadURL(url); // will eventually trigger above listener
});
What I did first wast to store every incoming URL in browser and simply eat the URL immediately when it arrives, then remove it from a 'received' list in browser, along the lines of this:
browser.recents = {} // <--- mutable state
browser.recentsExpiry = 40000;
browser.doSend = function(url) {
now = (new Date).getTime();
link.sendURL(url); // <-- URL goes out on the network
// Side-effect, mutating module state, clumsy clean up mechanism :(
browser.recents[url] = now;
setTimeout(function() { delete browser.recents[url] }, browser.recentsExpiry);
return true;
}
browser.process = function(url) {
if(/* sanity checks on `url`*/) {
now = (new Date).getTime();
var duplicate = browser.recents[url];
if(! duplicate) return browser.doSend(url);
if((now - duplicate_t) > browser.recentsExpiry) {
return browser.doSend(url);
}
return false;
}
}
It works but I'm a bit disappointed by my solution because of my habitual use of mutable state in browser. Is there a "Better Way (tm)" using immutable data structures/functional programming or the like for a situation like this?
A more functional approach to handling long-lived state is to use it as a parameter to a recursive function, and have one execution of the function responsible for handling a single "action" of some kind, then calling itself again with the new state.
F#'s MailboxProcessor is one example of this kind of approach. However it does depend on having the processing happen on an independent thread which isn't the same as the event-driven style of your code.
As you identify, the setTimeout in your code complicates the state management. One way you could simplify this out is to instead have browser.process filter out any timed-out URLs before it does anything else. That would also eliminate the need for the extra timeout check on the specific URL it is processing.
Even if you can't eliminate mutable state from your code entirely, you should think carefully about the scope and lifetime of that state.
For example might you want multiple independent browsers? If so you should think about how the recents set can be encapsulated to just belong to a single browser, so that you don't get collisions. Even if you don't need multiple ones for your actual application, this might help testability.
There are various ways you might keep the state private to a specific browser, depending in part on what features the language has available. For example in a language with objects a natural way would be to make it a private member of a browser object.
I am currently stuck and cant find any answers anywhere!! So any help at all would be great!
Currently Im trying to create a sharedObject on a client and send a string from the client containing information based on phone hardware(e.g. accelerometer and geolocation) to ams. From here I want to be able to access information from the sharedObject on the server in the main.asc to use elsewhere!
This is where the problem is occurring I cant access the shared object sent by the client. I sent my shared object like this:
//It's a best practice to always check for a successful NetConnection
protected function onNetStatus(event:NetStatusEvent):void
{
switch(event.info.code)//Check for a successful NetConnection
{
case "NetConnection.Connect.Success"://If the netConnection is a success#
so = SharedObject.getRemote("Data", nc.uri, false);//
so.connect(nc);//connect the sharedObject to the srever
so.addEventListener(SyncEvent.SYNC, syncHandler);//The sync listener
publishCamera(); //Publish the video
case "NetStream.Publish.Start"://If the netStream is a success
//etc
}
}
//It's a best practice to always check for a successful NetConnection
protected function syncHandler(event:SyncEvent):void
{
so.setProperty("username", nameForData);
so.setProperty("age", 21);
so.setProperty("nationality", "irish");
trace("Local"+so.data.username);
Im just not sure how to access so from server side!! I know this works as I have tested it but if there are better ways to implement it I would be glad to get advice!Below is proof that it is hitting the server
Update
Still stuck so I am adding a bounty and updating where I am and how I have progressed!
I have the information hitting the server(sometimes its not constant I dont know why)
Example is sometimes I get this in the admin console most times i have no information in the properties tab
What I am trying to do is to get the file to save every time I flush() the shared object so I can use it elsewhere
My server code is straight from the adobe api this is how it looks in the main asc:
in my onAppStart() I added these lines at the end:
application.allowDebug = true;
application.clearOnAppStop = false;
var Shared = SharedObject.get("Data", true);
trace("Name: "+Shared.name);
trace("Username: "+Shared.getProperty("username"));
in my onAppStop() I added these lines at the end:
var Shared = SharedObject.get("Data", true);
Shared.clear();
The serverside code for shared objects is (almost) the same as the client side.
So using so.getProperty(propertyName) should do the job.
If not check the server side shared object reference from adobe. Maybe that helps.
Hope i didn't missunderstand your question.
I can't get NVelocity to initialize. I'm not trying to do anything complicated, so it's just fine if it initializes at the defaults, but it won't even do that.
This:
VelocityEngine velocity = new VelocityEngine();
ExtendedProperties props = new ExtendedProperties();
velocity.Init(props);
Results in: "It appears that no class was specified as the ResourceManager..."
So does this:
VelocityEngine velocity = new VelocityEngine();
velocity.Init();
I can find precious little documentation on what the properties should be, nor how to get it to initialize with the simple defaults. Can anyone point to a resource?
A lot of pages point back to this page:
http://www.castleproject.org/others/nvelocity/usingit.html
But this page skips over the (seemingly) most important point -- how to set the properties and what to set them to.
I just want to load a simple template from a file.
Here's what I found out --
I was using the original NVelocity library, which hasn't had an update since 2003. I think it's a dead project.
I switched to the Castle Project version, and it's much easier -- in fact, it runs much like the examples on the page I linked to. It seems to set intelligent defaults for properties. I can initialize it without any properties set, but the template directory defaults to ".", so I generally set that one (do it before running "init").
To get the correct DLL, you need to download the latest NVelocity release (as of this writing it's 1.1).
Castle Project Download Page
You need to include the following files in your assembly, and make sure that their type is set to "Resource"
src\Runtime\Defaults\directive.properties
src\Runtime\Defaults\nvelocity.properties
These will then be found by ResourceLocator
src\Runtime\Resource\Loader\ResourceLocator.cs
If you get an exception on GetManifestResourceNames() as I did when trying to run Dvsl, then try modifying the ResourceLocator constructor to catch and ignore the error since the required files are in your local assembly (if you included them above) and the exception is only thrown by external assemblies (no idea why).
foreach(Assembly a in assemblies) {
String prefix = a.FullName.Substring(0,a.FullName.IndexOf(",")).ToLower();
try
{
String[] names = a.GetManifestResourceNames();
foreach (String s in names)
{
if (s.ToLower().Equals(fn) || s.ToLower().Equals(prefix + "." + fn))
{
this.filename = s;
assembly = a;
isResource = true;
}
}
} catch {
}
}
I've been struggling with this problem for last few hours but still got no idea what's wrong. Here's the scenario:
Application built on top of the Mate framework sometimes need to exchange data with remote server over plain binary socket.
When specific packet is received I have to switch view (using ViewStack) and create custom panel (using PopUpManager class). This custom panel contains a dataGrid component which has to be populated with some XML received along with mentioned packet.
Trouble is that when I try to assign XML to DataGrid's dataProvider I constantly get "Cannot access a property or method of a null object reference" error. The only thing I can think of is some kind of race when processing events and creating components.
Here are the most interesting pieces of code:
<!-- LoginEvent.LOGIN_OK _____________________________________________________________________ -->
<EventHandlers type="{LoginEvent.LOGIN_OK}">
<MethodInvoker generator="{UserManager}" method="storeCurrentUser" arguments="{event.fullName}"/>
<EventAnnouncer generator="{NavigationEvent}" type="{NavigationEvent.MAIN}"/>
<MethodInvoker generator="{CustomSocket}" method="listBoards"/>
In the above code I react when the LOGIN_OK packet is received.
Store user's data, change the view and ask the Socket class wrapper to send request (the reponse for that request is our verySpecificPacket)
Here's detailed info about how I change the view and create custom pop up. In MainUI.mxml:
<mate:Listener type="{NavigationEvent.MAIN}" method="handleNavigationEvent" />
private function launchBoardListWindow():void {
Logger.info("launchBoardListWindow()");
var win:BoardList = PopUpManager.createPopUp(this, BoardList, true) as BoardList;
PopUpManager.centerPopUp(win);
}
private function handleNavigationEvent(event:NavigationEvent):void {
viewStack.selectedIndex = MAIN;
launchBoardListWindow();
}
The third position in EventMap isn't important, it just ask socket wrapper to send some kind of packet. The server is supposed to respond with verySpecialPacket along with XML payload. And here we are at the part where the error is. In mxml describing my custom panel I set up a listener for an event which is being dispatched after my verySpecialPacket is received.
public function handleListBoardsEvent(e:ListBoardsEvent):void {
Logger.info("handleListBoardsEvent");
xmlData = e.xml;
boardList.dataProvider = xmlData.children(); // Here's the error!!!
}
I really don't get it, since the xmlData is OK, and custom panel with all child components were created. Thanks for reading!
You're likely on the right track in respect of a race condition.
Suggestion:
Put a try { ... } catch (e:Error) { trace("error"); } block around the code in your handleListBoardsEvent() method.
Then, put a breakpoint on the trace() and, when it hits, take a good look around at the various objects involved.
My guess is that you're attempting to access the boardList object before it is created - i.e. it's null.
The other possibility is that boardList.dataProvider is a setter and there's code in the setter that's barfing. (Although, if that were the case, I'm sure you would have noticed the stacktrace inFlexBuilder)