001: /**
002: * $Revision$
003: * $Date$
004: *
005: * Copyright (C) 2007 Jive Software. All rights reserved.
006: *
007: * This software is published under the terms of the GNU Public License (GPL),
008: * a copy of which is included in this distribution.
009: */package org.jivesoftware.openfire.gateway.avatars;
010:
011: import org.apache.log4j.Logger;
012: import org.jivesoftware.database.DbConnectionManager;
013: import org.jivesoftware.util.Base64;
014: import org.jivesoftware.util.NotFoundException;
015: import org.jivesoftware.util.StringUtils;
016: import org.jivesoftware.util.lock.LockManager;
017: import org.xmpp.packet.JID;
018:
019: import java.io.ByteArrayInputStream;
020: import java.security.MessageDigest;
021: import java.security.NoSuchAlgorithmException;
022: import java.sql.Connection;
023: import java.sql.PreparedStatement;
024: import java.sql.ResultSet;
025: import java.sql.SQLException;
026: import java.util.Date;
027: import java.util.concurrent.locks.Lock;
028:
029: /**
030: * @author Daniel Henninger
031: */
032: public class Avatar {
033:
034: static Logger Log = Logger.getLogger(Avatar.class);
035:
036: private static final String INSERT_AVATAR = "INSERT INTO gatewayAvatars(jid, xmppHash, legacyIdentifier, createDate, lastUpdate, imageType, imageData) "
037: + "VALUES (?,?,?,?,?,?,?)";
038: private static final String DELETE_AVATAR = "DELETE FROM gatewayAvatars WHERE jid=?";
039: private static final String LOAD_AVATAR = "SELECT xmppHash, legacyIdentifier, createDate, lastUpdate, imageType "
040: + "FROM gatewayAvatars WHERE jid=?";
041: private static final String RETRIEVE_IMAGE = "SELECT imageData FROM gatewayAvatars WHERE jid=?";
042: private static final String UPDATE_LEGACY_ID = "UPDATE gatewayAvatars SET legacyIdentifier=? WHERE jid=?";
043:
044: private JID jid;
045: private String xmppHash;
046: private String legacyIdentifier;
047: private Date createDate;
048: private Date lastUpdate;
049: private String mimeType;
050:
051: /**
052: * Creates a new avatar entry.
053: *
054: * @param jid JID of the avatar.
055: * @param imageData Binary image data.
056: * @throws IllegalArgumentException if any of the arguments are null.
057: */
058: public Avatar(JID jid, byte[] imageData)
059: throws IllegalArgumentException {
060: if (jid == null || imageData == null) {
061: throw new IllegalArgumentException(
062: "Avatar: Passed null argument.");
063: }
064: this .jid = jid;
065: try {
066: MessageDigest md = MessageDigest.getInstance("SHA-1");
067: md.update(imageData);
068: this .xmppHash = StringUtils.encodeHex(md.digest());
069: } catch (NoSuchAlgorithmException e) {
070: Log
071: .error("Avatar: Unable to find support for SHA algorithm?");
072: }
073: this .createDate = new Date();
074: this .lastUpdate = new Date();
075: ImageInfo imageInfo = new ImageInfo();
076: imageInfo.setInput(new ByteArrayInputStream(imageData));
077: this .mimeType = imageInfo.getMimeType();
078:
079: try {
080: insertIntoDb(Base64.encodeBytes(imageData));
081: } catch (SQLException e) {
082: Log.error("Avatar: SQL exception while inserting avatar: ",
083: e);
084: }
085: }
086:
087: /**
088: * Creates a new avatar entry.
089: *
090: * @param jid JID of the avatar.
091: * @param legacyIdentifier Hash or whatever is necessary to identify the avatar on the legacy network.
092: * @param imageData Binary image data.
093: * @throws IllegalArgumentException if any of the arguments are null.
094: */
095: public Avatar(JID jid, String legacyIdentifier, byte[] imageData)
096: throws IllegalArgumentException {
097: if (jid == null || legacyIdentifier == null
098: || imageData == null) {
099: throw new IllegalArgumentException(
100: "Avatar: Passed null argument.");
101: }
102: this .jid = jid;
103: this .legacyIdentifier = legacyIdentifier;
104: try {
105: MessageDigest md = MessageDigest.getInstance("SHA-1");
106: md.update(imageData);
107: this .xmppHash = StringUtils.encodeHex(md.digest());
108: } catch (NoSuchAlgorithmException e) {
109: Log
110: .error("Avatar: Unable to find support for SHA algorithm?");
111: }
112: this .createDate = new Date();
113: this .lastUpdate = new Date();
114: ImageInfo imageInfo = new ImageInfo();
115: imageInfo.setInput(new ByteArrayInputStream(imageData));
116: this .mimeType = imageInfo.getMimeType();
117: try {
118: insertIntoDb(Base64.encodeBytes(imageData));
119: } catch (SQLException e) {
120: Log.error("Avatar: SQL exception while inserting avatar: ",
121: e);
122: }
123: }
124:
125: /**
126: * Loads an existing avatar.
127: *
128: * @param jid JID of the contact whose avatar we are retrieving.
129: * @throws NotFoundException if avatar entry was not found in database.
130: */
131: public Avatar(JID jid) throws NotFoundException {
132: this .jid = jid;
133: loadFromDb();
134: Log.debug("Loaded avatar for " + this .jid + " of hash "
135: + this .xmppHash);
136: }
137:
138: /**
139: * Returns the JID of the avatar.
140: *
141: * @return JID of avatar.
142: */
143: public JID getJid() {
144: return jid;
145: }
146:
147: /**
148: * Returns the XMPP hash (sha1).
149: *
150: * @return SHA1 based XMPP hash.
151: */
152: public String getXmppHash() {
153: return xmppHash;
154: }
155:
156: /**
157: * Returns the legacy identifier of the avatar.
158: *
159: * This is completely up to the transport and is generally whatever type of hash is used on their end.
160: *
161: * @return Legacy identifier for the avatar.
162: */
163: public String getLegacyIdentifier() {
164: return legacyIdentifier;
165: }
166:
167: /**
168: * Sets the legacy identifier of the avatar.
169: *
170: * This is typically used for setting your own avatar information on the legacy service,
171: * and called after the avatar is set to store the known legacy identifier, if there is one,
172: * associated with your avatar.
173: *
174: * @param identifier Identifier to store.
175: */
176: public void setLegacyIdentifier(String identifier) {
177: this .legacyIdentifier = identifier;
178: Connection con = null;
179: PreparedStatement pstmt = null;
180: boolean abortTransaction = false;
181: try {
182: con = DbConnectionManager.getTransactionConnection();
183: pstmt = con.prepareStatement(UPDATE_LEGACY_ID);
184: pstmt.setString(1, jid.toString());
185: pstmt.setString(2, legacyIdentifier);
186: pstmt.executeUpdate();
187: } catch (SQLException sqle) {
188: abortTransaction = true;
189: Log
190: .error(
191: "Avatar: Major SQL error while updating legacy identifier: ",
192: sqle);
193: } finally {
194: DbConnectionManager.closeTransactionConnection(pstmt, con,
195: abortTransaction);
196: }
197: }
198:
199: /**
200: * Returns the creation date of the avatar in the database.
201: *
202: * @return Creation date of the avatar.
203: */
204: public Date getCreateDate() {
205: return createDate;
206: }
207:
208: /**
209: * Returns the date the avatar was last updated.
210: *
211: * @return Last update date of the avatar.
212: */
213: public Date getLastUpdate() {
214: return lastUpdate;
215: }
216:
217: /**
218: * Returns the mime type of the image that is stored.
219: *
220: * @return Mime type of the avatar image.
221: */
222: public String getMimeType() {
223: return mimeType;
224: }
225:
226: /**
227: * Retrieves the actual image data for this avatar.
228: *
229: * @return The base64 encoded image data for the avatar.
230: * @throws NotFoundException if avatar entry was not found in database.
231: */
232: public String getImageData() throws NotFoundException {
233: Connection con = null;
234: PreparedStatement pstmt = null;
235: ResultSet rs = null;
236: String imageData = null;
237: try {
238: con = DbConnectionManager.getConnection();
239: pstmt = con.prepareStatement(RETRIEVE_IMAGE);
240: pstmt.setString(1, jid.toString());
241: rs = pstmt.executeQuery();
242: if (!rs.next()) {
243: throw new NotFoundException("Avatar not found for "
244: + jid);
245: }
246: imageData = rs.getString(1);
247: } catch (SQLException sqle) {
248: Log.error(sqle);
249: } finally {
250: DbConnectionManager.closeConnection(rs, pstmt, con);
251: }
252: return imageData;
253: }
254:
255: /**
256: * Inserts a new avaar into the database.
257: *
258: * @param imageData Base64 encoded image data to be stored in database.
259: * @throws SQLException if the SQL statement is wrong for whatever reason.
260: */
261: private void insertIntoDb(String imageData) throws SQLException {
262: Connection con = null;
263: PreparedStatement pstmt = null;
264: boolean abortTransaction = false;
265: Lock l = LockManager.getLock("avatar" + jid.toString());
266: try {
267: l.lock();
268: con = DbConnectionManager.getTransactionConnection();
269: pstmt = con.prepareStatement(DELETE_AVATAR);
270: pstmt.setString(1, jid.toString());
271: pstmt.executeUpdate();
272: pstmt = con.prepareStatement(INSERT_AVATAR);
273: pstmt.setString(1, jid.toString());
274: pstmt.setString(2, xmppHash);
275: pstmt.setString(3, legacyIdentifier);
276: pstmt.setLong(4, createDate.getTime());
277: pstmt.setLong(5, lastUpdate.getTime());
278: pstmt.setString(6, mimeType);
279: pstmt.setString(7, imageData);
280: pstmt.executeUpdate();
281: } catch (SQLException sqle) {
282: abortTransaction = true;
283: throw sqle;
284: } finally {
285: l.unlock();
286: DbConnectionManager.closeTransactionConnection(pstmt, con,
287: abortTransaction);
288: }
289: }
290:
291: /**
292: * Load avatar from database.
293: *
294: * @throws NotFoundException if avatar entry was not found in database.
295: */
296: private void loadFromDb() throws NotFoundException {
297: Connection con = null;
298: PreparedStatement pstmt = null;
299: ResultSet rs = null;
300: try {
301: con = DbConnectionManager.getConnection();
302: pstmt = con.prepareStatement(LOAD_AVATAR);
303: pstmt.setString(1, jid.toString());
304: rs = pstmt.executeQuery();
305: if (!rs.next()) {
306: throw new NotFoundException("Avatar not found for "
307: + jid);
308: }
309: this .xmppHash = rs.getString(1);
310: this .legacyIdentifier = rs.getString(2);
311: this .createDate = new Date(rs.getLong(3));
312: this .lastUpdate = new Date(rs.getLong(4));
313: this .mimeType = rs.getString(5);
314: } catch (SQLException sqle) {
315: Log.error(sqle);
316: } finally {
317: DbConnectionManager.closeConnection(rs, pstmt, con);
318: }
319: }
320:
321: }
|