/** * Copyright (c) 2009-2014 Câmara dos Deputados. Todos os direitos reservados. * * e-Democracia é um software livre; você pode redistribuí-lo e/ou modificá-lo dentro * dos termos da Licença Pública Geral Menor GNU como publicada pela Fundação do * Software Livre (FSF); na versão 2.1 da Licença, ou (na sua opinião) qualquer versão. * * Este programa é distribuído na esperança de que possa ser útil, mas SEM NENHUMA GARANTIA; * sem uma garantia implícita de ADEQUAÇÃO a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. * Veja a Licença Pública Geral Menor GNU para maiores detalhes. */ package br.gov.camara.edemocracia.portlets.graficos.service.impl; import java.io.BufferedWriter; import java.io.CharArrayWriter; import java.io.IOException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import org.apache.commons.lang.StringUtils; import org.joda.time.DateMidnight; import org.joda.time.DateTimeZone; import org.joda.time.MutableDateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; import br.gov.camara.edemocracia.portlets.graficos.DadosConsolidados; import br.gov.camara.edemocracia.portlets.graficos.model.ContadorAcesso; import br.gov.camara.edemocracia.portlets.graficos.service.base.ContadorAcessoLocalServiceBaseImpl; import com.liferay.counter.service.CounterLocalServiceUtil; import com.liferay.portal.kernel.dao.jdbc.DataAccess; import com.liferay.portal.kernel.dao.orm.DynamicQuery; import com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil; import com.liferay.portal.kernel.dao.orm.OrderFactoryUtil; import com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.transaction.Transactional; import com.liferay.portal.kernel.util.Base64; import com.liferay.portal.kernel.util.PortalClassLoaderUtil; import com.liferay.portal.kernel.util.TimeZoneUtil; import com.liferay.portal.kernel.workflow.WorkflowConstants; import com.liferay.portal.model.Group; import com.liferay.portal.model.GroupConstants; import com.liferay.portal.model.Layout; import com.liferay.portal.service.ClassNameLocalServiceUtil; import com.liferay.portal.service.GroupLocalServiceUtil; import com.liferay.portal.service.UserLocalServiceUtil; import com.liferay.portlet.blogs.model.BlogsEntry; import com.liferay.portlet.documentlibrary.model.DLFileEntry; import com.liferay.portlet.wiki.model.WikiPage; /** * The implementation of the contador acesso local service. * * <p> * All custom service methods should be put in this class. Whenever methods are * added, rerun ServiceBuilder to copy their definitions into the * {@link br.gov.camara.edemocracia.portlets.graficos.service.ContadorAcessoLocalService} * interface. * </p> * * <p> * Never reference this interface directly. Always use * {@link br.gov.camara.edemocracia.portlets.graficos.service.ContadorAcessoLocalServiceUtil} * to access the contador acesso local service. * </p> * * <p> * This is a local service. Methods of this service will not have security * checks based on the propagated JAAS credentials because this service can only * be accessed from within the same VM. * </p> * * @author Robson Miranda * @see br.gov.camara.edemocracia.portlets.graficos.service.base.ContadorAcessoLocalServiceBaseImpl * @see br.gov.camara.edemocracia.portlets.graficos.service.ContadorAcessoLocalServiceUtil */ public class ContadorAcessoLocalServiceImpl extends ContadorAcessoLocalServiceBaseImpl { /** * Exporta os dados de uma compania em CSV * * @param companyId * @return * @throws SystemException */ @Override public String getCSV(long companyId) throws SystemException, IOException { DateMidnight mes = getMenorMes(companyId); DateMidnight atual = getMesAtual(); SimpleDateFormat mesAno = new SimpleDateFormat("yyyy-MM"); mesAno.setTimeZone(TimeZoneUtil.getDefault()); DateTimeFormatter iso = ISODateTimeFormat.dateTimeNoMillis().withZone( DateTimeZone.forTimeZone(TimeZoneUtil.getDefault())); CharArrayWriter writer = new CharArrayWriter(); BufferedWriter bw = new BufferedWriter(writer); bw.append("comunidade;data_geracao;ano_mes;contador;valor\r\n"); LinkedHashMap<String, Method> acessores = new LinkedHashMap<String, Method>(); for (Method m : DadosConsolidados.class.getMethods()) { if (m.getName().startsWith("get") && m.getReturnType() == int.class && m.getParameterTypes().length == 0) { StringBuilder sbNome = new StringBuilder(m.getName().substring( 3)); sbNome.setCharAt(0, Character.toLowerCase(sbNome.charAt(0))); if ("dataGeracao".equals(sbNome)) continue; int i = 0; while (i < sbNome.length()) { char chr = sbNome.charAt(i); if (Character.isUpperCase(chr) && i > 0 && Character.isLowerCase(sbNome.charAt(i - 1))) { sbNome.setCharAt(i, Character.toLowerCase(chr)); sbNome.insert(i, '_'); i++; } i++; } acessores.put(sbNome.toString(), m); } } while (mes.isBefore(atual) || mes.isEqual(atual)) { Map<Long, DadosConsolidados> dadosMes = getDadosMes(companyId, new Date(mes.getMillis())); for (Long groupId : dadosMes.keySet()) { String nome; if (groupId == null) { nome = "Portal"; } else { try { Group grupo = GroupLocalServiceUtil.getGroup(groupId); nome = grupo.getDescriptiveName().trim(); StringBuilder sbNome = new StringBuilder(nome); for (int i = 0; i < sbNome.length(); i++) { if (sbNome.charAt(i) == '"') { sbNome.insert(i, '"'); i++; } } } catch (PortalException e) { continue; } } DadosConsolidados dados = dadosMes.get(groupId); String cabecalho = "\"" + nome + "\";" + iso.print(dados.getDataGeracao().getTime()) + ";"; cabecalho += mesAno.format(new Date(mes.getMillis())) + ";"; for (Map.Entry<String, Method> entrada : acessores.entrySet()) { try { String valor = ((Integer) entrada.getValue().invoke( dados)).toString(); bw.append(cabecalho).append(entrada.getKey()) .append(";").append(valor).append("\r\n"); } catch (Exception ignore) { } } } mes = mes.plusMonths(1); } bw.close(); return writer.toString(); } /** * Lista todas as comunidades públicas, privadas e restritas da companhia * * @throws SystemException */ private List<Group> getComunidades(long companyId) throws SystemException { long groupClassNameId = ClassNameLocalServiceUtil .getClassNameId(Group.class); DynamicQuery query = DynamicQueryFactoryUtil.forClass(Group.class, PortalClassLoaderUtil.getClassLoader()); query.add(RestrictionsFactoryUtil.eq("companyId", companyId)); query.add(RestrictionsFactoryUtil.eq("classNameId", groupClassNameId)); query.add(RestrictionsFactoryUtil.in("type", new Object[] { GroupConstants.TYPE_SITE_OPEN, GroupConstants.TYPE_SITE_PRIVATE, GroupConstants.TYPE_SITE_RESTRICTED })); query.add(RestrictionsFactoryUtil.eq("parentGroupId", GroupConstants.DEFAULT_PARENT_GROUP_ID)); query.add(RestrictionsFactoryUtil.eq("active", true)); query.add(RestrictionsFactoryUtil.ne("name", GroupConstants.CONTROL_PANEL)); query.addOrder(OrderFactoryUtil.asc("name")); @SuppressWarnings("unchecked") List<Group> ret = GroupLocalServiceUtil.dynamicQuery(query); return ret; } /** * Obtém os dados consolidados até o dia de hoje * * @param companyId * @return * @throws SystemException */ @Transactional(readOnly = false) @Override public Map<Long, DadosConsolidados> getDadosConsolidados(long companyId) throws SystemException { DateMidnight mes = getMenorMes(companyId); DateMidnight atual = getMesAtual(); LinkedHashMap<Long, DadosConsolidados> totais = new LinkedHashMap<Long, DadosConsolidados>(); while (mes.isBefore(atual) || mes.isEqual(atual)) { Map<Long, DadosConsolidados> dadosMes = getDadosMes(companyId, new Date(mes.getMillis())); for (Long grupo : dadosMes.keySet()) { DadosConsolidados totaisGrupo; if (!totais.containsKey(grupo)) { totaisGrupo = new DadosConsolidados(); totais.put(grupo, totaisGrupo); } else { totaisGrupo = totais.get(grupo); } // Copia / adiciona as estatisticas DadosConsolidados dadosGrupo = dadosMes.get(grupo); totaisGrupo.setNumeroMembros(dadosGrupo.getNumeroMembros()); totaisGrupo.setForumTopicosCriados(totaisGrupo .getForumTopicosCriados() + dadosGrupo.getForumTopicosCriados()); totaisGrupo.setForumTotalPostagens(totaisGrupo .getForumTotalPostagens() + dadosGrupo.getForumTotalPostagens()); totaisGrupo.setForumVisualizacoes(dadosGrupo .getForumVisualizacoes()); totaisGrupo.setBatepapoMensagens(totaisGrupo .getBatepapoMensagens() + dadosGrupo.getBatepapoMensagens()); totaisGrupo.setBibliotecaComentarios(totaisGrupo .getBibliotecaComentarios() + dadosGrupo.getBibliotecaComentarios()); totaisGrupo.setBlogsComentarios(totaisGrupo .getBlogsComentarios() + dadosGrupo.getBlogsComentarios()); totaisGrupo.setWikiComentarios(totaisGrupo.getWikiComentarios() + dadosGrupo.getWikiComentarios()); totaisGrupo.setWikilegisSugestoes(totaisGrupo .getWikilegisSugestoes() + dadosGrupo.getWikilegisSugestoes()); totaisGrupo.setWikilegisComentarios(totaisGrupo .getWikilegisComentarios() + dadosGrupo.getWikilegisComentarios()); } mes = mes.plusMonths(1); } return totais; } /** * @return */ private DateMidnight getMesAtual() { DateMidnight atual = new DateMidnight( DateTimeZone.forTimeZone(TimeZoneUtil.getDefault())); atual = atual.withDayOfMonth(1); return atual; } /** * @param companyId * @return * @throws SystemException */ private DateMidnight getMenorMes(long companyId) throws SystemException { Date menorDataMessage = buscaData( "select min(createDate) from MBMessage where companyId = ?", companyId); Date menorDataChat = buscaData( "select min(a.messageTS) from CDChat_ChatRoomMessage a " + "JOIN CDChat_ChatRoom b ON a.chatRoomId = b.roomId " + "JOIN Group_ c on b.groupId = c.groupId " + "WHERE a.messageType = 0 and a.messageStatus = 1 and c.companyId = ?", companyId); if (menorDataMessage == null) // Se não tiver nenhuma mensagem menorDataMessage = new Date(); if (menorDataChat == null) menorDataChat = new Date(); Date menorData = menorDataMessage.before(menorDataChat) ? menorDataMessage : menorDataChat; DateMidnight mes = new DateMidnight(menorData.getTime(), DateTimeZone.forTimeZone(TimeZoneUtil.getDefault())); mes = mes.withDayOfMonth(1); return mes; } private Date buscaData(String sql, long companyId) throws SystemException { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = DataAccess.getConnection(); pstmt = conn.prepareStatement(sql); pstmt.setLong(1, companyId); rs = pstmt.executeQuery(); if (!rs.next()) return new Date(); return rs.getTimestamp(1); } catch (SQLException e) { throw new SystemException(e); } finally { DataAccess.cleanUp(rs); DataAccess.cleanUp(pstmt); DataAccess.cleanUp(conn); } } /** * Obtém os dados consolidados de todos os grupos * * @param companyId * @param mes * @return * @throws SystemException */ @Transactional(readOnly = false) private Map<Long, DadosConsolidados> getDadosMes(long companyId, Date mes) throws SystemException { DateTimeZone timeZone = DateTimeZone.forTimeZone(TimeZoneUtil .getDefault()); MutableDateTime mdt = new MutableDateTime(mes, timeZone); mdt.setTime(0, 0, 0, 0); mdt.setDayOfMonth(1); mes = new Date(mdt.getMillis()); Map<Long, DadosConsolidados> dc = null; List<ContadorAcesso> contadores = contadorAcessoPersistence.findByC_D( companyId, mes); // Verifica se precisa gerar novas estatísticas ContadorAcesso contador; if (contadores.isEmpty() || contadores.size() > 1) { for (ContadorAcesso contadorAcesso : contadores) deleteContadorAcesso(contadorAcesso); contador = createContadorAcesso(CounterLocalServiceUtil .increment(ContadorAcesso.class.getName())); } else { // Apenas um contador. Se for mês corrente e não tiver sido // atualizado hoje, // então atualiza contador = contadores.get(0); DateMidnight diaAtualizacao = new DateMidnight( contador.getDataAtualizacao(), timeZone); DateMidnight hoje = new DateMidnight(timeZone); DateMidnight mesAtualizacao = diaAtualizacao.withDayOfMonth(1); DateMidnight mesAtual = hoje.withDayOfMonth(1); DateMidnight mesDados = new DateMidnight(contador.getData(), timeZone).withDayOfMonth(1); // Mês atual. Precisa atualizar? if (mesDados.equals(mesAtual)) { if (!diaAtualizacao.equals(hoje)) contador.setCache(""); } else { // Nova atualização se os dados foram atualizados no mês a que // se // referem, ou se foram atualizados antes do mês chegar e // o mês atual for após o mês solicitado if (mesAtualizacao.equals(mesDados)) contador.setCache(""); else if (mesAtualizacao.isBefore(mesDados) && mesAtual.isAfter(mesDados)) contador.setCache(""); } } if (!StringUtils.isBlank(contador.getCache())) { dc = (Map<Long, DadosConsolidados>) Base64.stringToObject(contador .getCache().trim(), DadosConsolidados.class .getClassLoader()); if (dc == null) contador.setCache(""); } if (StringUtils.isBlank(contador.getCache())) { dc = constroiDadosConsolidados(companyId, mes); contador.setData(mes); contador.setDataAtualizacao(new Date()); contador.setCompanyId(companyId); contador.setCache(Base64.objectToString(dc)); // Se não conseguiu serializar, exclui os dados if (contador.getCache() == null && !contador.isNew()) deleteContadorAcesso(contador); else updateContadorAcesso(contador); } return dc; } private Map<Long, DadosConsolidados> constroiDadosConsolidados( long companyId, Date mes) throws SystemException { LinkedHashMap<Long, DadosConsolidados> retorno = new LinkedHashMap<Long, DadosConsolidados>(); DadosConsolidados globais = new DadosConsolidados(); globais.setDataGeracao(new Date()); retorno.put(null, globais); for (Group group : getComunidades(companyId)) { // Pega os grupos filho List<Long> grupos = new ArrayList<Long>(); grupos.add(group.getGroupId()); grupos.addAll(getGruposFilhos(group)); DadosConsolidados dc = new DadosConsolidados(); dc.setDataGeracao(new Date()); StringBuilder sbParamGrupos = new StringBuilder(); for (int i = 0; i < grupos.size(); i++) { if (i > 0) sbParamGrupos.append(","); sbParamGrupos.append("?"); } String paramGrupos = sbParamGrupos.toString(); long wikiPageClassId = ClassNameLocalServiceUtil .getClassNameId(WikiPage.class); long blogsEntryClassId = ClassNameLocalServiceUtil .getClassNameId(BlogsEntry.class); long dlFileEntryClassId = ClassNameLocalServiceUtil .getClassNameId(DLFileEntry.class); long wikilegisClassId = ClassNameLocalServiceUtil .getClassNameId("br.gov.camara.edemocracia.portlets.wikilegis.model.Artigo"); Connection conn = null; try { conn = DataAccess.getConnection(); String sql = "select COUNT(*) from (" + " select distinct u.userId from Users_Groups ug join User_ u on ug.userId = u.userId " + "where ug.groupId in (#PARAMS#) and u.status = " + WorkflowConstants.STATUS_APPROVED + ") as a"; dc.setNumeroMembros(consulta(conn, sql, paramGrupos, grupos, null)); // //// // Forum sql = "select count(*) from MBThread a join MBMessage b on a.rootMessageId = b.messageId " + "where a.groupId IN (#PARAMS#) and a.categoryId <> -1 !AND b.createDate between ? and ?!"; dc.setForumTopicosCriados(consulta(conn, sql, paramGrupos, grupos, mes)); sql = "select count(*) from MBMessage where groupId in (#PARAMS#) " + "and classNameId = 0 !and createDate between ? and ?!"; dc.setForumTotalPostagens(consulta(conn, sql, paramGrupos, grupos, mes)); sql = "select sum(viewCount) from MBThread where groupId in (#PARAMS#) and categoryId <> -1"; dc.setForumVisualizacoes(consulta(conn, sql, paramGrupos, grupos, null)); // //// // Wiki sql = "select count(*) from MBMessage where groupId in (#PARAMS#) and classNameId = " + wikiPageClassId + " " + "and rootMessageId <> messageId !and createDate between ? and ?!"; dc.setWikiComentarios(consulta(conn, sql, paramGrupos, grupos, mes)); // //// // Blogs sql = "select count(*) from MBMessage where groupId in (#PARAMS#) and classNameId = " + blogsEntryClassId + " " + "and rootMessageId <> messageId !and createDate between ? and ?!"; dc.setBlogsComentarios(consulta(conn, sql, paramGrupos, grupos, mes)); // /// // Biblioteca virtual sql = "select count(*) from MBMessage where groupId in (#PARAMS#) and classNameId = " + dlFileEntryClassId + " " + "and rootMessageId <> messageId !and createDate between ? and ?!"; dc.setBibliotecaComentarios(consulta(conn, sql, paramGrupos, grupos, mes)); // /// // Bate-papos sql = "select count(*) from CDChat_ChatRoomMessage a JOIN CDChat_ChatRoom b ON a.chatRoomId = b.roomId " + "WHERE b.groupId in (#PARAMS#) and a.messageType = 0 and a.messageStatus = 1 " + "!and a.messageTS between ? and ?!"; dc.setBatepapoMensagens(consulta(conn, sql, paramGrupos, grupos, mes)); // /// // Wikilegis sql = "select count(*) from CDWL_Contribuicao where artigoId in ( " + "select artigoId from CDWL_artigo where groupId in (#PARAMS#)) !and data_ between ? and ?! "; dc.setWikilegisSugestoes(consulta(conn, sql, paramGrupos, grupos, mes)); sql = "select count(*) " + "from CDWL_Artigo a left join MbMessage m on m.classPK = a.artigoId where m.classNameId = " + wikilegisClassId + " " + "and m.groupId in (#PARAMS#) and m.parentMessageId <> 0 !and createDate between ? and ?!"; dc.setWikilegisComentarios(consulta(conn, sql, paramGrupos, grupos, mes)); retorno.put(group.getGroupId(), dc); // Adiciona às estatísticas globais if (group.getType() == GroupConstants.TYPE_SITE_OPEN) { globais.adiciona(dc); } } catch (SQLException e) { throw new SystemException(e); } finally { DataAccess.cleanUp(conn); } } globais.setNumeroMembros(UserLocalServiceUtil .getCompanyUsersCount(companyId)); return retorno; } /** * @param group * * @return * @throws SystemException */ private List<Long> getGruposFilhos(Group group) throws SystemException { List<Long> grupos = new ArrayList<Long>(); long layoutClassNameId = ClassNameLocalServiceUtil .getClassNameId(Layout.class); DynamicQuery query = DynamicQueryFactoryUtil.forClass(Group.class); query.add(RestrictionsFactoryUtil.eq("companyId", group.getCompanyId())); query.add(RestrictionsFactoryUtil.eq("classNameId", layoutClassNameId)); query.add(RestrictionsFactoryUtil.eq("parentGroupId", group.getGroupId())); query.add(RestrictionsFactoryUtil.eq("active", true)); @SuppressWarnings("unchecked") List<Group> filhos = GroupLocalServiceUtil.dynamicQuery(query); for (Group filho : filhos) grupos.add(filho.getGroupId()); return grupos; } private int consulta(Connection conn, String sql, String paramGrupos, List<Long> grupos, Date mes) throws SQLException { sql = sql.replace("#PARAMS#", paramGrupos); boolean possuiData = false; int posData = sql.indexOf('!'); if (posData >= 0) { int end = sql.indexOf('!', posData + 1); if (end >= 0) { if (mes == null) { sql = sql.substring(0, posData) + sql.substring(end + 1); } else { sql = sql.substring(0, posData) + sql.substring(posData + 1, end) + sql.substring(end + 1); possuiData = true; } } } PreparedStatement pstmt = conn.prepareStatement(sql); try { int i; for (i = 0; i < grupos.size(); i++) pstmt.setLong(i + 1, grupos.get(i)); if (possuiData) { // Obtém a data inicial e final TimeZone tz = TimeZoneUtil.getDefault(); MutableDateTime dt = new MutableDateTime(mes, DateTimeZone.forTimeZone(tz)); dt.setTime(0, 0, 0, 0); dt.setDayOfMonth(1); Timestamp inicio = new Timestamp(dt.getMillis()); dt.addMonths(1); dt.addDays(-1); dt.setTime(23, 59, 59, 999); Timestamp fim = new Timestamp(dt.getMillis()); pstmt.setTimestamp(i++ + 1, inicio); pstmt.setTimestamp(i++ + 1, fim); } ResultSet rs = pstmt.executeQuery(); try { rs.next(); return rs.getInt(1); } finally { DataAccess.cleanUp(rs); } } finally { DataAccess.cleanUp(pstmt); } } }