In CakePHP, how do I load a component in a shell?
To use a component in a controller, you include an array of components in a property called $components. That doesn't work for my Shell. Neither does the "on-the-fly" loading suggested in the documentation.
class MakePdfsShell extends Shell {
public $components = array('Document'); // <- this doesn't work
public function main()
{
$this->Document = $this->Components->load('Document'); // <- this doesnt work either
$this->Document->generate(); // <- this is what I want to do
}
...
I have some xml utilities that I use across some of my controllers. One of the controller launches a heavy task via the cake console, so that it can quietly run in the background via PHP CLI, while the user's request is immediately completed (once the task done, it will e-mail the results to the user).
The xml utilities are generic enough to be used in controller and shell, are too specific to the application to warrant them vendor status. The offered solution with the Lib folder does not work as in CakePhp v3 there seems to be no Lib folder.
After some quite some time spent, I managed to load my controller component to the shell task. Here is how to:
namespace App\Shell;
use Cake\Console\Shell;
use Cake\Core\App;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use App\Controller\Component\XmlUtilitiesComponent; // <- resides in your app's src/Controller/Component folder
class XmlCheckShell extends Shell
{
public function initialize() {
$this->Utilities = new XmlUtilitiesComponent(new ComponentRegistry());
}
...
$this->Utilities can now be used across my entire shell class.
You simply don't.
If you think you have to load a component in shell your application architecture is bad designed and should be refactored.
Technically it is possible but it doesn't make sense and can have pretty nasty side effects. Components are not made to run outside of the scope of a request. A component is thought to run within the scope of a HTTP request and a controller - which is obviously not present in a shell.
Putting things in the right place
Why does XML manipulation stuff have to go into a component? This is simply the wrong place. This should go into a class, maybe App\Utility\XmlUtils for example and have no dependencies at all to the request nor controller.
The logic is properly decoupled then and can be used in other places that need it. Also if you get incoming XML the right place to do this manipulation (by using your utility class) would be inside the model layer, not the controller.
You want to learn about Separation of Concerns and tight coupling
Because you've gone just against both principles.
https://en.wikipedia.org/wiki/Separation_of_concerns
What is the difference between loose coupling and tight coupling in the object oriented paradigm?
Search before asking
You could have tried to search via Google or on SO you would have found one of these:
using components in Cakephp 2+ Shell
CakePHP using Email component from Shell cronjob
Using a plugin component from shell class in cakephp 2.0.2
...
Be aware that some of them might encourage bad practice. I haven't checked them all.
I assume that you have a component named YourComponent:
<?php
App::uses('Component', 'Controller');
class YourComponent extends Component {
public function testMe() {
return 'success';
}
}
- with cake 2., you can load your component like this
App::uses('ComponentCollection', 'Controller');
App::uses('YourComponent', 'Controller/Component');
class YourShell extends AppShell {
public function startup() {
$collection = new ComponentCollection();
$this->yourComponent = $collection->load('Your');
}
public function main() {
$this->yourComponent->testMe();
}
}
- with cake 3. you can load your component like this
<?php
namespace App\Shell;
use App\Controller\Component\YourComponent;
use Cake\Console\Shell;
use Cake\Controller\ComponentRegistry;
class YourShell extends Shell {
public function initialize() {
parent::initialize();
$this->yourComponent = new YourComponent(new ComponentRegistry(), []);
}
public function main() {
$pages = $this->yourComponent->testMe();
}
}
If you are trying to access a custom XyzComponent from a shell, then you probably have commonly-useful functionality there. The right place for commonly-useful functionality (that is also accessible from shells) is in /Lib/.
You can just move your old XyzComponent class from /Controller/Component/XyzComponent.php to /Lib/Xyz/Xyz.php. (You should rename your class to remove the "Component" suffix, e.g., "XyzComponent" becomes "Xyz".)
To access the new location, in your controller, remove 'Xyz' from your class::$components array. At the top of your controller file, add
App::uses('Xyz', 'Xyz'); // that's ('ClassName', 'folder_under_/Lib/')
Now you just need to instantiate the class. In your method you can do $this->Xyz = new Xyz(); Now you're using the same code, but it can also be accessed from your Shell.
//TestShell.php
class TestShell extends AppShell{
public function test(){
//to load a component in dis function
App::import('Component', 'CsvImporter');
$CsvImporter = new CsvImporterComponent();
$data = $CsvImporter->get();
}
}
//CsvImporterComponent.php
App::uses('Component', 'Controller');
class CsvImporterComponent extends Component {
function get(){
//your code
}
}
Related
I understand that this is because of the way proxies are created for handling caching, transaction related functionality in Spring. And the way to fix it is use AspectJ but I donot want to take that route cause it has its own problems. Can I detect self-invocation using any static analyis tools?
#Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
return getSession().getPerson(id);
}
public List<Person> findPersons(int[] ids) {
List<Person> list = new ArrayList<Person>();
for (int id : ids) {
list.add(findPerson(id));
}
return list;
}
If it would be sufficient for you to detect internal calls, you could use native AspectJ instead of Spring AOP for that and then throw runtime exceptions or log warnings every time this happens. That is not static analysis, but better than nothing. On the other hand, if you use native AspectJ, you are not limited to Spring proxies anyway and the aspects would work for self-invocation too.
Anyway, here is what an aspect would look like, including an MCVE showing how it works. I did it outside of Spring, which is why I am using a surrogate #Component annotation for demo purposes.
Update: Sorry for targeting #Component classes instead of #Cacheable classes/methods, but basically the same general approach I am showing here would work in your specific case, too, if you simply adjust the pointcut a bit.
Component annotation:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(TYPE)
public #interface Component {}
Sample classes (components and non-components):
This component is to be called by other components should not lead to exceptions/warnings:
package de.scrum_master.app;
#Component
public class AnotherComponent {
public void doSomething() {
System.out.println("Doing something in another component");
}
}
This class is not a #Component, so the aspect should ignore self-invocation inside it:
package de.scrum_master.app;
public class NotAComponent {
public void doSomething() {
System.out.println("Doing something in non-component");
new AnotherComponent().doSomething();
internallyCalled("foo");
}
public int internallyCalled(String text ) {
return 11;
}
}
This class is a #Component. The aspect should flag internallyCalled("foo"), but not new AnotherComponent().doSomething().
package de.scrum_master.app;
#Component
public class AComponent {
public void doSomething() {
System.out.println("Doing something in component");
new AnotherComponent().doSomething();
internallyCalled("foo");
}
public int internallyCalled(String text ) {
return 11;
}
}
Driver application:
Please note that I am creating component instances throughout this sample code with new instead of requesting beans from the application context, like I would do in Spring. But you can ignore that, it is just an example.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new NotAComponent().doSomething();
new AComponent().doSomething();
}
}
Console log when running without aspect:
Doing something in non-component
Doing something in another component
Doing something in component
Doing something in another component
Now with the aspect, instead of the last message we would expect an exception or a logged warning. Here is how to do that:
Aspect:
Sorry for using native AspectJ syntax here. Of course, you could also use annotation-based syntax.
package de.scrum_master.aspect;
import de.scrum_master.app.*;
public aspect SelfInvocationInterceptor {
Object around(Object caller, Object callee) :
#within(Component) &&
call(* (#Component *).*(..)) &&
this(caller) &&
target(callee)
{
if (caller == callee)
throw new RuntimeException(
"Self-invocation in component detected from " + thisEnclosingJoinPointStaticPart.getSignature() +
" to "+ thisJoinPointStaticPart.getSignature()
);
return proceed(caller, callee);
}
}
Console log when running with aspect:
Doing something in non-component
Doing something in another component
Doing something in component
Doing something in another component
Exception in thread "main" java.lang.RuntimeException: Self-invocation in component detected from void de.scrum_master.app.AComponent.doSomething() to int de.scrum_master.app.AComponent.internallyCalled(String)
at de.scrum_master.app.AComponent.internallyCalled_aroundBody3$advice(AComponent.java:8)
at de.scrum_master.app.AComponent.doSomething(AComponent.java:8)
at de.scrum_master.app.Application.main(Application.java:6)
I think, you can use this solution and maybe rather log warnings instead of throwing exceptions in order to softly guide your co-workers to inspect and improve their AOP-dependent Spring components. Sometimes maybe they do not wish self-invocation to trigger an aspect anyway, it depends on the situation. You could run the Spring application in full AspectJ mode and then, after evaluating the logs, switch back to Spring AOP. But maybe it would be simpler to just use native AspectJ to begin with and avoid the self-invocation problem altogether.
Update: In AspectJ you can also make the compiler throw warnings or errors if certain conditions are met. In this case you could only statically determine calls from components to other components, but without differentiating between self-invocation and calls on other methods from other components. So this does not help you here.
Please also notice that this solution is limited to classes annotated by #Component. If your Spring bean is instantiated in other ways, e.g. via XML configuration or #Bean factory method, this simple aspect does not work. But it could easily be extended by checking if the intercepted class is a proxy instance and only then decide to flag self-invocations. Then unfortunately, you would have to weave the aspect code into all of your application classes because the check can only happen during runtime.
I could explain many more things, such as using self-injection and call internal methods on the injected proxy instance instead of via this.internallyCalled(..). Then the self-invocation problem would be solved too and this approach also works in Spring AOP.
Can I detect self-invocation using any static analysis tools?
In theory you can, but be aware of Rice's theorem. Any such tool would sometimes give false alarms.
You could develop such a tool using abstract interpretation techniques. You may need more than a year of work.
You could subcontract the development of such tools to e.g. the Frama-C team. Then email me to basile.starynkevitch#cea.fr
I have two classes, one a parent, and one a child, that implement types to define their expected functionality. That is below here.
export abstract class BaseLogService implements IBaseLogService
{
static $inject: string[] = ['$http']
constructor(private $http:ng.IHttpService){
}
baseMethod1 = (objToLog): void => {
//do stuff
}
baseMethod2 = (objToLog): void => {
//do stuff
}
}
class LocalLogService extends BaseLogService implements ILocalLogService
{
constructor(private $http:ng.IHttpService){
super(this.$http);
}
localMethod1 = (): void => {
//do stuff
}
}
export interface IBaseLogService {
baseMethod1(objToLog):void;
baseMethod2(objToLog):void;
}
export interface ILocalLogService extends IBaseLogService{
localMethod1(): void;
}
The app utilizes a log service, and logs things for different companies. For company A it might log some different things compared to companies B and C. This is why there is a local log service in each company. But there are universal things that are logged across all companies. So this is why there is a common Log class that the local implementations extend.
With all that introduction out of the way my question is, how do I appropriately utilize my parent class' methods?
To expand on that, let me explain how this is all being used. This is angularjs so in a controller elsewhere in the app there is an api POST call.
On that call's promise return in the .then(), the baseMethod1 needs to be executed to log some common stuff. So it'd be something like the pseudo-code below
myThingToAdd: any;
static $inject: string[] = ['localLogService']
constructor(private localLogService: ILocalLogService){}
this.myPOSTApi.addAThing(this.myThingToAdd)
.then((POSTResponse) => {
this.localLogService.baseMethod1(POSTResponse);
});
This is where my confusion comes into play. I have some company specific stuff that needs to happen when baseMethod1 gets called. But because my service call goes straight to the parent class from the controller, I jump over the local child service where my local logic needs to happen.
I could set it up so my .then() service call goes to my local child class, which does my company specific logic before calling super.baseMethod1(), but that seems very indirect considering I'm able to call the parent class' method straight from my injected service in the controller.
Then I thought, I could make two calls, one for local logic, one for common, each going out to their respective services but that doesn't seem optimized to me. And feels like it defeats the purpose of inheritance.
Then I went down the rabbit hole overthinking my way to where I am now.
Should my parent class methods not be visible to the rest of the app and only be usable inside of my child class? If so how deep should the obfuscation go? Should my child class have publicly exposed methods, that call super.baseMethod1() directly like
localMethod1 = () => {
//local logic
super.BaseMethod1();
}
Or is even that too direct? Should I have a publicly exposed child method, that then calls a private internal method that calls my super method? Like
localMethod1 = () => {
//local logic
this.callSuperMethod1();
}
private callSuperMethod1 = () => {
super.baseMethod1();
}
I think I'm severely overthinking the ideas of inheritance, and encapsulation, and would appreciate some advice on how to proceed and strike a balance between pragmatic proper practices and efficient/optimized code.
Inheritance is exactly the way this problem is supposed to be addressed:
I could set it up so my .then() service call goes to my local child class, which does my company specific logic before calling super.baseMethod1(), but that seems very indirect considering I'm able to call the parent class' method straight from my injected service in the controller.
Child class can implement its own baseMethod1 and can also apply the behaviour from a parent with super method call. As explained in this answer, inheritance is one of few reasons why instance arrow methods shouldn't be used. This won't work because instance method doesn't have access to super:
localMethod1 = () => {
//local logic
super.BaseMethod1();
}
It is:
export abstract class BaseLogService {
...
constructor(protected $http:ng.IHttpService) {}
baseMethod1(objToLog): void {
//do stuff
}
...
}
class LocalLogService extends BaseLogService {
// can be omitted
constructor($http:ng.IHttpService){
super($http);
}
baseMethod1(objToLog): void {
super.baseMethod1(objToLog)
//do stuff
}
}
If ILocalLogService completely replicates public BaseLogService interface, it's redundant - BaseLogService can be used as interface. $http should be protected and not private, because it may be used in child classes. Visiblility modifier should be specified only once, in parent constructor. If child constructor does nothing except calling super, it can be omitted.
Since only one logger class is supposed to be used at a time, this is what DI pattern is for. This can be addressed solely with AngularJS DI:
// company A module
app.service('logger', ALogService);
Units that use the service shouldn't be aware of which implementation they are using:
FooController {
constructor(public logger: BaseLogService) {}
}
I reconfigured my cakeEmail class to log to a specific type by rewriting the send method. I would like to use this override globally. My current single file setup uses /Lib/CustomCakeEmail.php with
App:uses('CustomCakeEmail', 'Lib');
CakePhp: Cake Email AfterSend event suggests a method to globally override using AppController but I have been unable to even trigger the debugger in
App::uses('CustomCakeEmail', 'Lib');
class AppController extends Controller {
public function getEmailInstance($config = null) {
CakeLog::write('debug', 'appcontroller triggered');
return new CustomCakeEmail($config);
}
What is the correct way to implement this global override?
CakePHP Version 2.8.4
I was developing a pagefactory based framework. I had earlier used pagefactory.initements method to inintialise and move from page to page. Init method basically does the same work as saying Homepage HP = new HomePage(driver);
So it is necessary to use init method in pagefactory?
Will we loose something if we do not use it and instead use new to create a page.
If you are using a Java PageFactory with annotations yes.
The PageFactory.initElements(driver, My.class) command parses the annotations and sets up the Java Proxy classes. If you don't .initElements() none of the WebElements in your class will have locators assigned to them and they will all be null.
You can put the .initElements() in your constructor if you just want to new up a page e.g.:
public class MyPage {
public MyPage(WebDriver driver) throws Exception {
PageFactory.initElements(driver, this);
}
}
I’m trying to create a custom datasource for Amazon Web Services in CakePHP. My approach is as follows:
Base AwsDataSource that creates signatures, makes the actual HTTP requests etc
Various datasources for each AWS product (i.e. S3, SQS etc) that extend this class and specifies the endpoint to use
Models for things like S3Bucket, SqsQueue, SqsMessage and so on
My base datasource class looks like this (simplified):
<?php
class AwsDataSource extends DataSource {
public $config = array(
'key' => '',
'secret' => '',
'region' => ''
);
public $endpoint;
public function signRequest($parameters) {
// generates signature
}
public function makeRequest($parameters = array(), $method = 'get') {
// generates signature and makes HTTP request to AWS servers
}
}
And a sample model looks like this:
<?php
class SqsQueue extends AwsAppModel {
public $name = 'SqsQueue';
public $useTable = false;
}
My problem comes trying to then use these models/datasources in my CakePHP app.
I’ve implemented methods named create(), read(), update() and delete() in my AWS datasource as per the CakePHP cookbook, but they don‘t seem to be getting called. I know this because I’ve put die() statements in my datasource with a message, and execution is never stopped.
I’ve exhausted the cookbook, so if any one could show me how to get my models to call the CRUD methods in my datasource classes then I’d be most grateful.
My bad. Turns out my approach was flawed.
The datasource is specified in database config and is specified as AwsDataSource. Therefore, S3DataSource or SqsDataSource is never used, even though that’s where I’ve defined my CRUD methods, hence my application never exiting (because the CRUD methods aren’t defined in AwsDataSource, the actual datasource being called).
Looks like it’s back to the drawing board.