...
By adding support for your phone model you take advantage of sipXconfig open architecture: not only will other developers be able to add additional features to your particular phone configurator, but also every improvement to sipXconfig platform contirbutes contributes new features to your phone plugin. What's more if your phone is supported by sipXconfig it will be managed with other phones, ATAs, and gateways. Out of the box, your phone configurator will support basic UI, configuration backup and templating mechanism. Later you will be able to add more advanced features such as intercom, speed dial, directory, time zone support etc.
...
We'll create support for a ficticious fictitious phone called the Acme Phone. It supports 5 lines (also called registrations) and a handful of SIP related settings. It loads a single configuration file from a TFTP server and the format looks like a typical "INI" File.
...
Subclass from base phone class.
Panelcode |
---|
public class AcmePhone extends Phone \{ |
You'll want to select a new name for your phone and rename the Java source file appropriately.
It's important to know that a new instance of your phone class is instantiated each time sipXconfig generates a new configuration file, reboots a phone or displays settings screen in the web UI.
Define setting constants
Panelcode |
---|
private static final String USER_ID_SETTING = "credential/userId"; |
The field name is arbitrary, but the path should correspond to path in
...
phone.xml
...
or
...
line.xml
...
. You only need to define settings that will need special processing, one of which is described in the next section.
Support for external lines
Panelcode |
---|
@Override protected void @Override protected void setLineInfo(Line line, LineInfo info){ line \{ line.setSettingValue(USER_ID_SETTING, info.getUserId()); ... } |
@Override
Panel |
---|
protected LineInfo
\}
|
Code Block |
---|
@Override protected LineInfo getLineInfo(Line line){ LineInfo info = new \{ LineInfo info = new LineInfo(); info.setDisplayName info.setDisplayName(line.getSettingValue(DISPLAY_NAME_SETTING)); ... return info; }
return info;
\}
|
Here we account for all the very basic line information the system would need to examine and create a registration for your phone. This information is important when creating external line registationsregistrations, but may have other uses in the future.
Load settings files
Panelcode |
---|
@Override protected Setting @Override protected Setting loadSettings(){ return \{ return getModelFilesContext().loadModelFile("phone.xml", getBeanId()); } @Override protected Setting \} @Override protected Setting loadLineSettings(){ return \{ return getModelFilesContext().loadModelFile("line.xml", getBeanId()); }
\}
|
This is required and typical for all phones. Here you would have an opportunity to alter the settings model, for each phone instance.
...
Approach 1: Java annotations
Panelcode |
---|
@Override public void @Override public void initializeLine(Line line){ \{ line.addDefaultBeanSettingHandler(new AcmeLineDefaults(getPhoneContext())); }
\}
|
Here we delegate "filling-in" phone line settings for the administrator to
...
AcmeLineDefaults
...
so administrators do not have to fill them in themselves.
Panelcode |
---|
public static class AcmeLineDefaults{ private Line \{ private Line m_line; AcmeLineDefaults(Line line){ m \{ m_line = line; } \} @SettingEntry(path = USER_ID_SETTING) public String public String getUserName(){ String userName = null; User user = \{ String userName = null; User user = m_line.getUser(); if if (user != null){ userName = user.getUserName(); } return userName; } ... \{
userName = user.getUserName();
\}
return userName;
\}
...
|
This can range from the address of the voicemail server to what the magic keyword is for intercom feaurefeature. Admins will always have the opportunity to inspect and override these settings from the web UI for each phone or any phone group.
...
There are 2 ways of providing defaults for settings. In the first method you contruct construct a Java class and use Java annotations to match settings to code. Here we obtain the user id from the system user and "fill-in" the user id setting for AcmePhone. This is used for presenting values in the web ui and when generating profiles.
This uses Java annotations to associate a setting to a method call. What's nice about this approach is that you can write plain old Java code to determine your default settings and also unit test your code by creating very few additional classes.
Approach 2: Implemention Implementing SettingValueHandler'
When the settings you want to influence match a particular pattern, you should investigate implementing SettingValueHandler interface. For example, all integer settings, or setting names that end with "_xyz"
...
Phone-wide settings (as opposed to line settings) have a similiar opportunity to supply defaults by implementing
...
initialize()
...
method.
Step 4: Create Plugin Descriptor - acme-models.beans.xml
Inorder In order for the system to load support for you new phone model, you must create this descriptor file. File name must match the following wildcard expression
...
*-models.bean.xml
...
to get loaded automatically by the Spring framework.
File Format
Panelcode |
---|
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> |
You may have noticed from the DTD reference that this is a Spring Framework file. You don't need to know Spring Framework, all that you should need is described below.
Phone Model(s)
Panelcode |
---|
<bean id="acmePhoneStandard" class="org.sipfoundry.sipxconfig.phone.PhoneModel"> <property name="beanId" value="acmePhone"/> <property name="label" value="Acme"/> <property name="maxLineCount" value="5"/> </bean> |
This registers your phone with the system so users can select it as an available phone model. You may create as many of these <bean> elements as models you wish to distinguish. Every phone implementation needs at least one.
...
- id - unique identifier for one particular model of your phone. This must not change (unless you create a database patch beyond the scope of this document.)
- label - Name that appears from web UI to distinguish your phone to end user
- maxLineCount - Total number of registrations your phone can support. This is not the number of simultaneous calls, which is not captured here.
Phone Class
Panelcode |
---|
<bean id="acmePhone" class="org.sipfoundry.sipxconfig.phone.acme.AcmePhone" singleton="false" parent="abstractPhone"> </bean> |
This registers your phone implementation so the system knows how to instantiate your code.
...
From this plugin descriptor file you can gain access to other parts of the system that are of interest to your phone. For example, if your phone could browse LDAP servers, you could get access to the LDAP settings
Panelcode |
---|
<bean id="acmePhone" ...> <property name="ldapManager" ref="ldapManager"/> </bean> |
Panelcode | ||||
---|---|---|---|---|
| noformat||||
...
public setLdapManager(LdapManager ldapManager) {
m_ldapManager = ldapManager;
}
...
|
-- MichalBielicki
Q: How does one know what services are available or their names?
-- Lazyboy
A: No great way, but every part of the system is available from Spring so if it's configurable it's available somewhere.
Browse all *.beans.xml files. This also looks promising http://opensource.atlassian.com/confluence/spring/display/BDOC/Home
Step 5: Generating Profiles - config.vm
AcmePhone will generate it's its configation in the text file in the TFTP root directory. Although overkill for this simple example I will use Velocity to create a template to help generate a profile. You can implement any scheme you want including simply dumping setting values into a flat file using standard Java file IO. Velocity is a templating tool and is embedded into sipXconfig but it is not required to be used for configuration file generation. Its templating language is documented on the Velocity web site.
Here the first half of what my file looks like
Panelcode | ||||
---|---|---|---|---|
| noformat||||
#foreach ($group in $phone.Settings.Values)
[$\{group.Name\}]
#foreach ($setting in $group.Values)
$\{setting.ProfileName\}=$!\{setting.Value\}
#end
...
|
This template will copy all settings directly into the configuration file. Because of the simplicity of this method the setting groups and settings must be organized exactly how the config file breaks its settings into INI sections. The is fine for generating profiles, but may not always be helpful for the user. This is fine for now, however if this changes then settings can be filtered before passing them to Velocity or Velocity can call filter methods from java.
Step 6: Creating a unit test - AcmePhoneTest.java
Panelcode | ||
---|---|---|
| ||
No Format |
...
public void testGenerateTypicalProfile() throws Exception {
AcmePhone phone = new AcmePhone();
// call this to inject dummy data
PhoneTestDriver.supplyTestData(phone);
StringWriter actualWriter = new StringWriter();
phone.generateProfile(actualWriter);
InputStream expectedProfile = getClass().getResourceAsStream("expected-config");
String expected = IOUtils.toString(expectedProfile);
expectedProfile.close();
assertEquals(expected, actualWriter.toString());
}
...
|
This instructs your phone to generate a configuration into memory, then compares the configuration to what you expect it to be. Here we use a utility class
...
PhoneTestDriver
...
to create sample data but may create your own. Every single setting is compared, so means the unit test will have to be updated quite regularly, however if there was a problem with any of the settings, this is the best way for you and others find the issue.
...
To run my test, I go to unix or DOS prompt and run the following commands:
Panelcode |
---|
cd sipXconfig/neoconf ant -Dtest.name=AcmePhone default test |
If you get an error, you'll find results in
...
test-results/TEST-org.sipfoundry.sipxconfig.phone.acme.AcmePhoneTest.txt
All the unit tests can run from eclipse if you've set up your eclipse environment accordingly.
...
- Run coding style checker to ensure source code passed the coding conventions (standard java coding standards: http://java.sun.com/docs/codeconv/)
Panelcode |
---|
ant style |
- Run the clover coverage tool. Pingtel has a clover RPM and licence file if you don't already have clover installed. Clover's demo license should last 30 days until you can obtain one by emailing the sipx-dev list.
Panelcode |
---|
ant -Dwith.clover clean default test-all clover.viewer |
Advanced Features
Speed dials
...