├── lib
├── lombok.jar
└── apache-commons-codec-1.4.jar
├── .settings
├── org.eclipse.ltk.core.refactoring.prefs
├── org.eclipse.jst.j2ee.ejb.annotations.xdoclet.prefs
├── org.eclipse.wst.common.project.facet.core.xml
└── org.eclipse.jdt.core.prefs
├── .gitignore
├── .classpath
├── .project
├── README.md
├── src
└── BigBlueButton
│ ├── api
│ ├── BBBMeeting.java
│ └── BBBException.java
│ └── impl
│ ├── BBBModule.java
│ ├── BBBAPI.java
│ └── BaseBBBAPI.java
└── LICENSE
/lib/lombok.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bigbluebutton/bigbluebutton-api-java/HEAD/lib/lombok.jar
--------------------------------------------------------------------------------
/lib/apache-commons-codec-1.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bigbluebutton/bigbluebutton-api-java/HEAD/lib/apache-commons-codec-1.4.jar
--------------------------------------------------------------------------------
/.settings/org.eclipse.ltk.core.refactoring.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
3 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jst.j2ee.ejb.annotations.xdoclet.prefs:
--------------------------------------------------------------------------------
1 | XDOCLETBUILDERACTIVE=true
2 | XDOCLETHOME=
3 | XDOCLETUSEGLOBAL=true
4 | XDOCLETVERSION=1.2.1
5 | eclipse.preferences.version=1
6 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.common.project.facet.core.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | #*.jar # in order to include external library
15 | *.war
16 | *.ear
17 | *.zip
18 | *.tar.gz
19 | *.rar
20 |
21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
22 | hs_err_pid*
23 | /build/
24 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | bigbluebutton-api-java
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.wst.common.project.facet.core.builder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.wst.common.project.facet.core.nature
21 | org.eclipse.jdt.core.javanature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
6 | org.eclipse.jdt.core.compiler.compliance=1.8
7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
12 | org.eclipse.jdt.core.compiler.source=1.8
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Special note:
3 | 1. This project used Lombok library, if you want to deploy this project, make sure you have Lombok installed. To install, follow the following steps:
4 | - download from https://projectlombok.org/download
5 | - go to the location where the Lombar.jar was downloaded, and run "java -jar lombok.jar", change the jar file name to the name of the lombok jar file that you downloaded
6 | - after you run the previous step, a window should appear, brownse to your editor location (for eclipse, it's where eclipse.exe is located)
7 | - click install
8 | - launch Eclipse (or other IDE)
9 | - add the zomlok library to your project (right click on the properties -> Java Build Path -> Libraries -> add Extenal JARs -> select the lombok jar file that you just downloaded -> Apply and Close
10 |
11 |
--------------------------------------------------------------------------------
/src/BigBlueButton/api/BBBMeeting.java:
--------------------------------------------------------------------------------
1 | package BigBlueButton.api;
2 |
3 | import java.util.Map;
4 | import java.util.Date;
5 | import java.util.HashMap;
6 |
7 | import lombok.AccessLevel;
8 | import lombok.Getter;
9 | import lombok.Setter;
10 | import lombok.ToString;
11 |
12 | /**
13 | * Object for a BigBlueButton meeting.
14 | * @author Adrian Fish
15 | * Based on: https://github.com/sakaicontrib/bbb-tool/blob/master/api/src/java/org/sakaiproject/bbb/api/BBBMeeting.java
16 | *
17 | * Last modified by Yunkai Wang
18 | */
19 | @Getter @Setter @ToString
20 | public class BBBMeeting {
21 | private String name = null;
22 | private String meetingID;
23 | private String attendeePW = null;
24 | private String moderatorPW = null;
25 | private String dialNumber = null;
26 | private String voiceBridge = null;
27 | private String webVoice = null;
28 | private String logoutURL = null;
29 | private Boolean record = null;
30 | private Long duration = null;
31 |
32 | // user cannot directly modify this field
33 | @Setter (AccessLevel.NONE)
34 | private Map meta = new HashMap();
35 | private String moderatorOnlyMessage = null;
36 | private Boolean autoStartRecording = null;
37 | private Boolean allowStartStopRecording = null;
38 | private Boolean webcamsOnlyForModerator = null;
39 | private String logo = null;
40 | private String copyright = null;
41 | private Boolean muteOnStart = null;
42 | private String welcome = null;
43 | private Date startDate = null;
44 | private Date endDate = null;
45 |
46 | public BBBMeeting(String meetingID) {
47 | this.meetingID = meetingID;
48 | }
49 |
50 | public void addMeta(String key, String value) {
51 | meta.put(key, value);
52 | }
53 |
54 | public void removeMeta(String key) {
55 | if (meta.containsKey(key))
56 | meta.remove(key);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/BigBlueButton/api/BBBException.java:
--------------------------------------------------------------------------------
1 | package BigBlueButton.api;
2 |
3 | /**
4 | * Exception generated while communicating with BBB server
5 | *
6 | * see https://github.com/sakaicontrib/bbb-tool/blob/master/api/src/java/org/sakaiproject/bbb/api/BBBException.java
7 | */
8 | public class BBBException extends Exception {
9 | private static final long serialVersionUID = 2421100107566638321L;
10 |
11 | public static final String MESSAGEKEY_HTTPERROR = "httpError";
12 | public static final String MESSAGEKEY_NOTFOUND = "notFound";
13 | public static final String MESSAGEKEY_NOACTION = "noActionSpecified";
14 | public static final String MESSAGEKEY_IDNOTUNIQUE = "idNotUnique";
15 | public static final String MESSAGEKEY_NOTSTARTED = "notStarted";
16 | public static final String MESSAGEKEY_ALREADYENDED = "alreadyEnded";
17 | public static final String MESSAGEKEY_INTERNALERROR = "internalError";
18 | public static final String MESSAGEKEY_UNREACHABLE = "unreachableServerError";
19 | public static final String MESSAGEKEY_INVALIDRESPONSE = "invalidResponseError";
20 | public static final String MESSAGEKEY_GENERALERROR = "generalError";
21 |
22 | private String messageKey;
23 |
24 | public BBBException(String messageKey, String message, Throwable cause) {
25 | super(message, cause);
26 | this.messageKey = messageKey;
27 | }
28 |
29 | public BBBException(String messageKey, String message) {
30 | super(message);
31 | this.messageKey = messageKey;
32 | }
33 |
34 | public String getMessageKey() {
35 | return messageKey;
36 | }
37 |
38 | public void setMessageKey(String messageKey) {
39 | this.messageKey = messageKey;
40 | }
41 |
42 | public String getPrettyMessage() {
43 | String _message = getMessage();
44 | String _messageKey = getMessageKey();
45 |
46 | StringBuilder pretty = new StringBuilder();
47 | if(_message != null) {
48 | pretty.append(_message);
49 | }
50 | if(_messageKey != null && !"".equals(_messageKey.trim())) {
51 | pretty.append(" (");
52 | pretty.append(_messageKey);
53 | pretty.append(")");
54 | }
55 | return pretty.toString();
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/src/BigBlueButton/impl/BBBModule.java:
--------------------------------------------------------------------------------
1 | package BigBlueButton.impl;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileReader;
6 | import java.io.IOException;
7 | import java.util.HashMap;
8 | import java.util.LinkedList;
9 | import java.util.Map;
10 | import org.apache.commons.codec.binary.Base64;
11 |
12 | /**
13 | * Java class that stores that modules configuration to be used for create BigBlueButton meeting.
14 | * See the following url for more details
15 | * https://github.com/mconf/bigbluebutton-api-ruby/blob/master/lib/bigbluebutton_modules.rb
16 | *
17 | * @author Yunkai Wang
18 | */
19 | public class BBBModule {
20 | public enum PresentationType {
21 | url, file, base64;
22 | }
23 |
24 | private LinkedList presentation_urls = new LinkedList();
25 | private LinkedList presentation_files = new LinkedList();
26 | private Map presentation_base64s = new HashMap();
27 |
28 | public void add_presentation(PresentationType type, String value) {
29 | add_presentation(type, value, "");
30 | }
31 |
32 | public void add_presentation(PresentationType type, String value, String name) {
33 | switch (type) {
34 | case url:
35 | presentation_urls.add(value);
36 | return;
37 | case file:
38 | presentation_files.add(value);
39 | return;
40 | case base64:
41 | presentation_base64s.put(name, value);
42 | return;
43 | }
44 | }
45 |
46 | public String to_xml() throws IOException {
47 | if (!has_presentation()) return "";
48 | String xml = "";
49 | xml += presentations_to_xml();
50 | xml += "";
51 | return xml;
52 | }
53 |
54 | private String presentations_to_xml() throws IOException {
55 | String xml = "";
56 | for (String url : presentation_urls)
57 | xml += String.format("", url);
58 | for (Map.Entry entry : presentation_base64s.entrySet()) {
59 | xml += String.format("", entry.getKey());
60 | xml += entry.getValue();
61 | xml += "";
62 | }
63 |
64 | for (String fileName : presentation_files) {
65 | File f = new File(fileName);
66 | xml += String.format("", f.getName());
67 | FileReader fr = new FileReader(fileName);
68 | BufferedReader br = new BufferedReader(fr);
69 | String file = "";
70 | String line = "";
71 | while ((line = br.readLine()) != null)
72 | file += line + "\n";
73 | br.close();
74 | byte[] bytes = Base64.encodeBase64(file.getBytes());
75 | for (byte b : bytes)
76 | xml += (char)b;
77 | xml += "";
78 | }
79 | xml += "";
80 | return xml;
81 | }
82 |
83 | private boolean has_presentation() {
84 | return !(presentation_urls.isEmpty() &&
85 | presentation_files.isEmpty() &&
86 | presentation_base64s.isEmpty());
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/BigBlueButton/impl/BBBAPI.java:
--------------------------------------------------------------------------------
1 | package BigBlueButton.impl;
2 |
3 | import java.util.Map;
4 | import BigBlueButton.api.BBBException;
5 | import BigBlueButton.api.BBBMeeting;
6 |
7 | /**
8 | * List of supported API for BigBlueButton, see BigBlueButton doc for more information
9 | * Based on: https://github.com/sakaicontrib/bbb-tool/blob/master/impl/src/java/org/sakaiproject/bbb/impl/bbbapi/BBBAPI.java
10 | *
11 | * Last modified by Yunkai Wang
12 | */
13 | public interface BBBAPI {
14 |
15 | /**
16 | * Get the API version of the server
17 | *
18 | * @return API version
19 | */
20 | public String getAPIVersion();
21 |
22 | /**
23 | * Get the base url of the server
24 | *
25 | * @return url
26 | */
27 | public String getUrl();
28 |
29 | /**
30 | * Allow user to create meeting
31 | *
32 | * @return BBBMeeting object on success, on failure, BBBException is thrown
33 | * @throws BBBException
34 | */
35 | public BBBMeeting createMeeting(final String meetingID) throws BBBException;
36 | public BBBMeeting createMeeting(final BBBMeeting meeting) throws BBBException;
37 | public BBBMeeting createMeeting(final BBBMeeting meeting, final BBBModule module) throws BBBException;
38 |
39 | /**
40 | * Check if the meeting is already running
41 | *
42 | * @return true if the meeting is running, false otherwise
43 | * @throws BBBException
44 | */
45 | public boolean isMeetingRunning(String meetingID) throws BBBException;
46 |
47 | /**
48 | * Get meeting information corresponds to the given meetingID and role
49 | *
50 | * @return Map that contains all meeting information
51 | * @throws BBBException
52 | */
53 | public Map getMeetingInfo(String meetingID, String password) throws BBBException;
54 | public Map getMeetingInfo(final BBBMeeting meeting) throws BBBException;
55 |
56 | /**
57 | * End the given meeting
58 | *
59 | * @return true if the meeting is successfully ended or does not exist, false otherwise
60 | * @throws BBBException
61 | */
62 | public boolean endMeeting(String meetingID, String password) throws BBBException;
63 | public boolean endMeeting(final BBBMeeting meeting) throws BBBException;
64 |
65 | /**
66 | * Get the url to join the given meeting with the display name and corresponding role type
67 | *
68 | * @return url for joining the meeting
69 | */
70 | public String getJoinMeetingURL(String meetingID, String password, String userDisplayName);
71 | public String getJoinMeetingURL(String meetingID, String password, String userDisplayName, String userId);
72 |
73 | /**
74 | * Get the list of all live meetings in server, every parameter like meetingIDs can be a list of meetings but separated
75 | * by commas(e.g., "id1,id2")
76 | *
77 | * @return a map which has a field named meetings, and the value is the list of meeting information
78 | * @throws BBBException
79 | */
80 | public Map getMeetings() throws BBBException;
81 |
82 | /**
83 | * Get the list of recordings that map the given fields
84 | *
85 | * @return a map which has a field named recordings, and the value is the list of recording information
86 | * @throws BBBException
87 | */
88 | public Map getRecordings() throws BBBException;
89 | public Map getRecordings(String meetingIDs) throws BBBException;
90 | public Map getRecordings(String meetingIDs, String recordIDs) throws BBBException;
91 | public Map getRecordings(String meetingIDs, String recordIDs, String states) throws BBBException;
92 | public Map getRecordings(String meetingIDs, String recordIDs, String states, Map meta) throws BBBException;
93 |
94 | /**
95 | * Delete a given recording (or a list of recordings whose ids are separated by commas)
96 | *
97 | * @return true if the recording is successfully deleted, exception is thrown on failure
98 | * @throws BBBException
99 | */
100 | public boolean deteteRecordings(String recordIDs) throws BBBException;
101 |
102 | /**
103 | * Publish/unpublish the recording(s)
104 | *
105 | * @return true if the recording(s) is successfully published/unpublished, exception is thrown on failure
106 | * @throws BBBException
107 | */
108 | public boolean publishRecordings(String recordIDs, boolean publish) throws BBBException;
109 |
110 | /**
111 | * Update the recording(s)
112 | *
113 | * @return true if the recording(s) is successfully updated, exception is thrown on failure
114 | * @throws BBBException
115 | */
116 | public boolean updateRecordings(String recordIDs) throws BBBException;
117 | public boolean updateRecordings(String recordIDs, Map meta) throws BBBException;
118 |
119 | /**
120 | * Get the default config xml file from the BBB server and save it to the given file path
121 | *
122 | * @return true on success, on failure, BBBException is thrown
123 | * @throws BBBException
124 | */
125 | public boolean getDefaultConfigXML(String fileName) throws BBBException;
126 |
127 |
128 | /**
129 | * Set the config for the given meeting
130 | *
131 | * @return true on success, on failure, BBBException is thrown
132 | * @throws BBBException
133 | */
134 | public boolean setConfigXML(String meetingID, String fileName) throws BBBException;
135 | }
136 |
--------------------------------------------------------------------------------
/src/BigBlueButton/impl/BaseBBBAPI.java:
--------------------------------------------------------------------------------
1 | package BigBlueButton.impl;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.DataOutputStream;
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.io.InputStreamReader;
10 | import java.io.StringReader;
11 | import java.io.UnsupportedEncodingException;
12 | import java.net.HttpURLConnection;
13 | import java.net.URL;
14 | import java.net.URLEncoder;
15 | import java.text.ParseException;
16 | import java.text.SimpleDateFormat;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.Map.Entry;
21 | import java.util.HashMap;
22 |
23 | import javax.xml.parsers.DocumentBuilder;
24 | import javax.xml.parsers.DocumentBuilderFactory;
25 | import javax.xml.parsers.ParserConfigurationException;
26 |
27 | import BigBlueButton.api.BBBException;
28 | import BigBlueButton.api.BBBMeeting;
29 |
30 | import org.apache.commons.codec.digest.DigestUtils;
31 | import org.w3c.dom.Document;
32 | import org.w3c.dom.Node;
33 | import org.w3c.dom.NodeList;
34 | import org.xml.sax.InputSource;
35 | import org.xml.sax.SAXException;
36 |
37 | /**
38 | * Base class for interacting with any BigBlueButton API version.
39 | * @author Nuno Fernandes
40 | *
41 | * Last modified by Yunkai Wang
42 | */
43 | public class BaseBBBAPI implements BBBAPI {
44 | /**
45 | * BBB server url, including bigbluebutton webapp path. Will default to
46 | * http://localhost/bigbluebutton if not specified
47 | */
48 | protected String bbbUrl = "http://127.0.0.1/bigbluebutton";
49 | /** BBB security salt */
50 | protected String bbbSalt = null;
51 |
52 | // API Server Path
53 | protected final static String API_SERVERPATH = "/api/";
54 |
55 | // API Calls
56 | protected final static String APICALL_CREATE = "create";
57 | protected final static String APICALL_ISMEETINGRUNNING = "isMeetingRunning";
58 | protected final static String APICALL_GETMEETINGINFO = "getMeetingInfo";
59 | protected final static String APICALL_GETMEETINGS = "getMeetings";
60 | protected final static String APICALL_JOIN = "join";
61 | protected final static String APICALL_END = "end";
62 | protected final static String APICALL_VERSION = "";
63 | protected final static String APICALL_getRecordS = "getRecordings";
64 | protected final static String APICALL_PUBLISHRECORDINGS = "publishRecordings";
65 | protected final static String APICALL_UPDATERECORDINGS = "updateRecordings";
66 | protected final static String APICALL_DELETERECORDINGS = "deleteRecordings";
67 | protected final static String APICALL_GETCONFIGXML = "getDefaultConfigXML";
68 | protected final static String APICALL_SETCONFIGXML = "setConfigXML";
69 |
70 | // API Response Codes
71 | protected final static String APIRESPONSE_SUCCESS = "SUCCESS";
72 | protected final static String APIRESPONSE_FAILED = "FAILED";
73 |
74 | // API Versions
75 | public final static String APIVERSION_063 = "0.63";
76 | public final static String APIVERSION_064 = "0.64";
77 | public final static String APIVERSION_070 = "0.70";
78 | public final static String APIVERSION_080 = "0.80";
79 | public final static String APIVERSION_081 = "0.81";
80 | public final static String APIVERSION_MINIMUM = APIVERSION_063;
81 | public final static String APIVERSION_LATEST = APIVERSION_081;
82 |
83 | // -----------------------------------------------------------------------
84 | // --- Initialization related methods ------------------------------------
85 | // -----------------------------------------------------------------------
86 | public BaseBBBAPI(String url, String salt) {
87 | this.bbbUrl = url;
88 |
89 | if (bbbUrl.endsWith("/") && bbbUrl.length() > 0) {
90 | bbbUrl = bbbUrl.substring(0, bbbUrl.length() - 1);
91 | }
92 |
93 | this.bbbSalt = salt;
94 | }
95 |
96 | public String getUrl() {
97 | return this.bbbUrl;
98 | }
99 |
100 | /*
101 | public String getSalt() {
102 | return this.bbbSalt;
103 | }
104 | */
105 |
106 | private String encode(String msg) throws UnsupportedEncodingException {
107 | return URLEncoder.encode(msg, getParametersEncoding());
108 | }
109 |
110 | // -----------------------------------------------------------------------
111 | // --- BBB API implementation methods ------------------------------------
112 | // -----------------------------------------------------------------------
113 |
114 | /* Create BBB meeting */
115 | public BBBMeeting createMeeting(final String meetingID) throws BBBException {
116 | return createMeeting(new BBBMeeting(meetingID), null);
117 | }
118 |
119 | public BBBMeeting createMeeting(final BBBMeeting meeting) throws BBBException {
120 | return createMeeting(meeting, null);
121 | }
122 |
123 | public BBBMeeting createMeeting(final BBBMeeting meeting, final BBBModule module) throws BBBException {
124 | try {
125 | StringBuilder query = new StringBuilder();
126 | query.append("meetingID=" + meeting.getMeetingID());
127 | if (meeting.getName() != null)
128 | query.append("&name=" + encode(meeting.getName()));
129 | if (meeting.getAttendeePW() != null)
130 | query.append("&attendeePW=" + meeting.getAttendeePW());
131 | if (meeting.getModeratorPW() != null)
132 | query.append("&moderatorPW=" + meeting.getModeratorPW());
133 | if (meeting.getWelcome() != null)
134 | query.append("&welcome=" + encode(meeting.getWelcome()));
135 | if (meeting.getDialNumber() != null)
136 | query.append("&dialNumber=" + meeting.getDialNumber());
137 | if (meeting.getVoiceBridge() != null)
138 | query.append("&voiceBridge=" + meeting.getVoiceBridge());
139 | if (meeting.getWebVoice() != null)
140 | query.append("&webVoice=" + encode(meeting.getWebVoice()));
141 | if (meeting.getLogoutURL() != null)
142 | query.append("&logoutURL=" + encode(meeting.getLogoutURL()));
143 | if (meeting.getRecord() != null)
144 | query.append("&record=" + Boolean.toString(meeting.getRecord()));
145 | if (meeting.getDuration() != null)
146 | query.append("&duration=" + meeting.getDuration().toString());
147 | if (!meeting.getMeta().isEmpty()) {
148 | for(Entry entry : meeting.getMeta().entrySet()) {
149 | String key = entry.getKey();
150 | String value = entry.getValue();
151 | query.append("&meta_" + key + "=");
152 | query.append(encode(value));
153 | }
154 | }
155 | if (meeting.getModeratorOnlyMessage() != null)
156 | query.append("&moderatorOnlyMessage=" + encode(meeting.getModeratorOnlyMessage()));
157 | if (meeting.getAutoStartRecording() != null)
158 | query.append("&autoStartRecording=" + Boolean.toString(meeting.getAutoStartRecording()));
159 | if (meeting.getAllowStartStopRecording() != null)
160 | query.append("&allowStartStopRecording=" + Boolean.toString(meeting.getAllowStartStopRecording()));
161 | if (meeting.getWebcamsOnlyForModerator() != null)
162 | query.append("&logo=" + Boolean.toString(meeting.getWebcamsOnlyForModerator()));
163 | if (meeting.getLogo() != null)
164 | query.append("&logo=" + encode(meeting.getLogo()));
165 | if (meeting.getCopyright() != null)
166 | query.append("©right=" + encode(meeting.getCopyright()));
167 | if (meeting.getMuteOnStart() != null)
168 | query.append("&muteOnStart=" + Boolean.toString(meeting.getMuteOnStart()));
169 | query.append(getCheckSumParameterForQuery(APICALL_CREATE, query.toString()));
170 |
171 | Map response = doAPICall(APICALL_CREATE, query.toString(),
172 | module == null ? null : module.to_xml());
173 |
174 | // capture important information from returned response
175 | meeting.setModeratorPW((String)response.get("moderatorPW"));
176 | meeting.setAttendeePW((String)response.get("attendeePW"));
177 | meeting.setDialNumber((String)response.get("dialNumber"));
178 | meeting.setVoiceBridge((String)response.get("voiceBridge"));
179 | SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy");
180 | try {
181 | meeting.setStartDate(formatter.parse((String)response.get("createDate")));
182 | } catch (ParseException e) { }
183 |
184 | return meeting;
185 | } catch (BBBException e) {
186 | throw e;
187 | } catch (IOException e) {
188 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
189 | }
190 | }
191 |
192 | /* Check if meeting is running */
193 | public boolean isMeetingRunning(String meetingID)
194 | throws BBBException {
195 | try {
196 | StringBuilder query = new StringBuilder();
197 | query.append("meetingID=" + meetingID);
198 | query.append(getCheckSumParameterForQuery(APICALL_ISMEETINGRUNNING, query.toString()));
199 |
200 | Map response = doAPICall(APICALL_ISMEETINGRUNNING, query.toString());
201 | return Boolean.parseBoolean((String) response.get("running"));
202 | } catch (Exception e) {
203 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
204 | }
205 | }
206 |
207 | /* Get list of all running BBB meetings */
208 | public Map getMeetings() throws BBBException {
209 | try {
210 | StringBuilder query = new StringBuilder();
211 | query.append(getCheckSumParameterForQuery(APICALL_GETMEETINGS, query.toString()));
212 | Map response = doAPICall(APICALL_GETMEETINGS, query.toString());
213 |
214 | return response;
215 | } catch (Exception e) {
216 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
217 | }
218 | }
219 |
220 | /* Get BBB meeting information */
221 | public Map getMeetingInfo(final BBBMeeting meeting) throws BBBException {
222 | return getMeetingInfo(meeting.getMeetingID(), meeting.getModeratorPW());
223 | }
224 |
225 | public Map getMeetingInfo(String meetingID, String password)
226 | throws BBBException {
227 | try {
228 | StringBuilder query = new StringBuilder();
229 | query.append("meetingID=" + meetingID);
230 | query.append("&password=" + password);
231 | query.append(getCheckSumParameterForQuery(APICALL_GETMEETINGINFO, query.toString()));
232 | Map response = doAPICall(APICALL_GETMEETINGINFO, query.toString());
233 | return response;
234 | } catch (BBBException e) {
235 | throw new BBBException(e.getMessageKey(), e.getMessage(), e);
236 | }
237 | }
238 |
239 | /* End given BBB meeting */
240 | public boolean endMeeting(final BBBMeeting meeting) throws BBBException {
241 | return endMeeting(meeting.getMeetingID(), meeting.getModeratorPW());
242 | }
243 |
244 | public boolean endMeeting(String meetingID, String password) throws BBBException {
245 | StringBuilder query = new StringBuilder();
246 | query.append("meetingID=" + meetingID);
247 | query.append("&password=" + password);
248 | query.append(getCheckSumParameterForQuery(APICALL_END, query.toString()));
249 |
250 | try {
251 | doAPICall(APICALL_END, query.toString());
252 | } catch (BBBException e) {
253 | if(BBBException.MESSAGEKEY_NOTFOUND.equals(e.getMessageKey())) {
254 | // we can safely ignore this one: the meeting is not running
255 | return true;
256 | }else{
257 | throw e;
258 | }
259 | }
260 |
261 | return true;
262 | }
263 |
264 | /** Get recordings from BBB server */
265 | public Map getRecordings() throws BBBException {
266 | return getRecordings(null, null, null, null);
267 | }
268 |
269 | public Map getRecordings(String meetingIDs) throws BBBException {
270 | return getRecordings(meetingIDs, null, null, null);
271 | }
272 |
273 | public Map getRecordings(String meetingIDs, String recordIDs) throws BBBException {
274 | return getRecordings(meetingIDs, recordIDs, null, null);
275 | }
276 |
277 | public Map getRecordings(String meetingIDs, String recordIDs, String states) throws BBBException {
278 | return getRecordings(meetingIDs, recordIDs, states, null);
279 | }
280 |
281 | public Map getRecordings(String meetingIDs, String recordIDs, String states, Map meta) throws BBBException {
282 | try {
283 | StringBuilder query = new StringBuilder();
284 | if (meetingIDs != null)
285 | query.append("meetingID=" + meetingIDs);
286 | if (recordIDs != null)
287 | query.append("recordID=" + recordIDs);
288 | if (states != null)
289 | query.append("state=" + states);
290 | if (meta != null && meta.size() != 0) {
291 | for(Entry entry : meta.entrySet()) {
292 | String key = entry.getKey();
293 | String value = entry.getValue();
294 | query.append("&meta_" + key + "=");
295 | query.append(encode(value));
296 | }
297 | }
298 | query.append(getCheckSumParameterForQuery(APICALL_getRecordS, query.toString()));
299 | Map response = doAPICall(APICALL_getRecordS, query.toString());
300 |
301 | return response;
302 | } catch (BBBException e) {
303 | throw new BBBException(e.getMessageKey(), e.getMessage(), e);
304 | } catch (IOException e) {
305 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
306 | }
307 | }
308 |
309 | /* Detete a record from BBB server */
310 | public boolean deteteRecordings(String recordIDs) throws BBBException {
311 | StringBuilder query = new StringBuilder();
312 | query.append("recordID=" + recordIDs);
313 | query.append(getCheckSumParameterForQuery(APICALL_DELETERECORDINGS, query.toString()));
314 | try {
315 | Map response = doAPICall(APICALL_DELETERECORDINGS, query.toString());
316 | return response.get("returncode").toString().equals("SUCCESS") ? true : false;
317 | } catch (BBBException e) {
318 | throw e;
319 | }
320 | }
321 |
322 | /* Publish/Unpublish a recording on BBB server */
323 | public boolean publishRecordings(String recordIDs, boolean publish) throws BBBException {
324 | StringBuilder query = new StringBuilder();
325 | query.append("recordID=" + recordIDs);
326 | query.append("&publish=" + Boolean.toString(publish));
327 | query.append(getCheckSumParameterForQuery(APICALL_PUBLISHRECORDINGS, query.toString()));
328 |
329 | try {
330 | Map response = doAPICall(APICALL_PUBLISHRECORDINGS, query.toString());
331 | return response.get("returncode").toString().equals("SUCCESS") ? true : false;
332 | } catch (BBBException e) {
333 | throw e;
334 | }
335 | }
336 |
337 | /* Update a recording on BBB server */
338 | public boolean updateRecordings(String recordingIDs) throws BBBException {
339 | return updateRecordings(recordingIDs, null);
340 | }
341 |
342 | public boolean updateRecordings(String recordingIDs, Map meta) throws BBBException {
343 | try {
344 | StringBuilder query = new StringBuilder();
345 | query.append("recordID=" + recordingIDs);
346 | if (meta != null && meta.size() != 0) {
347 | for(Entry entry : meta.entrySet()) {
348 | String key = entry.getKey();
349 | String value = entry.getValue();
350 | query.append("&meta_" + key + "=");
351 | query.append(encode(value));
352 | }
353 | }
354 | query.append(getCheckSumParameterForQuery(APICALL_UPDATERECORDINGS, query.toString()));
355 | Map response = doAPICall(APICALL_UPDATERECORDINGS, query.toString());
356 | return response.get("returncode").toString().equals("SUCCESS") ? true : false;
357 | } catch (BBBException e) {
358 | throw e;
359 | } catch (IOException e) {
360 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
361 | }
362 | }
363 |
364 | /* Build the join meeting url based on user role */
365 | public String getJoinMeetingURL(String meetingID, String password, String userDisplayName) {
366 | return getJoinMeetingURL(meetingID, password, userDisplayName, null);
367 | }
368 |
369 | public String getJoinMeetingURL(String meetingID, String password, String userDisplayName, String userId) {
370 | StringBuilder url = null;
371 | try {
372 | StringBuilder joinQuery = new StringBuilder();
373 | joinQuery.append("meetingID=" + meetingID);
374 | if (userId != null)
375 | joinQuery.append("&userID=" + encode(userId));
376 |
377 | joinQuery.append("&fullName=");
378 | userDisplayName = (userDisplayName == null) ? "user" : userDisplayName;
379 | try {
380 | joinQuery.append(encode(userDisplayName));
381 | } catch (UnsupportedEncodingException e) {
382 | joinQuery.append(userDisplayName);
383 | }
384 | joinQuery.append("&password=" + password);
385 | joinQuery.append(getCheckSumParameterForQuery(APICALL_JOIN, joinQuery.toString()));
386 |
387 | url = new StringBuilder(bbbUrl);
388 | if (url.toString().endsWith("/api")) {
389 | url.append("/");
390 | } else {
391 | url.append(API_SERVERPATH);
392 | }
393 | url.append(APICALL_JOIN + "?" + joinQuery);
394 | } catch (UnsupportedEncodingException e) { }
395 | return url.toString();
396 | }
397 |
398 | /* Download default config xml file from the server and save the file to given file location */
399 | public boolean getDefaultConfigXML(String filePath) throws BBBException {
400 | try {
401 | StringBuilder query = new StringBuilder();
402 | query.append(getCheckSumParameterForQuery(APICALL_GETCONFIGXML, query.toString()));
403 | Map response = doAPICall(APICALL_GETCONFIGXML, query.toString());
404 |
405 | File file = new File(filePath);
406 | if (file.exists() && !file.canWrite()) {
407 | throw new IOException("Failed to edit " + filePath);
408 | } else if (!file.exists()) {
409 | if (!file.createNewFile())
410 | throw new IOException("Failed to create " + filePath);
411 | }
412 |
413 | FileOutputStream output = new FileOutputStream(file);
414 | output.write(((String)response.get("xml")).getBytes());
415 | output.close();
416 | return true;
417 | } catch (BBBException e) {
418 | throw e;
419 | } catch (IOException e) {
420 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
421 | }
422 | }
423 |
424 | /* set the config.xml file for the given meeting */
425 | public boolean setConfigXML(String meetingID, String filePath) throws BBBException {
426 | try {
427 | StringBuilder query = new StringBuilder();
428 | query.append("meetingID=" + meetingID);
429 | query.append(getCheckSumParameterForQuery(APICALL_SETCONFIGXML, query.toString()));
430 |
431 | File file = new File(filePath);
432 | if (!file.exists() || !file.canRead()) {
433 | throw new IOException("Failed to read " + filePath);
434 | }
435 |
436 | FileInputStream input = new FileInputStream(file);
437 | byte[] b = input.readAllBytes();
438 | input.close();
439 | String xml = "";
440 | for (byte a : b)
441 | xml += (char)a;
442 | query.append("&configXML=" + encode(xml));
443 |
444 | Map response = doAPICall(APICALL_SETCONFIGXML, query.toString());
445 | return response.get("returncode").toString().equals("SUCCESS") ? true : false;
446 | } catch (BBBException e) {
447 | throw e;
448 | } catch (IOException e) {
449 | throw new BBBException(BBBException.MESSAGEKEY_INTERNALERROR, e.getMessage(), e);
450 | }
451 | }
452 |
453 | /** Get the BBB API version running on BBB server */
454 | public final String getAPIVersion() {
455 | String _version = null;
456 | try {
457 | Map response = doAPICall(APICALL_VERSION, null);
458 | _version = (String) response.get("version");
459 | _version = _version != null ? _version.trim() : null;
460 | if (_version == null || Float.valueOf(_version.substring(0, 3)) < 0.0) {
461 | _version = null;
462 | }
463 | _version = _version.trim();
464 | } catch (BBBException e) {
465 | if (BBBException.MESSAGEKEY_NOACTION.equals(e.getMessageKey())) {
466 | // we are clearly connecting to BBB < 0.70 => assuming minimum
467 | // version (0.63)
468 | _version = APIVERSION_MINIMUM;
469 | } else {
470 | // something went wrong => warn user
471 | _version = null;
472 | }
473 | } catch (Exception e) {
474 | // something went wrong => warn user
475 | _version = null;
476 | }
477 | return _version;
478 | }
479 |
480 | // -----------------------------------------------------------------------
481 | // --- BBB API utility methods -------------------------------------------
482 | // -----------------------------------------------------------------------
483 | /** Compute the query string checksum based on the security salt */
484 | protected String getCheckSumParameterForQuery(String apiCall,
485 | String queryString) {
486 | if (bbbSalt != null)
487 | return "&checksum=" + DigestUtils.shaHex(apiCall + queryString + bbbSalt);
488 | else
489 | return "";
490 | }
491 |
492 | /** Encoding used when encoding url parameters */
493 | protected String getParametersEncoding() {
494 | return "UTF-8";
495 | }
496 |
497 |
498 | /* Make an API call */
499 | protected Map doAPICall(String apiCall, String query) throws BBBException {
500 | return doAPICall(apiCall, query, null);
501 | }
502 |
503 | protected Map doAPICall(String apiCall, String query, String data) throws BBBException {
504 | StringBuilder urlStr = new StringBuilder(bbbUrl);
505 | if (urlStr.toString().endsWith("/api")){
506 | urlStr.append("/");
507 | } else {
508 | urlStr.append(API_SERVERPATH);
509 | }
510 | urlStr.append(apiCall);
511 | if (query != null) {
512 | urlStr.append("?");
513 | urlStr.append(query);
514 | }
515 |
516 | try {
517 | // open connection
518 | URL url = new URL(urlStr.toString());
519 | HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
520 | httpConnection.setUseCaches(false);
521 | httpConnection.setDoOutput(true);
522 | if(data != null){
523 | httpConnection.setRequestMethod("POST");
524 | httpConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
525 | httpConnection.setRequestProperty("Content-Length", "" + data.length());
526 | httpConnection.setRequestProperty("Content-Language", "en-US");
527 | httpConnection.setDoInput(true);
528 |
529 | DataOutputStream wr = new DataOutputStream( httpConnection.getOutputStream() );
530 | wr.writeBytes (data);
531 | wr.flush();
532 | wr.close();
533 | } else {
534 | httpConnection.setRequestMethod("GET");
535 | }
536 | httpConnection.connect();
537 |
538 | int responseCode = httpConnection.getResponseCode();
539 | if (responseCode == HttpURLConnection.HTTP_OK) {
540 | // read response
541 | InputStreamReader isr = null;
542 | BufferedReader reader = null;
543 | StringBuilder xml = new StringBuilder();
544 | try {
545 | isr = new InputStreamReader(httpConnection.getInputStream(), "UTF-8");
546 | reader = new BufferedReader(isr);
547 | String line = reader.readLine();
548 | while (line != null) {
549 | if( !line.startsWith(""))
550 | xml.append(line.trim());
551 | line = reader.readLine();
552 | }
553 | } finally {
554 | if (reader != null)
555 | reader.close();
556 | if (isr != null)
557 | isr.close();
558 | }
559 | httpConnection.disconnect();
560 |
561 | // parse response
562 | //Patch to fix the NaN error
563 | String stringXml = xml.toString();
564 | stringXml = stringXml.replaceAll(">.\\s+?<", "><");
565 |
566 | if (apiCall.equals(APICALL_GETCONFIGXML)) {
567 | Map map = new HashMap();
568 | map.put("xml", stringXml);
569 | return map;
570 | }
571 |
572 | Document dom = null;
573 |
574 | // Initialize XML libraries
575 | DocumentBuilderFactory docBuilderFactory;
576 | DocumentBuilder docBuilder;
577 | docBuilderFactory = DocumentBuilderFactory.newInstance();
578 | try {
579 | docBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
580 | docBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
581 |
582 | docBuilder = docBuilderFactory.newDocumentBuilder();
583 | dom = docBuilder.parse(new InputSource( new StringReader(stringXml)));
584 | } catch (ParserConfigurationException e) { }
585 | Map response = getNodesAsMap(dom, "response");
586 |
587 | String returnCode = (String) response.get("returncode");
588 | if (APIRESPONSE_FAILED.equals(returnCode)) {
589 | throw new BBBException((String) response.get("messageKey"), (String) response.get("message"));
590 | }
591 |
592 | return response;
593 | } else {
594 | throw new BBBException(BBBException.MESSAGEKEY_HTTPERROR, "BBB server responded with HTTP status code " + responseCode);
595 | }
596 |
597 | } catch(BBBException e) {
598 | throw new BBBException( e.getMessageKey(), e.getMessage(), e);
599 | } catch(IOException e) {
600 | throw new BBBException(BBBException.MESSAGEKEY_UNREACHABLE, e.getMessage(), e);
601 | } catch(SAXException e) {
602 | throw new BBBException(BBBException.MESSAGEKEY_INVALIDRESPONSE, e.getMessage(), e);
603 | } catch(IllegalArgumentException e) {
604 | throw new BBBException(BBBException.MESSAGEKEY_INVALIDRESPONSE, e.getMessage(), e);
605 | } catch(Exception e) {
606 | throw new BBBException(BBBException.MESSAGEKEY_UNREACHABLE, e.getMessage(), e);
607 | }
608 | }
609 |
610 |
611 | // -----------------------------------------------------------------------
612 | // --- BBB Other utility methods -----------------------------------------
613 | // -----------------------------------------------------------------------
614 | /** Get all nodes under the specified element tag name as a Java map */
615 | protected Map getNodesAsMap(Document dom, String elementTagName) {
616 | Node firstNode = dom.getElementsByTagName(elementTagName).item(0);
617 | return processNode(firstNode);
618 | }
619 |
620 | protected Map processNode(Node _node) {
621 | Map map = new HashMap();
622 | NodeList responseNodes = _node.getChildNodes();
623 | int images = 1; //counter for images (i.e image1, image2, image3)
624 | for (int i = 0; i < responseNodes.getLength(); i++) {
625 | Node node = responseNodes.item(i);
626 | String nodeName = node.getNodeName().trim();
627 | if (node.getChildNodes().getLength() == 1
628 | && ( node.getChildNodes().item(0).getNodeType() == org.w3c.dom.Node.TEXT_NODE || node.getChildNodes().item(0).getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE) ) {
629 | String nodeValue = node.getTextContent();
630 | if (nodeName == "image" && node.getAttributes() != null){
631 | Map imageMap = new HashMap();
632 | Node heightAttr = node.getAttributes().getNamedItem("height");
633 | Node widthAttr = node.getAttributes().getNamedItem("width");
634 | Node altAttr = node.getAttributes().getNamedItem("alt");
635 |
636 | imageMap.put("height", heightAttr.getNodeValue());
637 | imageMap.put("width", widthAttr.getNodeValue());
638 | imageMap.put("title", altAttr.getNodeValue());
639 | imageMap.put("url", nodeValue);
640 | map.put(nodeName + images, imageMap);
641 | images++;
642 | } else {
643 | map.put(nodeName, nodeValue != null ? nodeValue.trim() : null);
644 | }
645 | } else if (node.getChildNodes().getLength() == 0
646 | && node.getNodeType() != org.w3c.dom.Node.TEXT_NODE
647 | && node.getNodeType() != org.w3c.dom.Node.CDATA_SECTION_NODE) {
648 | map.put(nodeName, "");
649 | } else if (node.getChildNodes().getLength() >= 1) {
650 | boolean isList = false;
651 | for (int c = 0; c < node.getChildNodes().getLength(); ++c) {
652 | try {
653 | Node n = node.getChildNodes().item(c);
654 | if (n.getChildNodes().item(0).getNodeType() != org.w3c.dom.Node.TEXT_NODE
655 | && n.getChildNodes().item(0).getNodeType() != org.w3c.dom.Node.CDATA_SECTION_NODE) {
656 | isList = true;
657 | break;
658 | }
659 | } catch (Exception e) {
660 | continue;
661 | }
662 | }
663 | List