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