001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.verify;
016    
017    import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
018    import com.liferay.portal.kernel.concurrent.ThrowableAwareRunnable;
019    import com.liferay.portal.kernel.dao.jdbc.AutoBatchPreparedStatementUtil;
020    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.security.auth.FullNameGenerator;
024    import com.liferay.portal.kernel.security.auth.FullNameGeneratorFactory;
025    import com.liferay.portal.kernel.util.LoggingTimer;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.verify.model.VerifiableAuditedModel;
029    
030    import java.sql.Connection;
031    import java.sql.PreparedStatement;
032    import java.sql.ResultSet;
033    import java.sql.SQLException;
034    import java.sql.Timestamp;
035    
036    import java.util.ArrayList;
037    import java.util.Collection;
038    import java.util.List;
039    import java.util.Map;
040    
041    /**
042     * @author Michael C. Han
043     * @author Shinn Lok
044     */
045    public class VerifyAuditedModel extends VerifyProcess {
046    
047            public void verify(VerifiableAuditedModel... verifiableAuditedModels)
048                    throws Exception {
049    
050                    List<String> unverifiedTableNames = new ArrayList<>();
051    
052                    for (VerifiableAuditedModel verifiableAuditedModel :
053                                    verifiableAuditedModels) {
054    
055                            unverifiedTableNames.add(verifiableAuditedModel.getTableName());
056                    }
057    
058                    List<VerifyAuditedModelRunnable> verifyAuditedModelRunnables =
059                            new ArrayList<>(unverifiedTableNames.size());
060    
061                    while (!unverifiedTableNames.isEmpty()) {
062                            int count = unverifiedTableNames.size();
063    
064                            for (VerifiableAuditedModel verifiableAuditedModel :
065                                            verifiableAuditedModels) {
066    
067                                    if (unverifiedTableNames.contains(
068                                                    verifiableAuditedModel.getJoinByTableName()) ||
069                                            !unverifiedTableNames.contains(
070                                                    verifiableAuditedModel.getTableName())) {
071    
072                                            continue;
073                                    }
074    
075                                    VerifyAuditedModelRunnable verifyAuditedModelRunnable =
076                                            new VerifyAuditedModelRunnable(verifiableAuditedModel);
077    
078                                    verifyAuditedModelRunnables.add(verifyAuditedModelRunnable);
079    
080                                    unverifiedTableNames.remove(
081                                            verifiableAuditedModel.getTableName());
082                            }
083    
084                            if (unverifiedTableNames.size() == count) {
085                                    throw new VerifyException(
086                                            "Circular dependency detected " + unverifiedTableNames);
087                            }
088                    }
089    
090                    doVerify(verifyAuditedModelRunnables);
091            }
092    
093            @Override
094            protected void doVerify() throws Exception {
095                    Map<String, VerifiableAuditedModel> verifiableAuditedModelsMap =
096                            PortalBeanLocatorUtil.locate(VerifiableAuditedModel.class);
097    
098                    Collection<VerifiableAuditedModel> verifiableAuditedModels =
099                            verifiableAuditedModelsMap.values();
100    
101                    verify(
102                            verifiableAuditedModels.toArray(
103                                    new VerifiableAuditedModel[verifiableAuditedModels.size()]));
104            }
105    
106            protected Object[] getAuditedModelArray(
107                            Connection con, String tableName, String pkColumnName, long primKey,
108                            boolean allowAnonymousUser, long previousUserId)
109                    throws Exception {
110    
111                    try (PreparedStatement ps = con.prepareStatement(
112                                    "select companyId, userId, createDate, modifiedDate from " +
113                                            tableName + " where " + pkColumnName + " = ?")) {
114    
115                            ps.setLong(1, primKey);
116    
117                            try (ResultSet rs = ps.executeQuery()) {
118                                    if (rs.next()) {
119                                            long companyId = rs.getLong("companyId");
120    
121                                            long userId = 0;
122                                            String userName = null;
123    
124                                            if (allowAnonymousUser) {
125                                                    userId = previousUserId;
126                                                    userName = "Anonymous";
127                                            }
128                                            else {
129                                                    userId = rs.getLong("userId");
130                                                    userName = getUserName(con, userId);
131                                            }
132    
133                                            Timestamp createDate = rs.getTimestamp("createDate");
134                                            Timestamp modifiedDate = rs.getTimestamp("modifiedDate");
135    
136                                            return new Object[] {
137                                                    companyId, userId, userName, createDate, modifiedDate
138                                            };
139                                    }
140    
141                                    if (_log.isDebugEnabled()) {
142                                            _log.debug("Unable to find " + tableName + " " + primKey);
143                                    }
144    
145                                    return null;
146                            }
147                    }
148            }
149    
150            protected Object[] getDefaultUserArray(Connection con, long companyId)
151                    throws Exception {
152    
153                    try (PreparedStatement ps = con.prepareStatement(
154                                    "select userId, firstName, middleName, lastName from User_" +
155                                            " where companyId = ? and defaultUser = ?")) {
156    
157                            ps.setLong(1, companyId);
158                            ps.setBoolean(2, true);
159    
160                            try (ResultSet rs = ps.executeQuery()) {
161                                    if (rs.next()) {
162                                            long userId = rs.getLong("userId");
163                                            String firstName = rs.getString("firstName");
164                                            String middleName = rs.getString("middleName");
165                                            String lastName = rs.getString("lastName");
166    
167                                            FullNameGenerator fullNameGenerator =
168                                                    FullNameGeneratorFactory.getInstance();
169    
170                                            String userName = fullNameGenerator.getFullName(
171                                                    firstName, middleName, lastName);
172    
173                                            Timestamp createDate = new Timestamp(
174                                                    System.currentTimeMillis());
175    
176                                            return new Object[] {
177                                                    companyId, userId, userName, createDate, createDate
178                                            };
179                                    }
180    
181                                    return null;
182                            }
183                    }
184            }
185    
186            protected String getUserName(Connection con, long userId) throws Exception {
187                    try (PreparedStatement ps = con.prepareStatement(
188                                    "select firstName, middleName, lastName from User_ where " +
189                                            "userId = ?")) {
190    
191                            ps.setLong(1, userId);
192    
193                            try (ResultSet rs = ps.executeQuery()) {
194                                    if (rs.next()) {
195                                            String firstName = rs.getString("firstName");
196                                            String middleName = rs.getString("middleName");
197                                            String lastName = rs.getString("lastName");
198    
199                                            FullNameGenerator fullNameGenerator =
200                                                    FullNameGeneratorFactory.getInstance();
201    
202                                            return fullNameGenerator.getFullName(
203                                                    firstName, middleName, lastName);
204                                    }
205    
206                                    return StringPool.BLANK;
207                            }
208                    }
209            }
210    
211            protected void verifyAuditedModel(
212                            Connection con, PreparedStatement ps, String tableName,
213                            long primKey, Object[] auditedModelArray, boolean updateDates)
214                    throws Exception {
215    
216                    try {
217                            long companyId = (Long)auditedModelArray[0];
218    
219                            if (auditedModelArray[2] == null) {
220                                    auditedModelArray = getDefaultUserArray(con, companyId);
221    
222                                    if (auditedModelArray == null) {
223                                            return;
224                                    }
225                            }
226    
227                            long userId = (Long)auditedModelArray[1];
228                            String userName = (String)auditedModelArray[2];
229                            Timestamp createDate = (Timestamp)auditedModelArray[3];
230                            Timestamp modifiedDate = (Timestamp)auditedModelArray[4];
231    
232                            ps.setLong(1, companyId);
233                            ps.setLong(2, userId);
234                            ps.setString(3, userName);
235    
236                            if (updateDates) {
237                                    ps.setTimestamp(4, createDate);
238                                    ps.setTimestamp(5, modifiedDate);
239                                    ps.setLong(6, primKey);
240                            }
241                            else {
242                                    ps.setLong(4, primKey);
243                            }
244    
245                            ps.addBatch();
246                    }
247                    catch (Exception e) {
248                            if (_log.isWarnEnabled()) {
249                                    _log.warn("Unable to verify model " + tableName, e);
250                            }
251                    }
252            }
253    
254            protected void verifyAuditedModel(
255                            VerifiableAuditedModel verifiableAuditedModel)
256                    throws Exception {
257    
258                    try (LoggingTimer loggingTimer = new LoggingTimer(
259                                    verifiableAuditedModel.getTableName())) {
260    
261                            StringBundler sb = new StringBundler(8);
262    
263                            sb.append("select ");
264                            sb.append(verifiableAuditedModel.getPrimaryKeyColumnName());
265                            sb.append(", companyId, userId");
266    
267                            if (verifiableAuditedModel.getJoinByTableName() != null) {
268                                    sb.append(StringPool.COMMA_AND_SPACE);
269                                    sb.append(verifiableAuditedModel.getJoinByTableName());
270                            }
271    
272                            sb.append(" from ");
273                            sb.append(verifiableAuditedModel.getTableName());
274                            sb.append(" where userName is null order by companyId");
275    
276                            Object[] auditedModelArray = null;
277    
278                            long previousCompanyId = 0;
279    
280                            try (Connection con = DataAccess.getUpgradeOptimizedConnection();
281                                    PreparedStatement ps1 = con.prepareStatement(sb.toString());
282                                    ResultSet rs = ps1.executeQuery();
283                                    PreparedStatement ps2 =
284                                            AutoBatchPreparedStatementUtil.autoBatch(
285                                                    _createPreparedStatement(
286                                                            con, verifiableAuditedModel.getTableName(),
287                                                            verifiableAuditedModel.
288                                                                    getPrimaryKeyColumnName(),
289                                                            verifiableAuditedModel.isUpdateDates()))) {
290    
291                                    while (rs.next()) {
292                                            long companyId = rs.getLong("companyId");
293                                            long primKey = rs.getLong(
294                                                    verifiableAuditedModel.getPrimaryKeyColumnName());
295                                            long previousUserId = rs.getLong("userId");
296    
297                                            if (verifiableAuditedModel.getJoinByTableName()
298                                                            != null) {
299    
300                                                    long relatedPrimKey = rs.getLong(
301                                                            verifiableAuditedModel.getJoinByTableName());
302    
303                                                    auditedModelArray = getAuditedModelArray(
304                                                            con, verifiableAuditedModel.getRelatedModelName(),
305                                                            verifiableAuditedModel.getRelatedPKColumnName(),
306                                                            relatedPrimKey,
307                                                            verifiableAuditedModel.isAnonymousUserAllowed(),
308                                                            previousUserId);
309                                            }
310                                            else if (previousCompanyId != companyId) {
311                                                    auditedModelArray = getDefaultUserArray(con, companyId);
312    
313                                                    previousCompanyId = companyId;
314                                            }
315    
316                                            if (auditedModelArray == null) {
317                                                    continue;
318                                            }
319    
320                                            verifyAuditedModel(
321                                                    con, ps2, verifiableAuditedModel.getTableName(),
322                                                    primKey, auditedModelArray,
323                                                    verifiableAuditedModel.isUpdateDates());
324                                    }
325    
326                                    ps2.executeBatch();
327                            }
328                    }
329            }
330    
331            private PreparedStatement _createPreparedStatement(
332                            Connection con, String tableName, String primaryKeyColumnName,
333                            boolean updateDates)
334                    throws SQLException {
335    
336                    StringBundler sb = new StringBundler(7);
337    
338                    sb.append("update ");
339                    sb.append(tableName);
340                    sb.append(" set companyId = ?, userId = ?, userName = ?");
341    
342                    if (updateDates) {
343                            sb.append(", createDate = ?, modifiedDate = ?");
344                    }
345    
346                    sb.append(" where ");
347                    sb.append(primaryKeyColumnName);
348                    sb.append(" = ?");
349    
350                    return con.prepareStatement(sb.toString());
351            }
352    
353            private static final Log _log = LogFactoryUtil.getLog(
354                    VerifyAuditedModel.class);
355    
356            private class VerifyAuditedModelRunnable extends ThrowableAwareRunnable {
357    
358                    public VerifyAuditedModelRunnable(
359                            VerifiableAuditedModel verifiableAuditedModel) {
360    
361                            _verifiableAuditedModel = verifiableAuditedModel;
362                    }
363    
364                    @Override
365                    protected void doRun() throws Exception {
366                            verifyAuditedModel(_verifiableAuditedModel);
367                    }
368    
369                    private final VerifiableAuditedModel _verifiableAuditedModel;
370    
371            }
372    
373    }