Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Response Factory #73

Closed
wants to merge 1 commit into from

Conversation

dkgroot
Copy link

@dkgroot dkgroot commented Apr 14, 2015

Added ResponseFactory

  • Allows you to setResponseHandler("MyDefaultHandler") from MyMessageAction.php
  • Searches for Response with the same name as the Action /PAMI/Message/Action/TestAction -> /PAMI/Message/Response/TestResponse
  • Falls back to GenericResponse Handler (Like UnknownEvent) This will make it easier for people to extend the handling of ResponseMessage.php without having to change this file.
    Added logging to this factory classes, so we can see what it produces. Might be usefull in the Event Factory as well.
  • Added initial test/factories/
  • Kept the input key/value Sanitzer for a later PR
  • Tried to preserve indentation as much as possible (looks ok as far as i can tell).

This was referenced Apr 14, 2015
@jacobkiers
Copy link
Contributor

It took me a while, but I think I understand what you are trying to achieve now.

Goals

From the description and the code I can see two objectives:

  1. For synchronous Actions (*Action objects), the ability to specify which response message is created for a specific Action.
  2. The ability to override the created response message.

Is this correct?

Comments

Now, assuming it is, I have a few comments about the general approach.

First, a lot is hard coded here, which I believe will prevent achieving the second objective. This has especially to do with the fact that:

  • The response factory implementation is hard coded
  • The response message implementations must be in the namespace of the PAMI project

Secondly, the response class to be used for creating a specific response is set on the message object. I may be wrong, but I think that you try to achieve that a command of type XAction will always receive a response of type MyXResponse. By setting this on the message object, it must be set for each individual message of that type. It would be better if one is able to specify that globally.

Alternative suggestion

Given these comments, I suggest the following approach:

  • Create an interface IResponseFactory, which will have two methods (see further for an example)
  • Add the ability to register the response class for a specific Action
  • Allow the user to set the Factory (IClient::setResponseFactory(IResponseFactory $factory)
  • Create the response message with the factory when it is received, with a fallback to the GenericResponse or ResponseMessage class. NOTE: This is already the case in the current implementation.

Example interface

As an example of the interface, I would suggest the following.

interface IResponseFactory
{
    /**
     * @param string $commandClass The class name of the command
     * @param string $responseClass The class name of the response
     *
     * @return void
     * @throws InvalidArgumentException when the response class is not found.
     */
    public function registerReponseClassForCommand($commandClass, $responseClass);

    /**
     * @param OutgoingMessage $originalMessage The message that was originally sent
     * @param string $rawResponseMessage The raw array received from Asterisk
     *
     * @return ResponseMessage
     */
    public function createReponseForCommand($originalMessage, $rawResponseMessage);
}

Please do not bother with commenting on the naming of things. I named things to make this suggestion as clear as possible, not to be how PAMI does things now.

@dkgroot
Copy link
Author

dkgroot commented Apr 15, 2015

Hi Jacob,

Without the example uses of the change it might be a little harder to see the benefit.

Goals:

Basically yes. It will look for a Response Handler with the the same name as the Action first, if not found it will use the GenericReponse handler.
For example:
SCCPShowDeviceAction.php
Will 'automatically' result in
SCCPShowDeviceResponse.php

If need be you can override it by setting the ResponseHandler manually when creating the action. For example to point multiple Actions to one ResponseHandler like for example SCCPGenericResponse.php.

Comments:

Which parts are hardcoded according to you ? I followed the '/Event/Factory/Impl/EventFactoryImpl.php which was already there. This should not really have to be different.

I will try to match an Action with a Response and FallBack to GenericResponse.php if not found. You can override this behaviour by setting a ResponseHandler Manually, for example to point multiple Actions to a MinimalResponse or something. I thought this would make things pretty flexible.

Alternative Suggestion:

I guess that would work as well, and could be benefitial. I hope this does not mean multiple ReponseFactory implementations (although that would be possible using the same interface).

I did not want to change to much of the original design and only suggest a possible implementation (was mostly focussed on the response results). There are many ways to reach the same goal. At the moment ClientImpl seems to be able to handle only one outgoing Action message (well) at a time. And my impletation might be reinforing that (which can be seen as bad).

(You have the same problem as I do when typing Response , somehow we skip over the first 's' :-)

@jacobkiers
Copy link
Contributor

I've created a pull request on your branch (dkgroot#2), showing what I meant.

@jacobkiers
Copy link
Contributor

But let's keep the discussion going here, and not at the PR on your branch, OK?

@jacobkiers
Copy link
Contributor

I forgot to say what changes. First off, it is still compatible with @dkgroot's approach. But it also give the user a little more flexibility.

With my example code, it is possible register actions for a specific message object and for all messages of the same class, using the factory provided in PAMI.

If someone still needs more flexibility, it is possible to inject a fully customized Response Factory, as long as it adheres to the IResponseFactory interface.

EDIT: typo's

@dkgroot
Copy link
Author

dkgroot commented Apr 16, 2015

Jacob, thanks for taking the trouble to create an example implementation, that always aids in understanding. As far as i can see it (only) adds a layer of indirection which can/might be a good thing as it will give the opportunity to replace the responseFactory by the end user of the library.

The actual original question remains however, which is do 'we' want:

  • a responseFactory
  • automatic selection of the responsemessage based on the name of the action.
  • to be able to set a responseHandler on an Action, to override automatic selection
  • to keep the somewhat kludgy solution of using $_lastActionClass

I think these question should be answered before choosing any implementation. I would be absolutely fine with your solution to the problem.

You might even want to duplicate this factory interface to the EventFactory as well. There are some events for which it would make sense to handle them using one EventHandler (The ....CloseEvents are all the same, for example). (ie: For the chan-sccp-b there are quite a number of Aktion commands the will generate Events that need to be closed by these ...CloseEvents). Let's keep this for the next PR.

@jacobkiers
Copy link
Contributor

Hi Diederik,

Let's start with an even more fundamental question: do we want specific Response messages in the first place? Personally, I would welcome it. At the very least it makes it easier to develop against a specific type, which aids in development (for example, IDE's can provide autocompletion, which is a huge win for me).

Given that we do, I would answer your questions as follows:

  • Yes, a Response Factory makes sense. Any implementation will have some kind of a Factory, because any implementation will have to instantiate new objects. So better to make a specific class responsible for that, than having to add the logic to the - already quite complicated - ClientImpl.
  • Yes, automatic selection makes sense in the default case. In almost 100% of the cases, one would want to use the PAMI-provided response class.
  • I'm not sure about setting a response class for a specific instance of an Action. Being able to specify it for a whole class of Actions (i.e. any FooAction will always result in a BarResponse) could be useful.
  • Using $_lastActionClass in ClientImpl is an artefact of the current implementation. We'll deal with that when discussing implementations :).

Let's first see what @marcelog thinks, and whether he has any ideas with regards to implementation.

@dkgroot
Copy link
Author

dkgroot commented Apr 18, 2015

Waiting for @marcelog to see how we can make progress on this. I have a whole slew of action/repsonses in waiting. There is even someone (@pnlarsson) who created a PR against my repository with PJSIP related actions, events and responses, using the same response factory :-)

@dkgroot
Copy link
Author

dkgroot commented May 1, 2015

@marcelog: Any progress / viewpoints, or just really really busy ?

@dkgroot
Copy link
Author

dkgroot commented Aug 23, 2015

Any chance of revisiting this MR ?

@jacobkiers
Copy link
Contributor

@marcelog I'd like to see this one get in as well. Could you check this out and respond please? It's waiting for your feedback in order to move forward.

Thanks!

@marcelog
Copy link
Owner

@dkgroot @jacobkiers I'm not sure I like how it is right now. Also, there are no further comments from others to have something like this.

Since I'm the only one that can merge, you will have to be patient guys and wait for me to have enough time to sit and think about it for a bit (so far, I couldn't). Perhaps I'd like to have a different implementation, or maybe a few adjustments would do the job.

It's not necessary to insist by adding more comments here: it's in my queue, and that's all I can say right now. I know I'm being the bottleneck, but this is actually how open source works sometimes: out of the free time and good will of others (I did too have pull requests of mine open for months, some of them got merged, some are not even reviewed yet, and I trust that someone will eventually have the time to review them). Unfortunately, PAMI will not pay my bills (or anything, since it's just a hobby and a way to contribute something to others) so other things get my attention sometimes.

Thanks for understanding!

@dkgroot
Copy link
Author

dkgroot commented Aug 24, 2015

Hi Marcelo,

On 24-08-15 13:11, Marcelo Gornstein wrote:

@dkgroot https://github.com/dkgroot @jacobkiers https://github.com/jacobkiers I'm not sure I like how it is right now. Also, there are no further comments from others to have something like this.

If there is anything you would like to see change, just give me a holler. Someone sent me a PR based on my current trunk revision, for merge. With further asterisk message processing using the same response factory.

Since I'm the only one that can merge, you will have to be patient guys and wait for me to have enough time to sit and think about it for a bit (so far, I couldn't). Perhaps I'd like to have a different implementation, or maybe a few adjustments would do the job.

Of course, no hurry. Just didn't know if you had seen the PR and if there was anything you wanted me to do.

It's not necessary to insist by adding more comments here: it's in my queue, and that's all I can say right now. I know I'm being the bottleneck, but this is actually how open source works sometimes: out of the free time and good will of others (I did too have pull requests of mine open for months, some of them got merged, some are not even reviewed yet, and I trust that someone will eventually have the time to review them). Unfortunately, PAMI will not pay my bills (or anything, since it's just a hobby and a way to contribute something to others) so other things get my attention sometimes.

Ok super. Sure, I do understand ! I will not ask any further, just didn't know how to proceed. Have the same problem with the chan-sccp-b project ;-)

Thanks for understanding!

No problem !

Regards,

Diederik


Reply to this email directly or view it on GitHub #73 (comment).

@marcelog
Copy link
Owner

Thanks, and sorry if I seemed to be a little "loud" there. I didn't reply before because I don't actually have a better thing to say other than "I'm not convinced", so I wanted to sit and look at the specific problem of how you are getting the responses (the forest) instead of the tree (the response factory implementation). I always try to point out specific things and improvements. So far I don't have them.

Do you know if this might be needed for other things than chan-sccp-b? Can you give some specific examples of the issues you want to solve when dealing with chan-sccp-b with this pull? (I understand the format of the responses change, but how exactly?)

Thanks!

@dkgroot
Copy link
Author

dkgroot commented Aug 24, 2015

No problem at all.

pnlarsson used the extended implementation to implement some PJSIP parsers also using the Response Factory, see:
pnlarsson@d18e9cb
https://github.com/pnlarsson/PAMI/blob/chan-sccp-b/src/mg/PAMI/Message/Event/AorDetailEvent.php

By having the factory you can move some of the post message processing into PAMI, making it easier to use PAMI as a library, understanding the differences between series of AMI output. PJSIP and SCCP have partially digressed/extended the sometimes minimal output from an AMI command. For example in the case of SCCP a command like 'SCCPShowDevice' will return a message header with multiple sub lists, which standard AMI does not really have a natural format for. We used what was there, but the post-processer has to know and expect what we are sending.

Example:

Response: Success
EventList: start
Message: SCCPShowDevice list will follow

Event: SCCPShowDevice
MACAddress: SEPE01234567
ProtocolVersion: Supported '0', In Use '0'
ProtocolInUse: NONE Version 0
DeviceFeatures: 0,00000000000000000000000000000000
Tokenstate: No Token
Keepalive: 60
RegistrationState: None(2)
State: On Hook(0)
MWILight: On(2)
MWIHandsetLight: 0000000000000000000000000000000 (0)
MWIDuringCall: keep on
Description: VideoPhone
ConfigPhoneType: bl7970
SkinnyPhoneType: Undefined: Maybe you forgot the devicetype in your config(0)
SoftkeySupport: no
Softkeyset: my_softkeyset ((nil))
BTemplateSupport: no
linesRegistered: no
ImageVersion:
TimezoneOffset: 0
Capabilities: (nothing)
CodecsPreference: (g722/64k (6), g722/56k (7), g722/48k (8), alaw/64k (2), alaw/56k (3), ulaw/64k (4), ulaw/56k (5), g729 (11), g729a (12), g729b (15), g729ab (16), g729/annex/b (85), h263 (101), h263p (102), h264 (103))
AudioTOS: 184
AudioCOS: 6
VideoTOS: 136
VideoCOS: 5
DNDFeatureEnabled: yes
DNDStatus: Disabled
DNDAction: Reject
CanTransfer: on
CanPark: on
CanCFWDALL: on
CanCFWBUSY: off
CanCFWNOANSWER: off
AllowRinginNotification: no
PrivateSoftkey: on
DtmfMode: SKINNY
Nat: Auto
Videosupport: no
DirectRTP: off
TrustPhoneIpDeprecated: off
BindAddress: ???.???.???.???
ServerAddress: ???.???.???.???
DenyPermit: deny:0.0.0.0/0.0.0.0,permit:10.1X.XXX.XXX/255.255.255.0,permit:127.0.0.0/255.255.255.0,
PermitHosts:
EarlyRTP: Ringout
DeviceStateAcc: None
LastUsedAccessory: None
LastDialedNumber: (0)
DefaultLineInstance: 0
CustomBackgroundImage: http://10.1X.XXX.XXX:8011/static/strand.png
CustomRingTone: ---
UsePlacedCalls: on
PendingUpdate: off
PendingDelete: off
DirectedPickup: on
PickupContext: sccp (exists)
PickupModeAnswer: on
allowConference: on
confPlayGeneralAnnounce: on
confPlayPartAnnounce: on
confMuteOnEntry: on
confMusicOnHoldClass: default
confShowConflist: on
conflistActive: off

Event: TableStart
TableName: Buttons

Event: SCCPDeviceButtonEntry
ChannelType: SCCP
ChannelObjectType: DeviceButton
Id: 1
Inst: 0
TypeStr: Line
Type: 0
pendUpdt: No
pendDel: No
Default: No

Event: SCCPDeviceButtonEntry
ChannelType: SCCP
ChannelObjectType: DeviceButton
Id: 2
Inst: 0
TypeStr: Line
Type: 0
pendUpdt: No
pendDel: No
Default: No

Event: TableEnd
TableName: Buttons
TableEntries: 2

Event: TableStart
TableName: LineButtons

Event: SCCPDeviceLineEntry
ChannelType: SCCP
ChannelObjectType: DeviceLine
Id: 2
Name: 98099
Suffix: 2
Label: Shared
CfwdType: None
CfwdNumber:

Event: TableEnd
TableName: LineButtons
TableEntries: 1

Event: TableStart
TableName: SpeeddialButtons

Event: SCCPDeviceSpeeddialEntry
ChannelType: SCCP
ChannelObjectType: DeviceSpeeddial
Id: 3
Name: 98011
Number: 98011
Hint: 112@hints

Event: TableEnd
TableName: SpeeddialButtons
TableEntries: 1

Event: TableStart
TableName: FeatureButtons

Event: SCCPDeviceFeatureEntry
ChannelType: SCCP
ChannelObjectType: DeviceFeature
Id: 8
Name: call forward
Options: 500
Status: 0

Event: TableEnd
TableName: FeatureButtons
TableEntries: 1

Event: TableStart
TableName: ServiceURLButtons

Event: TableEnd
TableName: ServiceURLButtons
TableEntries: 0

Event: TableStart
TableName: CallStatistics

Event: SCCPDeviceStatisticsEntry
ChannelType: SCCP
ChannelObjectType: DeviceStatistics
Type: LAST
Calls: 0
PcktSnt: 0
PcktRcvd: 0
Lost: 0
Jitter: 0
Latency: 0
Quality: 0.000000
avgQual: 0.000000
meanQual: 0.000000
maxQual: 0.000000
rConceal: 0.000000
sConceal: 0

Event: SCCPDeviceStatisticsEntry
ChannelType: SCCP
ChannelObjectType: DeviceStatistics
Type: AVG
Calls: 0
PcktSnt: 0
PcktRcvd: 0
Lost: 0
Jitter: 0
Latency: 0
Quality: 0.000000
avgQual: 0.000000
meanQual: 0.000000
maxQual: 0.000000
rConceal: 0.000000
sConceal: 0

Event: TableEnd
TableName: CallStatistics
TableEntries: 2

Event: SCCPShowDeviceComplete
EventList: Complete
ListItems: 282

ListTableItems: 6

Because a little extended knowledge is expected about the returned information, it will be easier for me to add the post processing into PAMI, than it might be for someone else to do it in their source code using PAMI. PAMI as is will be able to parse and pass-on the information provided, but the enduser(s) will have to implement their own methods to use the information.

I tried adding the factory in such a way that non of the other messages/events would have to be touched/changed. I hope this somewhat long winded explanation helps in understanding my objective(s) for the change.

@@ -402,6 +416,10 @@ public function send(OutgoingMessage $message)
);
}
$this->_lastActionId = $message->getActionId();
// If there are multiple outgoing messages in flight, we might have to add this information to a queue instead, like:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the commented code if it's not used

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem

@dkgroot
Copy link
Author

dkgroot commented Aug 24, 2015

Would you like me to make these corrections before discussing any further progression, or leave it until you have had a change to deliberate if you would like to see something like this in your source base ? No stress, take your time.

@dkgroot dkgroot deleted the response_factory_v2 branch April 24, 2019 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants