Check whether a controller exists in extension of \Zend\Mvc\Router\Http\Segment - migration

I am working on a legacy application based on ZF2; some times ago we upgraded to a recent version of ZF2 and now our logs are flooded with warnings about deprecated getServiceLocator().
I am searching for a way to check whether a controller exists in a router which extends \Zend\Mvc\Router\Http\Segment; at the moment the source is like:
protected function isInvokableController($controller)
{
return $this->getServiceLocator()->get('ControllerLoader')->has($controller);
}
this leads to a warning about getServiceLocator() deprecation.
Till now I did not find any way to get the ControllerLoader as DI. Is there any way to create routers via factories and getting DI (I guess there will be some other places in the application for using this) or any other way to check whether a controller exists?

Related

Workbox/Vue: Create a custom variation on an existing caching strategy handler

Background:
I'm building an SPA (Single Page Application) PWA (Progressive Web App) using Vue.js. I've a remote PostgreSQL database, serving the tables over HTTP with PostgREST. I've a working Workbox Service Worker and IndexedDB, which hold a local copy of the database tables. I've also registered some routes in my service-worker.js; everything is fine this far....
I'm letting Workbox cache GET calls that return tables from the REST service. For example:
https://www.example.com/api/customers will return a json object of the customers.
workbox.routing.registerRoute('https://www.example.com/api/customers', workbox.strategies.staleWhileRevalidate())
At this point, I need Workbox to do the stale-while-revalidate pattern, but to:
Not use a cache, but instead return the local version of this table, which I have stored in IndexedDB. (the cache part)
Make the REST call, and update the local version, if it has changed. (the network part)
I'm almost certain that there is no configurable option for this in this workbox strategy. So I would write the code for this, which should be fairly simple. The retrieval of the cache is simply to return the contents of the requested table from IndexedDB. For the update part, I'm thinking to add a data revision number to compare against. And thus decide if I need to update the local database.
Anyway, we're now zooming in on the actual question:
Question:
Is this actually a good way to use Workbox Routes/Caching, or am I now misusing the technology because I use IndexedDB as the cache?
and
How can I make my own version of the StaleWhileRevalidate strategy? I would be happy to understand how to simply make a copy of the existing Workbox version and be able to import it and use it in my Vue.js Service Worker. From there I can make my own necessary code changes.
To make this question a bit easier to answer, these are the underlying subquestions:
First of all, the StaleWhileRevalidate.ts (see link below) is a .ts (TypeScript?) file. Can (should) I simply import this as a module? I propably can. but then I get errors:
When I to import my custom CustomStaleWhileRevalidate.ts in my main.js, I get errors on all of the current import statements because (of course) the workbox-core/_private/ directory doesn't exist.
How to approach this?
This is the current implementation on Github:
https://github.com/GoogleChrome/workbox/blob/master/packages/workbox-strategies/src/StaleWhileRevalidate.ts
I don't think using the built-in StaleWhileRevalidate strategy is the right approach here. It might be possible to do what you're describing using StaleWhileRevalidate along with a number of custom plugin callbacks to override the default behavior... but honestly, you'd end up changing so much via plugins that starting from scratch would make more sense.
What I'd recommend that you do instead is to write a custom handlerCallback function that implements exactly the logic you want, and returns a Response.
// Your full logic goes here.
async function myCustomHandler({event, request}) {
event.waitUntil((() => {
const idbStuff = ...;
const networkResponse = await fetch(...);
// Some IDB operation go here.
return finalResponse;
})());
}
workbox.routing.registerRoute(
'https://www.example.com/api/customers',
myCustomHandler
);
You could do this without Workbox as well, but if you're using Workbox to handle some of your unrelated caching needs, it's probably easiest to also register this logic via a Workbox route.

Failure in creation of feature class: Unable to create object class extension COM component

I am working with ArcGIS 10.5, installed on-premise, and are developing our feature class in .NET.
I have an issue with registering feature classes. We have created a Feature Class and registered the DLL through “ESRIRegAsm.exe”, and it appears in ArcCatalog:
Trigger appearing image
But when I try to create the Feature Class, I get the following error:
Failed to create feature class. Unable to create object class extension COM Component
Which isn't very helpful, unfortunately.
The odd thing is, that we have another trigger registered on another Feature Class, that works as expected. And the new trigger is based on a copy of the old trigger's code (with changed GUID's).
The steps I have done so far:
I have tried to add the feature class to the component category using categories.exe.
Registered it using ESRIRegAsm.exe for both Desktop and Engine.
Checked that there are .ecfg config files - and there are.
Checked that the CLSID's appear in the windows registry.
The essential parts of our trigger source-code can be found here: here.
Any help would be greatly appreciated, as we are stuck on this.
Our issue was that our ID's were wrong in the code.
The ClassExtensionCLSID should return the same ID as TriggerExtension has.
Moreover, InstanceCLSID should always return 52353152-891A-11D0-BEC6-00805F7C4268, and should hence not get a new ID.

Symfony - fallback to another application if Symfonfy app can not handle the request

We have an old Yii application along with new Symfony one.
The basic idea is simple - I need to check if there is a route matching in Symfony application then it is cool, if not then bootstrap Yii application and try to handle the request with it.
The main idea to not instantiate AppKernel (and do not load autoload.php - since there is two different autoload.php for each project) before I am sure there is route matching.
Can I do it somehow?
We've done this before with legacy applications.
There are two approaches you can take.
Wrap your old application inside a symfony project (recommended).
Unfortunately this will indeed load the symfony front-controller and kernel. No way around that. You need to make sure that symfony can't handle the request and to do that the kernel needs to be booted up.
Use sub-directories and apache virtual hosts to load one application vs the other as needed.
Given option 1,
You can either create your own front controller that loads either symfony or yii by reading routes (from static files if using yml or xml, or annotations which will be more complex) OR EventListener (RequestListener) that listens to the HttpKernelInterface::MASTER_REQUEST and ensures that a route can be returned.
Creating your own front controller is the only way that you can make it not load the symfony kernel, but it will require you to write something that understands the routes in both frameworks (or at least symfony's) and hands off the request appropriately.
Event listener example:
public function onkernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
... Code to continue normally, or bootstrap yii and return a custom response... (Can include and ob_start, or make an http request, etc)
}
public function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest']
];
}
As you see, the kernel needs to be booted to ensure symfony can't serve the route. Unless creating your own front controller (as stated above).
A third approach would be to create a fallback controller, which would load up a specified URL if no route was found within symfony. Although this approach is generally used for legacy projects that lack a framework and use page scripts instead of proper routes, and definitely requires the use/help of output buffering.
The EventListener approach gives you the opportunity to create a proper Request to hand off to yii, and using what is returned to create a Response as proper symfony object (can also use ob or other options).
Thank you.
This is an alternative to vpassapera's solution -http://stovepipe.systems/post/migrating-your-project-to-symfony

Conversion of V2 Ninject Binding to V3

I've been banging my head at this for about 8 hours now, and I just can't seem to find a simple explanation on how to change my custom bootstrapper for ninject (Last worked on the code back in v2.x.x.x) to the new v3.0.0.0 syntax.
I currently have the following:
public class NinjectCustomBootStrapper : NinjectNancyBootstrapper
{
protected override Ninject.IKernel GetApplicationContainer()
{
return Program.MyContainer;
}
}
in a septate class, and :
public static IKernel MyContainer
{
get { return _myContainer ?? (_myContainer = CreateKernel()); }
set { _myContainer = value; }
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<CardMonitorService>().ToSelf().InSingletonScope();
return kernel;
}
in my main program 'Program.c' in a command line app.
Iv'e since updated ninject to V3.0.0.0 only to find that there's been some breaking changes. I'll admit I don't use ninject very often (I usually use structuremap), and the only reason this project does is I didn't write it originally.
Since I've upgraded Ninject, now when the app is started up I get the following exception:
Method not found: 'Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax`1<!0>
Ninject.Syntax.IBindingToSyntax`1.ToConstant(!0)'.
After a ton of searching and researching, the closest I've been able to find is this:
http://sharpfellows.com/post/Ninject-Auto-registration-is-changing-in-version-3.aspx
Which while it points me in the right direction, still isn't quite a solution as I'm not using a custom binding generator.
So my question is this.
How do I rewrite the above so that my project once again works and the WCF service when called gets the correct singleton binding handed to it when a request comes in. Going back to ninject 2 is not an option, as other dependencies in the project that have been added have forced the v3 upgrade and these add new functionality that's been requested hence why I'm working on it.
For reference this is a .NET4 build, running on NancyFX with a self hosting WCF setup as a windows service using Topshelf to provide the SCM interface.
Cheers
Shawty
Addendum to clear things up a little
This is an existing project that was originally written sometime back, I've been asked to add some new features to the project.
As part of adding these new features I have been required to upgrade the version of Ninject being used from an earlier version to V3.0.0.0 as newer dependencies added to the project require the newer version of Ninject.
Under the previous V2 of Ninject the code I have given above worked fine, with no issues, since the project has had Ninject V3 added I now get the exception as described above.
I cannot go back to the earlier version of Ninject as that will mean not being able to add the new functionality that I'm adding.
From the research I've done so far the sharpfellows link above is the closest explanation of my issue that I've managed to find so far on the internet.
I don't use Ninject very often, so I've not got the background to know what's changed between V2 & V3 that (based on my research) is the cause of my issue.
I need to know how to change my code written under V2 (and shown above) so that it works under V3.
MissingMethodException is usually a deployment problem. You compile against a different assembly than you deploy. Check that you deployed the same version and same build.
So after a week or so it turns out that the problem was that the Nancy dev team broke binary comparability with the latest version of ninject (or vice versa) :-)
There is a GitHub pull request to fix this available at :
https://github.com/NancyFx/Nancy.Bootstrappers.Ninject/pull/6
However the next version 'Nancy.Bootstrapper.Ninject' 0.12 will be out on NuGet soon which will have the fix implemented.

Why is Mage_Persistent breaking /api/wsdl?soap

I get the following error within Magento CE 1.6.1.0
Warning: session_start() [<a href='function.session-start'>function.session-start</a>]: Cannot send session cookie - headers already sent by (output started at /home/dev/env/var/www/user/dev/wdcastaging/lib/Zend/Controller/Response/Abstract.php:586) in /home/dev/env/var/www/user/dev/wdcastaging/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php on line 119
when accessing /api/soap/?wsdl
Apparently, a session_start() is being attempted after the entire contents of the WSDL file have already been output, resulting in the error.
Why is magento attempting to start a session after outputting all the datums? I'm glad you asked. So it looks like controller_front_send_response_after is being hooked by Mage_Persistent in order to call synchronizePersistentInfo(), which in turn ends up getting that session_start() to fire.
The interesting thing is that this wasn't always happening, initially the WSDL loaded just fine for me, initially I racked my brains to try and see what customization may have been made to our install to cause this, but the tracing I've done seems to indicate that this is all happening entirely inside of core.
We have also experienced a tiny bit of (completely unrelated) strangeness with Mage_Persistent which makes me a little more willing to throw my hands up at this point and SO it.
I've done a bit of searching on SO and have found some questions related to the whole "headers already sent" thing in general, but not this specific case.
Any thoughts?
Oh, and the temporary workaround I have in place is simply disabling Mage_Persistent via the persistent/options/enable config data. I also did a little bit of digging as to whether it might be possible to observe an event in order to disable this module only for the WSDL controller (since that seems to be the only one having problems), but it looks like that module relies exclusively on this config flag to determine it's enabled status.
UPDATE: Bug has been reported: http://www.magentocommerce.com/bug-tracking/issue?issue=13370
I'd report this is a bug to the Magento team. The Magento API controllers all route through standard Magento action controller objects, and all these objects inherit from the Mage_Api_Controller_Action class. This class has a preDispatch method
class Mage_Api_Controller_Action extends Mage_Core_Controller_Front_Action
{
public function preDispatch()
{
$this->getLayout()->setArea('adminhtml');
Mage::app()->setCurrentStore('admin');
$this->setFlag('', self::FLAG_NO_START_SESSION, 1); // Do not start standart session
parent::preDispatch();
return $this;
}
//...
}
which includes setting a flag to ensure normal session handling doesn't start for API methods.
$this->setFlag('', self::FLAG_NO_START_SESSION, 1);
So, it sounds like there's code in synchronizePersistentInf that assumes the existence of a session object, and when it uses it the session is initialized, resulting in the error you've seen. Normally, this isn't a problem as every other controller has initialized a session at this point, but the API controllers explicitly turns it off.
As far as fixes go, your best bet (and probably the quick answer you'll get from Magento support) will be to disable the persistant cart feature for the default configuration setting, but then enable it for specific stores that need it. This will let carts
Coming up with a fix on your own is going to be uncharted territory, and I can't think of a way to do it that isn't terribly hacky/unstable. The most straight forward way would be a class rewrite on the synchronizePersistentInf that calls it's parent method unless you've detected this is an API request.
This answer is not meant to replace the existing answer. But I wanted to drop some code in here in case someone runs into this issue, and comments don't really allow for code formatting.
I went with a simple local code pool override of Mage_Persistent_Model_Observer_Session to exit out of the function for any URL routes that are within /api/*
Not expecting this fix to need to be very long-lived or upgrade-friendly, b/c I'm expecting them to fix this in the next release or so.
public function synchronizePersistentInfo(Varien_Event_Observer $observer)
{
...
if ($request->getRouteName() == 'api') {
return;
}
...
}