JDBCGroupProvider.java 10.7 KB
Newer Older
1
/*
2
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
3
 *
4 5 6 7 8 9 10 11 12 13 14
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
Matt Tucker's avatar
Matt Tucker committed
15 16
 */

17
package org.jivesoftware.openfire.group;
Matt Tucker's avatar
Matt Tucker committed
18

19 20 21 22 23
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
24 25 26
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Matt Tucker's avatar
Matt Tucker committed
27

28 29 30 31 32 33 34
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;

Matt Tucker's avatar
Matt Tucker committed
35 36 37
/**
 * The JDBC group provider allows you to use an external database to define the make up of groups.
 * It is best used with the JDBCAuthProvider to provide integration between your external system and
38
 * Openfire.  All data is treated as read-only so any set operations will result in an exception.
Matt Tucker's avatar
Matt Tucker committed
39
 *
40
 * To enable this provider, set the following in the system properties:
Matt Tucker's avatar
Matt Tucker committed
41
 *
42 43 44
 * <ul>
 * <li><tt>provider.group.className = org.jivesoftware.openfire.group.JDBCGroupProvider</tt></li>
 * </ul>
Matt Tucker's avatar
Matt Tucker committed
45 46 47
 *
 * Then you need to set your driver, connection string and SQL statements:
 *
48 49 50 51 52 53 54 55 56 57
 * <ul>
 * <li><tt>jdbcProvider.driver = com.mysql.jdbc.Driver</tt></li>
 * <li><tt>jdbcProvider.connectionString = jdbc:mysql://localhost/dbname?user=username&amp;password=secret</tt></li>
 * <li><tt>jdbcGroupProvider.groupCountSQL = SELECT count(*) FROM myGroups</tt></li>
 * <li><tt>jdbcGroupProvider.allGroupsSQL = SELECT groupName FROM myGroups</tt></li>
 * <li><tt>jdbcGroupProvider.userGroupsSQL = SELECT groupName FORM myGroupUsers WHERE username=?</tt></li>
 * <li><tt>jdbcGroupProvider.descriptionSQL = SELECT groupDescription FROM myGroups WHERE groupName=?</tt></li>
 * <li><tt>jdbcGroupProvider.loadMembersSQL = SELECT username FORM myGroupUsers WHERE groupName=? AND isAdmin='N'</tt></li>
 * <li><tt>jdbcGroupProvider.loadAdminsSQL = SELECT username FORM myGroupUsers WHERE groupName=? AND isAdmin='Y'</tt></li>
 * </ul>
Matt Tucker's avatar
Matt Tucker committed
58
 *
59 60 61 62 63 64 65
 * In order to use the configured JDBC connection provider do not use a JDBC
 * connection string, set the following property
 *
 * <ul>
 * <li><tt>jdbcGroupProvider.useConnectionProvider = true</tt></li>
 * </ul>
 *
Matt Tucker's avatar
Matt Tucker committed
66 67
 * @author David Snopek
 */
68
public class JDBCGroupProvider extends AbstractGroupProvider {
Matt Tucker's avatar
Matt Tucker committed
69

70 71
	private static final Logger Log = LoggerFactory.getLogger(JDBCGroupProvider.class);

72
    private String connectionString;
Matt Tucker's avatar
Matt Tucker committed
73 74 75 76 77 78 79

    private String groupCountSQL;
    private String descriptionSQL;
    private String allGroupsSQL;
    private String userGroupsSQL;
    private String loadMembersSQL;
    private String loadAdminsSQL;
80
    private boolean useConnectionProvider;
Matt Tucker's avatar
Matt Tucker committed
81

82 83
    private XMPPServer server = XMPPServer.getInstance();  

Matt Tucker's avatar
Matt Tucker committed
84 85 86 87
    /**
     * Constructor of the JDBCGroupProvider class.
     */
    public JDBCGroupProvider() {
88 89 90 91 92 93 94 95 96 97
        // Convert XML based provider setup to Database based
        JiveGlobals.migrateProperty("jdbcProvider.driver");
        JiveGlobals.migrateProperty("jdbcProvider.connectionString");
        JiveGlobals.migrateProperty("jdbcGroupProvider.groupCountSQL");
        JiveGlobals.migrateProperty("jdbcGroupProvider.allGroupsSQL");
        JiveGlobals.migrateProperty("jdbcGroupProvider.userGroupsSQL");
        JiveGlobals.migrateProperty("jdbcGroupProvider.descriptionSQL");
        JiveGlobals.migrateProperty("jdbcGroupProvider.loadMembersSQL");
        JiveGlobals.migrateProperty("jdbcGroupProvider.loadAdminsSQL");

98 99 100 101 102 103 104 105 106 107 108 109 110
        useConnectionProvider = JiveGlobals.getBooleanProperty("jdbcGroupProvider.useConnectionProvider");

        if (!useConnectionProvider) {
            // Load the JDBC driver and connection string.
            String jdbcDriver = JiveGlobals.getProperty("jdbcProvider.driver");
            try {
                Class.forName(jdbcDriver).newInstance();
            }
            catch (Exception e) {
                Log.error("Unable to load JDBC driver: " + jdbcDriver, e);
                return;
            }
            connectionString = JiveGlobals.getProperty("jdbcProvider.connectionString");
Matt Tucker's avatar
Matt Tucker committed
111 112
        }

113
        // Load SQL statements
114 115 116 117 118 119
        groupCountSQL = JiveGlobals.getProperty("jdbcGroupProvider.groupCountSQL");
        allGroupsSQL = JiveGlobals.getProperty("jdbcGroupProvider.allGroupsSQL");
        userGroupsSQL = JiveGlobals.getProperty("jdbcGroupProvider.userGroupsSQL");
        descriptionSQL = JiveGlobals.getProperty("jdbcGroupProvider.descriptionSQL");
        loadMembersSQL = JiveGlobals.getProperty("jdbcGroupProvider.loadMembersSQL");
        loadAdminsSQL = JiveGlobals.getProperty("jdbcGroupProvider.loadAdminsSQL");
Matt Tucker's avatar
Matt Tucker committed
120 121
    }

122 123 124 125 126 127
    private Connection getConnection() throws SQLException {
        if (useConnectionProvider)
            return DbConnectionManager.getConnection();
        return DriverManager.getConnection(connectionString);
    }

128
    @Override
Matt Tucker's avatar
Matt Tucker committed
129 130 131
    public Group getGroup(String name) throws GroupNotFoundException {
        String description = null;

132
        Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
133
        PreparedStatement pstmt = null;
134
        ResultSet rs = null;
Matt Tucker's avatar
Matt Tucker committed
135
        try {
136
            con = getConnection();
137
            pstmt = con.prepareStatement(descriptionSQL);
Matt Tucker's avatar
Matt Tucker committed
138
            pstmt.setString(1, name);
139
            rs = pstmt.executeQuery();
Matt Tucker's avatar
Matt Tucker committed
140 141 142 143 144 145 146
            if (!rs.next()) {
                throw new GroupNotFoundException("Group with name "
                        + name + " not found.");
            }
            description = rs.getString(1);
        }
        catch (SQLException e) {
147
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
148 149
        }
        finally {
150
            DbConnectionManager.closeConnection(rs, pstmt, con);
Matt Tucker's avatar
Matt Tucker committed
151 152 153 154 155 156 157
        }
        Collection<JID> members = getMembers(name, false);
        Collection<JID> administrators = getMembers(name, true);
        return new Group(name, description, members, administrators);
    }

    private Collection<JID> getMembers(String groupName, boolean adminsOnly) {
158
        List<JID> members = new ArrayList<>();
159 160

        Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
161
        PreparedStatement pstmt = null;
162
        ResultSet rs = null;
Matt Tucker's avatar
Matt Tucker committed
163
        try {
164
            con = getConnection();
Matt Tucker's avatar
Matt Tucker committed
165 166 167 168
            if (adminsOnly) {
                if (loadAdminsSQL == null) {
                    return members;
                }
169
                pstmt = con.prepareStatement(loadAdminsSQL);
Matt Tucker's avatar
Matt Tucker committed
170 171
            }
            else {
172
                pstmt = con.prepareStatement(loadMembersSQL);
Matt Tucker's avatar
Matt Tucker committed
173 174 175
            }

            pstmt.setString(1, groupName);
176
            rs = pstmt.executeQuery();
Matt Tucker's avatar
Matt Tucker committed
177 178 179
            while (rs.next()) {
                String user = rs.getString(1);
                if (user != null) {
180 181 182 183 184 185 186
                    JID userJID;
                    if (user.contains("@")) {
                        userJID = new JID(user);
                    }
                    else {
                        userJID = server.createJID(user, null); 
                    }
Matt Tucker's avatar
Matt Tucker committed
187 188 189 190 191
                    members.add(userJID);
                }
            }
        }
        catch (SQLException e) {
192
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
193 194
        }
        finally {
195
            DbConnectionManager.closeConnection(rs, pstmt, con);
Matt Tucker's avatar
Matt Tucker committed
196 197 198 199
        }
        return members;
    }

200
    @Override
Matt Tucker's avatar
Matt Tucker committed
201 202
    public int getGroupCount() {
        int count = 0;
203
        Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
204
        PreparedStatement pstmt = null;
205
        ResultSet rs = null;
Matt Tucker's avatar
Matt Tucker committed
206
        try {
207
            con = getConnection();
208 209
            pstmt = con.prepareStatement(groupCountSQL);
            rs = pstmt.executeQuery();
Matt Tucker's avatar
Matt Tucker committed
210 211 212 213 214
            if (rs.next()) {
                count = rs.getInt(1);
            }
        }
        catch (SQLException e) {
215
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
216 217
        }
        finally {
218
            DbConnectionManager.closeConnection(rs, pstmt, con);
Matt Tucker's avatar
Matt Tucker committed
219 220 221 222
        }
        return count;
    }

223
    @Override
224
    public Collection<String> getGroupNames() {
225
        List<String> groupNames = new ArrayList<>();
226
        Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
227
        PreparedStatement pstmt = null;
228
        ResultSet rs = null;
Matt Tucker's avatar
Matt Tucker committed
229
        try {
230
            con = getConnection();
231 232
            pstmt = con.prepareStatement(allGroupsSQL);
            rs = pstmt.executeQuery();
Matt Tucker's avatar
Matt Tucker committed
233 234 235 236 237
            while (rs.next()) {
                groupNames.add(rs.getString(1));
            }
        }
        catch (SQLException e) {
238
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
239 240
        }
        finally {
241
            DbConnectionManager.closeConnection(rs, pstmt, con);
Matt Tucker's avatar
Matt Tucker committed
242
        }
243
        return groupNames;
Matt Tucker's avatar
Matt Tucker committed
244 245
    }

246
    @Override
247
    public Collection<String> getGroupNames(int start, int num) {
248
        List<String> groupNames = new ArrayList<>();
249
        Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
250
        PreparedStatement pstmt = null;
251
        ResultSet rs = null;
Matt Tucker's avatar
Matt Tucker committed
252
        try {
253
            con = getConnection();
254 255
            pstmt = DbConnectionManager.createScrollablePreparedStatement(con, allGroupsSQL);
            rs = pstmt.executeQuery();
Matt Tucker's avatar
Matt Tucker committed
256 257 258 259 260 261 262 263
            DbConnectionManager.scrollResultSet(rs, start);
            int count = 0;
            while (rs.next() && count < num) {
                groupNames.add(rs.getString(1));
                count++;
            }
        }
        catch (SQLException e) {
264
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
265 266
        }
        finally {
267
            DbConnectionManager.closeConnection(rs, pstmt, con);
Matt Tucker's avatar
Matt Tucker committed
268
        }
269
        return groupNames;
Matt Tucker's avatar
Matt Tucker committed
270 271
    }

272
    @Override
273
    public Collection<String> getGroupNames(JID user) {
274
        List<String> groupNames = new ArrayList<>();
275
        Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
276
        PreparedStatement pstmt = null;
277
        ResultSet rs = null;
Matt Tucker's avatar
Matt Tucker committed
278
        try {
279
            con = getConnection();
280
            pstmt = con.prepareStatement(userGroupsSQL);
281
            pstmt.setString(1, server.isLocal(user) ? user.getNode() : user.toString());
282
            rs = pstmt.executeQuery();
Matt Tucker's avatar
Matt Tucker committed
283 284 285 286 287
            while (rs.next()) {
                groupNames.add(rs.getString(1));
            }
        }
        catch (SQLException e) {
288
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
289 290
        }
        finally {
291
            DbConnectionManager.closeConnection(rs, pstmt, con);
Matt Tucker's avatar
Matt Tucker committed
292
        }
293
        return groupNames;
Matt Tucker's avatar
Matt Tucker committed
294
    }
295
}