public boolean logEvent( final IClient client, final String eventText, final InputStream logFileStream) { Integer id = executeWithConnection( new ConnectionExecuteFunction<Integer>() { public Integer execute(Connection c) throws SQLException { return logEvent(client, eventText, logFileStream, c); } }); if (id == null) { // DB error would have already been logged. return false; } boolean ok = false; if (logFileStream == null) { ok = true; } else { File userMailDir = client.getUserMailbox().getBaseDir(); File logFile = getEventLogFile(userMailDir, id.intValue()); try { FileUtils.copyStreamToFile(logFileStream, logFile); ok = true; } catch (IOException ioe) { m_logger .log("Can't write log file") .param(LoggingConsts.USER_ID, client.getUserId()) .param(LoggingConsts.FILENAME, logFile == null ? "" : logFile.getAbsolutePath()) .param(LoggingConsts.CONTENT, eventText) .error(ioe); } } return ok; }
/** * If 'file' exists, attempt to delete it. Logs, but doesn't throw upon failure. * * @param file * @param userId only for logging. */ private void deleteFileIfNeeded(File file, int userId) { if (file.exists()) { boolean ok = file.delete(); if (!ok) { // This is a problem because it may delay informing a client to leave // activation. LogMessageGen lmg = new LogMessageGen(); lmg.setSubject("Unable to delete file"); lmg.param(LoggingConsts.USER_ID, userId); lmg.param(LoggingConsts.FILENAME, file.getAbsolutePath()); m_logCategory.error(lmg.toString()); } } }
public CacheFileNames getCacheFileNames(final UserAccount user) { SecureHashKeyResult toRet = executeWithConnection( new ConnectionExecuteFunction<SecureHashKeyResult>() { public SecureHashKeyResult execute(Connection c) throws SQLException { return getSecureHashKey(user, c); } }); if (toRet == null) { // A DB error should have been logged. return null; } // EMSDEV-7854. Signal file manipulation is done outside of a DB transaction. String stateFile = null; String newmailFile = null; ClientHashSupport hash = null; byte[] key = toRet.getKey(); if (key != null) { hash = new ClientHashSupport(); hash.setCustomerId(user.getCustomerID()); hash.setHashKey(key); hash.setUserId(user.getUserID()); hash.getStateCachePath(); hash.setLastActivationId(user.getLastActivationId()); // Lines below don't hit the spindle. File stateFileF = new File(getCacheBase(), hash.getStateCachePath()); stateFile = stateFileF.getAbsolutePath(); File newmailFileF = new File(getCacheBase(), hash.getNewMailCachePath()); newmailFile = newmailFileF.getAbsolutePath(); if (toRet.isJustCreated()) { // If Webmail doesn't find the signal file, it polls the database, // so ensure the files are there. updateSignalFiles(hash, user.getUserState(), user.isDeleted()); } } return new CacheFileNames(stateFile, newmailFile); }
public ClientSession getClientSession(String activationToken) { if (!StringUtils.hasValue(activationToken)) { m_logCategory.warn("activationToken is required"); return null; } File file = getSessionFile(activationToken); if (!file.exists()) { // Can't assume it's an authentication problem -- session file is removed // during active->recovery transition. // If this occurs, the client gets a 401 and will call into 'transition' or 'checkin'. LogMessageGen lmg = new LogMessageGen(); lmg.setSubject("Session file not found"); lmg.param(LoggingConsts.FILENAME, file.getAbsolutePath()); m_logCategory.info(lmg.toString()); return null; } try { ClientSession clientSession = null; clientSession = new ClientSession(); clientSession.read(file); clientSession.setActivationToken(activationToken); return clientSession; } catch (IOException ioe) { // Possible if the state file was removed after the file.exists() above, // or some other problem. // info, not warning because a client could ask for this file right when // the session is being removed. The client will then do a 'checkin', // same as described above. LogMessageGen lmg = new LogMessageGen(); lmg.setSubject("Unable to read session file"); lmg.param(LoggingConsts.FILENAME, file.getAbsolutePath()); m_logCategory.info(lmg.toString(), ioe); } return null; }
/** * Doesn't write the file atomically. Logs, but doesn't throw upon failure. * * @param file can't be null. * @param userId only for logging. * @param content * @param errorLevel if true log an error level message; otherwise warning level. */ private void touchFile(File file, byte[] content, int userId, boolean errorLevel) { OutputStream out = null; try { file.getParentFile().mkdirs(); out = new FileOutputStream(file); out.write(content); out.close(); out = null; } catch (IOException ioe) { LogMessageGen lmg = new LogMessageGen(); lmg.setSubject("Unable to touch cache file"); lmg.param(LoggingConsts.USER_ID, userId); lmg.param(LoggingConsts.FILENAME, file.getAbsolutePath()); lmg.param(LoggingConsts.CONTENT, content); if (errorLevel) { m_logCategory.error(lmg.toString(), ioe); } else { m_logCategory.warn(lmg.toString(), ioe); } } finally { // normally closed above; just for leak prevention. IOUtils.safeClose(out, false, m_logCategory); } }
/** * If we'll block due to an nfs issue, let it block here before we open a DB connection. This * reduces the window where an nfs problem can hoard a connection. We don't care if exists() fails * because file calls usually create dirs lazily. */ private void blockHereIfBadNfs() { m_cacheBaseDir.exists(); }
public void purgeLogEvents(final List<SearchConstraint> constraints, final int daysToKeep) { blockHereIfBadNfs(); StringBuffer sql = new StringBuffer( "select u.mail_directory || '/logs/' || e.event_id || '.log' " + "from dat_client_log_events e, dat_user_account u " + "where event_time < current_timestamp - (? || ' days')::interval " + "and e.has_log_file != 0 " + "and e.user_id = u.object_id"); appendWhereConstraints(sql, constraints, s_propToColumnMap); m_logCategory.info("Purge event logs query is\n" + sql); StringBuffer sql2 = new StringBuffer( "delete from dat_client_log_events " + "where event_time < current_timestamp - (? || ' days')::interval"); if (!constraints.isEmpty()) { sql2.append(" and event_id in (select e.event_id from dat_client_log_events e "); appendWhere(sql2, constraints, s_propToColumnMap); sql2.append(")"); } m_logCategory.info("Purge event logs query is\n" + sql2); try { Connection c = m_txManager.getConnection(); PreparedStatement stmt = null; ResultSet rs = null; boolean needsRollback = true; try { stmt = c.prepareStatement(sql.toString()); stmt.setInt(1, daysToKeep); rs = stmt.executeQuery(); while (rs.next()) { File logFile = new File(getNfsRoot(), rs.getString(1)); // nfs usage, but DB transaction takes no locks. if (logFile.exists()) { boolean ok = logFile.delete(); if (!ok) { m_logger .log("Unable to delete") .param(LoggingConsts.FILENAME, logFile.getAbsolutePath()) .warn(); } } // file exists. } // each row // Below, no nfs usage occurs. We can use the same connection. // the 'sql delete' may use DB locks. stmt = c.prepareStatement(sql2.toString()); stmt.setInt(1, daysToKeep); stmt.executeUpdate(); c.commit(); needsRollback = false; } finally { DbUtils.safeClose(rs); DbUtils.safeClose(stmt); if (needsRollback) { c.rollback(); } m_txManager.returnConnection(c); } } catch (Exception ex) { m_logger.log("Error in purgeLogEvents").warn(ex); } }
private File getEventLogFile(File userMailDir, int id) { File logDir = new File(userMailDir, "logs"); logDir.mkdir(); return new File(logDir, id + ".log"); }
public ClientSession createClientSession(IClientInfo client, String clientType) { // Both OE and BB share the session file. Don't stomp an existing file. if (client == null || !client.isConfigured()) { throw new IllegalStateException("Client not configured."); } clientType = clientType == null ? "" : clientType; ClientSession session = null; String tmpName = null; String targetName = null; try { File targetFile = getSessionFile(client.getLastActivationToken()); targetName = targetFile.getAbsolutePath(); if (targetFile.exists()) { session = new ClientSession(); session.read(targetFile); if (session.getLastActivationId() != client.getLastActivationId() || session.getUserId() != client.getUserId()) { // Existing session file found but it's clearly not ours. // A hash collision is improbable. If this occurs, its more likely to be a // bug. We can't safely rewrite the session file because another client // might later call getClientSession() and read our email. // This is a support concern. LogMessageGen lmg = new LogMessageGen(); lmg.setSubject("Found existing session, but the content is unexpected."); lmg.param(LoggingConsts.FILENAME, targetFile.getAbsolutePath()); m_logCategory.error(lmg.toString()); throw new ClientServiceException(""); } session.setActivationToken(client.getLastActivationToken()); } // targetFile already found. if (session == null) { UserAccount user = m_userService.getUserAccount(client.getCustomerId(), client.getUserId()); session = client.createSession(user); // What if the BB and OE create a session at the same time? // This ensures a unique filename. // The client who does the last renameTo() below wins the race // and that becomes the common session file. tmpName = targetFile.getAbsolutePath() + "_tmp_" + clientType; File tmpFile = new File(tmpName); session.write(tmpFile); // If 'targetFile' already exists it'll get clobbered. // Makes the session targetFile generation atomic. boolean ok = FileUtils.clobberingRename(tmpFile, targetFile); if (!ok) { throw new IOException("Rename failed"); } } // need to create session. } catch (Exception ex) { if (ex instanceof ClientServiceException) { // We already logged it. throw (ClientServiceException) ex; } LogMessageGen lmg = new LogMessageGen(); lmg.setSubject("Unable to generate session file."); lmg.param(LoggingConsts.USER_ID, client.getUserId()); lmg.param(LoggingConsts.TEMP_NAME, tmpName); lmg.param(LoggingConsts.FILENAME, targetName); m_logCategory.error(lmg.toString(), ex); throw new ClientServiceException(""); } return session; }