Java tutorial
/** * Copyright 2013 nabla * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package com.nabla.wapp.report.server; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.Properties; import java.util.logging.Level; import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.LocaleUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.birt.core.exception.BirtException; import org.eclipse.birt.core.framework.Platform; import org.eclipse.birt.report.engine.api.EngineConfig; import org.eclipse.birt.report.engine.api.EngineException; import org.eclipse.birt.report.engine.api.IReportEngine; import org.eclipse.birt.report.engine.api.IReportEngineFactory; import org.simpleframework.xml.core.Persister; import com.google.inject.Inject; import com.google.inject.Singleton; import com.nabla.wapp.report.server.xml.ReportDesign; import com.nabla.wapp.report.shared.ReportErrors; import com.nabla.wapp.server.auth.IUserSessionContext; import com.nabla.wapp.server.database.Database; import com.nabla.wapp.server.database.StatementFormat; import com.nabla.wapp.server.general.Util; import com.nabla.wapp.shared.auth.AccessDeniedException; import com.nabla.wapp.shared.dispatch.DispatchException; import com.nabla.wapp.shared.dispatch.InternalErrorException; import com.nabla.wapp.shared.general.IntegerSet; import com.nabla.wapp.shared.general.Nullable; @Singleton public class ReportManager { private static final Log log = LogFactory.getLog(ReportManager.class); public static final String INTERNAL_REPORT_FOLDER = "/WEB-INF/reports/"; public static final String REPORT_FILE_EXTENSION = "rptdesign"; public static final String PROPERTIES_FILE_EXTENSION = "properties"; public static final String[] RESOURCE_FILE_EXTENSIONS = { "css", "rptlibrary", "js", "properties" }; private final IReportEngine engine; private final IReportCategoryValidator reportCategoryValidator; @Inject public ReportManager(final IReportCategoryValidator reportCategoryValidator) throws BirtException { this.reportCategoryValidator = reportCategoryValidator; if (log.isDebugEnabled()) log.debug("initializing BIRT report engine"); final EngineConfig config = new EngineConfig(); Platform.startup(config); final IReportEngineFactory factory = (IReportEngineFactory) Platform .createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY); engine = factory.createReportEngine(config); engine.changeLogLevel(Level.WARNING); /* engine.destroy(); Platform.shutdown(); */ } public ReportTemplate open(final String internalName, final IUserSessionContext ctx) throws SQLException, DispatchException { final PreparedStatement stmt = ctx.isRoot() ? StatementFormat.prepare(ctx.getReadConnection(), "SELECT r.id, r.content, COALESCE(n.text, r.name) AS 'name'" + " FROM report AS r LEFT JOIN report_name_localized AS n ON r.id=n.report_id AND n.locale LIKE ?" + " WHERE r.internal_name LIKE ?;", ctx.getLocale().toString(), internalName) : StatementFormat.prepare(ctx.getReadConnection(), "SELECT r.id, r.content, COALESCE(n.text, r.name) AS 'name'" + " FROM user_role AS p INNER JOIN (" + "report AS r LEFT JOIN report_name_localized AS n ON r.id=n.report_id AND n.locale LIKE ?" + ") ON r.role_id=p.role_id" + " WHERE r.internal_name LIKE ? AND p.user_id=?;", ctx.getLocale().toString(), internalName, ctx.getUserId()); try { final ResultSet rs = stmt.executeQuery(); try { if (!rs.next()) throw new AccessDeniedException(); return open(rs, ctx); } finally { rs.close(); } } finally { stmt.close(); } } public ReportTemplate open(final Integer reportId, final IUserSessionContext ctx) throws SQLException, DispatchException { final PreparedStatement stmt = ctx.isRoot() ? StatementFormat.prepare(ctx.getReadConnection(), "SELECT r.id, r.content, COALESCE(n.text, r.name) AS 'name'" + " FROM report AS r LEFT JOIN report_name_localized AS n ON r.id=n.report_id AND n.locale LIKE ?" + " WHERE r.id=?;", ctx.getLocale().toString(), reportId) : StatementFormat.prepare(ctx.getReadConnection(), "SELECT r.id, r.content, COALESCE(n.text, r.name) AS 'name'" + " FROM user_role AS p INNER JOIN (" + "report AS r LEFT JOIN report_name_localized AS n ON r.id=n.report_id AND n.locale LIKE ?" + ") ON r.role_id=p.role_id" + " WHERE p.user_id=? AND r.id=?;", ctx.getLocale().toString(), ctx.getUserId(), reportId); try { final ResultSet rs = stmt.executeQuery(); try { if (!rs.next()) throw new AccessDeniedException(); return open(rs, ctx); } finally { rs.close(); } } finally { stmt.close(); } } public ReportTemplateList open(final IntegerSet reportIds, final IUserSessionContext ctx) throws SQLException, DispatchException { final ReportTemplateList templates = new ReportTemplateList(); final PreparedStatement stmt = ctx.isRoot() ? StatementFormat.prepare(ctx.getReadConnection(), "SELECT r.id, r.content, COALESCE(n.text, r.name) AS 'name'" + " FROM report AS r LEFT JOIN report_name_localized AS n ON r.id=n.report_id AND n.locale LIKE ?" + " WHERE r.id IN (?);", ctx.getLocale().toString(), reportIds) : StatementFormat.prepare(ctx.getReadConnection(), "SELECT r.id, r.content, COALESCE(n.text, r.name) AS 'name'" + " FROM user_role AS p INNER JOIN (" + "report AS r LEFT JOIN report_name_localized AS n ON r.id=n.report_id AND n.locale LIKE ?" + ") ON r.role_id=p.role_id" + " WHERE p.user_id=? AND r.id IN (?);", ctx.getLocale().toString(), ctx.getUserId(), reportIds); try { final ResultSet rs = stmt.executeQuery(); try { while (rs.next()) templates.add(open(rs, ctx)); } finally { rs.close(); } } finally { stmt.close(); } if (templates.size() != reportIds.size()) throw new AccessDeniedException(); return templates; } public ReportTemplate open(final ResultSet rs, final IUserSessionContext ctx) throws SQLException, DispatchException { try { final Integer id = rs.getInt("id"); return new ReportTemplate(id, engine.openReportDesign(rs.getString("name"), rs.getBinaryStream("content"), new StreamResolvingResourceLocator(ctx.getReadConnection(), id))); } catch (EngineException e) { throw new InternalErrorException(Util.formatInternalErrorDescription(e)); } } public int addReport(final Connection conn, final String reportName, final InputStream design, final InputStream in) throws SQLException, DispatchException { return addReport(conn, reportName, null, design, in); } public int addReport(final Connection conn, final String reportName, @Nullable final String internalName, final InputStream design, final InputStream in) throws SQLException, DispatchException { // load and scan report design if (log.isDebugEnabled()) log.debug("scanning report " + reportName); ReportDesign report; try { report = new Persister().read(ReportDesign.class, design); } catch (Exception e) { if (log.isErrorEnabled()) log.error("fail to load report design", e); throw new InternalErrorException(Util.formatInternalErrorDescription(e)); } // add report record final Integer roleId = getRole(conn, report.getRole()); if (roleId == null) { if (log.isErrorEnabled()) log.error("invalid role '" + report.getRole() + "' defined for report '" + reportName + "'"); throw new DispatchException(ReportErrors.REPORT_DESIGN_INVALID_ROLE); } final String category = report.getCategory(); if (!reportCategoryValidator.isValid(category)) { if (log.isErrorEnabled()) log.error("invalid category '" + category + "' defined for report ' " + reportName + "'"); throw new DispatchException(ReportErrors.REPORT_DESIGN_INVALID_CATEGORY); } final PreparedStatement stmt = conn.prepareStatement( "INSERT INTO report (name,internal_name,category,role_id,content) VALUES(?,?,?,?,?);", Statement.RETURN_GENERATED_KEYS); try { stmt.setString(1, report.getTitle()); if (internalName != null) stmt.setString(2, internalName); else stmt.setNull(2, Types.VARCHAR); if (category != null) stmt.setString(3, category); else stmt.setNull(3, Types.VARCHAR); stmt.setInt(4, roleId); stmt.setBinaryStream(5, in); if (log.isDebugEnabled()) log.debug("uploading report " + reportName); if (stmt.executeUpdate() != 1) { if (log.isErrorEnabled()) log.error("failed to add internal report '" + reportName + "'"); throw new InternalErrorException(); } final ResultSet rsKey = stmt.getGeneratedKeys(); try { rsKey.next(); return rsKey.getInt(1); } finally { rsKey.close(); } } finally { stmt.close(); } } public void upgradeReport(final Connection conn, final Integer reportId, final String reportName, final InputStream in) throws SQLException, DispatchException { // load and scan report design ReportDesign report; try { report = new Persister().read(ReportDesign.class, in); } catch (Exception e) { if (log.isErrorEnabled()) log.error("fail to load report design", e); throw new InternalErrorException(Util.formatInternalErrorDescription(e)); } // update report record if (log.isDebugEnabled()) log.debug("uploading report " + reportName); if (!Database.executeUpdate(conn, "UPDATE report SET name=?,content=? WHERE id=?;", report.getTitle(), in, reportId)) { if (log.isErrorEnabled()) log.error("failed to upgrade internal report '" + reportName + "'"); throw new InternalErrorException(); } } public void loadLocaleReportName(final Connection conn, final Integer reportId, final String fileName, final InputStream resource) throws SQLException, InternalErrorException { final String name = FilenameUtils.getBaseName(fileName); int pos = name.indexOf('_'); if (pos < 0) return; final String locale = name.substring(pos + 1, name.length()); try { LocaleUtils.toLocale(locale); } catch (IllegalArgumentException __) { if (log.isErrorEnabled()) log.error("unsupported locale '" + locale + "'"); return; } // get title in locale final Properties properties = new Properties(); try { properties.load(resource); } catch (Exception e) { if (log.isErrorEnabled()) log.error("fail to load locale properties file '" + fileName + "'", e); return; } final String title = properties.getProperty(ReportDesign.TITLE); if (title == null) return; if (!Database.executeUpdate(conn, "INSERT INTO report_name_localized (report_id,locale,text) VALUES(?,?,?);", reportId, locale, title)) throw new InternalErrorException("failed to add locale report title"); } public void loadReportResource(final Connection conn, final Integer reportId, final String fileName, final InputStream resource) throws SQLException, InternalErrorException { if (log.isDebugEnabled()) log.debug("uploading resource " + fileName); if (!Database.executeUpdate(conn, "INSERT INTO report_resource (report_id,name,content) VALUES(?,?,?);", reportId, fileName, resource)) throw new InternalErrorException("failed to load report resource '" + fileName + "'"); } public static File copyStreamToTempFile(final InputStream in) throws InternalErrorException { File rslt; try { rslt = File.createTempFile("wapp_report", null); } catch (IOException e) { if (log.isErrorEnabled()) log.error("fail to create temporary file to hold stream"); throw new InternalErrorException(Util.formatInternalErrorDescription(e)); } try { final FileOutputStream out = new FileOutputStream(rslt); IOUtils.copy(in, out); out.close(); } catch (Throwable e) { rslt.delete(); throw new InternalErrorException(Util.formatInternalErrorDescription(e)); } return rslt; } protected static Integer getRole(final Connection conn, final String name) throws SQLException { if (name != null) { final PreparedStatement stmt = StatementFormat.prepare(conn, "SELECT id FROM role WHERE name LIKE ? AND uname IS NOT NULL;", name); try { final ResultSet rs = stmt.executeQuery(); try { if (rs.next()) return rs.getInt(1); } finally { rs.close(); } } finally { stmt.close(); } } return null; } }