/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.venky.swf.controller; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.activation.MimetypesFileTypeMap; import javax.servlet.http.HttpServletRequest; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.Query; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.json.simple.JSONObject; import com.venky.core.date.DateUtils; import com.venky.core.log.SWFLogger; import com.venky.core.log.TimerStatistics.Timer; import com.venky.core.string.StringUtil; import com.venky.core.util.ObjectUtil; import com.venky.swf.controller.annotations.RequireLogin; import com.venky.swf.db.Database; import com.venky.swf.db.JdbcTypeHelper.TypeConverter; import com.venky.swf.db.annotations.column.ui.mimes.MimeType; import com.venky.swf.db.annotations.model.EXPORTABLE; import com.venky.swf.db.jdbc.ConnectionManager; import com.venky.swf.db.model.Model; import com.venky.swf.db.model.User; import com.venky.swf.db.model.io.xls.XLSModelReader; import com.venky.swf.db.model.io.xls.XLSModelWriter; import com.venky.swf.db.model.reflection.ModelReflector; import com.venky.swf.db.table.BindVariable; import com.venky.swf.db.table.Table; import com.venky.swf.db.table.Table.ColumnDescriptor; import com.venky.swf.exceptions.AccessDeniedException; import com.venky.swf.integration.FormatHelper; import com.venky.swf.path.Path; import com.venky.swf.plugins.lucene.index.LuceneIndexer; import com.venky.swf.routing.Config; import com.venky.swf.routing.Router; import com.venky.swf.sql.Conjunction; import com.venky.swf.sql.Expression; import com.venky.swf.sql.Operator; import com.venky.swf.sql.Select; import com.venky.swf.views.BytesView; import com.venky.swf.views.DashboardView; import com.venky.swf.views.HtmlView; import com.venky.swf.views.HtmlView.StatusType; import com.venky.swf.views.RedirectorView; import com.venky.swf.views.View; import com.venky.swf.views.login.LoginView; import com.venky.swf.views.model.FileUploadView; /** * * @author venky */ public class Controller { public static final int MAX_LIST_RECORDS = 30 ; protected Path path; public Path getPath() { return path; } public Controller(Path path){ this.path = path ; } public View tron(String loggerName){ Config.instance().getLogger(StringUtil.valueOf(loggerName)).setLevel(Level.ALL); return back(); } public View troff(String loggerName){ Config.instance().getLogger(StringUtil.valueOf(loggerName)).setLevel(Level.OFF); return back(); } public View reset_router(){ if (Config.instance().isDevelopmentEnvironment()){ Router.instance().reset(); } return back(); } @RequireLogin(false) public View login(){ if (getPath().getRequest().getMethod().equals("GET") && getPath().getSession() == null ) { if (getPath().getRequest().getParameterMap().isEmpty()){ return createLoginView(); }else { return authenticate(); } }else if (getPath().getSession() != null){ if ( getSessionUser() == null ) { return createLoginView(); }else { return new RedirectorView(getPath(), loginSuccessful()); } }else{ return authenticate(); } } protected String loginSuccessful(){ String redirectedTo = getPath().getRequest().getParameter("_redirect_to"); if (ObjectUtil.isVoid(redirectedTo)){ redirectedTo="dashboard"; } return redirectedTo; } protected View authenticate(){ if (getPath().isRequestAuthenticated()){ return new RedirectorView(getPath(), loginSuccessful()); }else { return createLoginView(StatusType.ERROR, "Login incorrect!"); } } protected final HtmlView createLoginView(StatusType statusType, String text){ invalidateSession(); HtmlView lv = createLoginView(); lv.setStatus(statusType, text); return lv; } protected void invalidateSession(){ path.invalidateSession(); } protected HtmlView createLoginView(){ invalidateSession(); return new LoginView(getPath()); } @SuppressWarnings("unchecked") public <U extends User> U getSessionUser(){ return (U)getPath().getSessionUser(); } @RequireLogin(false) public View logout(){ invalidateSession(); return new RedirectorView(getPath(), "login"); } @RequireLogin(false) public View index(){ return new RedirectorView(getPath(), "dashboard"); } public DashboardView dashboard(){ return Controller.dashboard(getPath()); } protected DashboardView dashboard(HtmlView aContainedView){ return Controller.dashboard(getPath(),aContainedView); } protected static DashboardView dashboard(Path currentPath){ return new DashboardView(currentPath); } protected static DashboardView dashboard(Path currentPath, HtmlView aContainedView){ DashboardView dashboard = dashboard(currentPath); dashboard.setChildView(aContainedView); return dashboard; } @RequireLogin(false) public View resources(String name) throws IOException{ Path p = getPath(); if (name.startsWith("/config/")){ return new BytesView(p, "Access Denied!".getBytes()); } InputStream is = getClass().getResourceAsStream(name); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte [] buffer = new byte[1024]; int read = 0 ; try { if (is != null){ while ((read = is.read(buffer)) >= 0){ baos.write(buffer,0,read); } }else { return new BytesView(p, "No such resource!".getBytes()); } }catch (IOException ex){ // } p.getResponse().setDateHeader("Expires", DateUtils.addHours(System.currentTimeMillis(), 24*365*15)); return new BytesView(getPath(), baos.toByteArray(),MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(name)); } public <M extends Model> JSONObject autocompleteJSON(Class<M> modelClass, Expression baseWhereClause, String fieldName ,String value){ FormatHelper<JSONObject> fh = FormatHelper.instance(MimeType.APPLICATION_JSON, "entries", true); ModelReflector<M> reflector = ModelReflector.instance(modelClass); ColumnDescriptor fd = reflector.getColumnDescriptor(fieldName); String columnName = fd.getName(); Expression where = new Expression(reflector.getPool(),Conjunction.AND); //where.add(baseWhereClause); int maxRecordsToGet = MAX_LIST_RECORDS; if (!ObjectUtil.isVoid(value)){ if (reflector.getIndexedColumns().contains(columnName)){ LuceneIndexer indexer = LuceneIndexer.instance(reflector); StringBuilder qry = new StringBuilder(); qry.append("( ").append(columnName).append(":").append(QueryParser.escape(value)).append("* )"); Query q = indexer.constructQuery(qry.toString()); List<Integer> topRecords = indexer.findIds(q, maxRecordsToGet); int numRecordRetrieved = topRecords.size(); if (numRecordRetrieved > 0 && numRecordRetrieved < maxRecordsToGet){ Expression idExpression = Expression.createExpression(reflector.getPool(),"ID", Operator.IN, topRecords.toArray()); where.add(idExpression); } } where.add(new Expression(reflector.getPool(),columnName,Operator.LK,new BindVariable(reflector.getPool(),"%"+value+"%"))); } Select q = new Select().from(modelClass); q.where(where).orderBy(reflector.getOrderBy()); List<M> records = q.execute(modelClass,maxRecordsToGet,new DefaultModelFilter<M>(modelClass)); Iterator<M> i = records.iterator(); while (i.hasNext()){ M m = i.next(); if (!baseWhereClause.eval(m)){ i.remove(); } } Method fieldGetter = reflector.getFieldGetter(fieldName); TypeConverter<?> converter = Database.getJdbcTypeHelper(reflector.getPool()).getTypeRef(fieldGetter.getReturnType()).getTypeConverter(); for (M record:records){ try { createEntry(reflector,fh,converter.toString(fieldGetter.invoke(record)),record.getId()); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { throw new RuntimeException(ex); } } return fh.getRoot(); } public <M extends Model> View autocomplete(Class<M> modelClass, Expression baseWhereClause, String fieldName ,String value){ JSONObject doc = autocompleteJSON(modelClass, baseWhereClause, fieldName, value); Config.instance().getLogger(getClass().getName()).info(doc.toString()); return new BytesView(path, String.valueOf(doc).getBytes()); } private void createEntry(ModelReflector<? extends Model> reflector,FormatHelper<JSONObject> doc,Object name, Object id){ JSONObject elem = doc.createChildElement("entry"); FormatHelper<JSONObject> elemHelper = FormatHelper.instance(elem); elemHelper.setAttribute("name", Database.getJdbcTypeHelper(reflector.getPool()).getTypeRef(name.getClass()).getTypeConverter().toString(name)); elemHelper.setAttribute("id", Database.getJdbcTypeHelper(reflector.getPool()).getTypeRef(id.getClass()).getTypeConverter().toString(id)); } protected Map<String,Object> getFormFields(){ return getPath().getFormFields(); } private List<Sheet> getSheetsToImport(Workbook book, ImportSheetFilter filter){ List<Sheet> sheets = new ArrayList<Sheet>(); for (int i = 0; i < book.getNumberOfSheets() ; i ++ ){ Sheet sheet = book.getSheetAt(i); if (filter.filter(sheet)){ sheets.add(sheet); } } return sheets; } protected void importxls(InputStream in,ImportSheetFilter filter){ List<ModelReflector<? extends Model>> modelReflectorsOfImportedTables = new ArrayList<ModelReflector<? extends Model>>(); Workbook book = null; try { book = new HSSFWorkbook(in); for (Sheet sheet : getSheetsToImport(book,filter)){ Table<? extends Model> table = getTable(sheet); if (table == null){ continue; } Config.instance().getLogger(getClass().getName()).info("Importing:" + table.getTableName()); try { modelReflectorsOfImportedTables.add(table.getReflector()); importxls(sheet, table.getModelClass()); } catch (Exception e) { for (ModelReflector<? extends Model> ref : modelReflectorsOfImportedTables){ Database.getInstance().getCache(ref).clear(); } if (!(e instanceof RuntimeException)){ throw new RuntimeException(e); }else { throw (RuntimeException)e; } } } }catch (IOException ex){ throw new RuntimeException(ex); }finally { try { if (book != null) book.close(); } catch (IOException e) { throw new RuntimeException(e); } } } public View importxls(){ return importxls(getDefaultImportSheetFilter()); } public final View importxls(ImportSheetFilter filter){ HttpServletRequest request = getPath().getRequest(); if (request.getMethod().equalsIgnoreCase("GET")) { return dashboard(new FileUploadView(getPath())); }else { Map<String,Object> formFields = getFormFields(); if (!formFields.isEmpty()){ InputStream in = (InputStream)formFields.get("datafile"); if (in == null){ throw new RuntimeException("Nothing uploaded!"); } importxls(in,filter); } return back(); } } @RequireLogin(false) public RedirectorView back(){ RedirectorView v = new RedirectorView(getPath()); v.setRedirectUrl(getPath().getBackTarget()); return v; } protected static <M extends Model> Table<M> getTable(Sheet sheet){ String tableName = StringUtil.underscorize(sheet.getSheetName()); Table<M> table = Database.getTable(tableName); return table; } protected <M extends Model> XLSModelReader<M> getXLSModelReader(Class<M> modelClass){ return new XLSModelReader<M>(modelClass); } protected <M extends Model> void importxls(Sheet sheet, Class<M> modelClass){ XLSModelReader<M> modelReader = getXLSModelReader(modelClass); importRecords(modelReader.read(sheet), modelClass); } protected <M extends Model> void importRecords(List<M> records,Class<M> modelClass){ for (M m :records){ importRecord(m, modelClass); } } protected <M extends Model> void importRecord(M record, Class<M> modelClass){ getPath().fillDefaultsForReferenceFields(record,modelClass); save(record,modelClass); } protected <M extends Model> void save(M record, Class<M> modelClass) { if (record.getRawRecord().isNewRecord()){ record.setCreatorUserId(getSessionUser().getId()); record.setCreatedAt(null); } if (record.isDirty()){ record.setUpdaterUserId(getSessionUser().getId()); record.setUpdatedAt(null); } record.save(); //Allow extensions to fill defaults etc. if (!record.isAccessibleBy(getSessionUser()) || !getPath().getModelAccessPath(modelClass).canAccessControllerAction("save",String.valueOf(record.getId()))){ Database.getInstance().getCache(ModelReflector.instance(modelClass)).clear(); throw new AccessDeniedException(); } } public View exportxls(){ Workbook wb = new HSSFWorkbook(); for (String pool: ConnectionManager.instance().getPools()){ Map<String,Table<? extends Model>> tables = Database.getTables(pool); for (String tableName: tables.keySet()){ Table<? extends Model> table = tables.get(tableName); EXPORTABLE exportable = table.getReflector().getAnnotation(EXPORTABLE.class); if (table.isReal() && (exportable == null || exportable.value())) { Config.instance().getLogger(getClass().getName()).info("Exporting:" + table.getTableName()); exportxls(table.getModelClass(), wb); } } } try { String baseFileName = "db"+ new SimpleDateFormat("yyyyMMddHHmm").format(new java.util.Date(System.currentTimeMillis())) ; ByteArrayOutputStream os = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(os); zos.putNextEntry(new ZipEntry(baseFileName + ".xls")); wb.write(zos); zos.closeEntry(); zos.close(); return new BytesView(getPath(), os.toByteArray(),MimeType.APPLICATION_ZIP,"content-disposition", "attachment; filename=" + baseFileName + ".zip"); } catch (IOException e) { throw new RuntimeException(e); } } private final SWFLogger cat = Config.instance().getLogger(getClass().getName()); protected class DefaultModelFilter<M extends Model> implements Select.ResultFilter<M> { Select.AccessibilityFilter<M> defaultFilter = new Select.AccessibilityFilter<M>(); Class<M> modelClass = null; public DefaultModelFilter(Class<M> modelClass) { super(); this.modelClass = modelClass; } @Override public boolean pass(M record) { Timer timer = cat.startTimer("DefaultModelFilter.pass",Config.instance().isTimerAdditive()); try { return defaultFilter.pass(record) && getPath().getModelAccessPath(modelClass).canAccessControllerAction("index", StringUtil.valueOf(record.getId())); }finally{ timer.stop(); } } } protected <M extends Model> void exportxls(Class<M> modelClass,Workbook wb){ ModelReflector<M> reflector = ModelReflector.instance(modelClass); List<String> fieldsIncluded = reflector.getFields(); Iterator<String> fieldIterator = fieldsIncluded.iterator(); int numUniqueKeys = reflector.getUniqueKeys().size(); while (fieldIterator.hasNext()){ String field = fieldIterator.next(); EXPORTABLE exportable = reflector.getAnnotation(reflector.getFieldGetter(field), EXPORTABLE.class); if (exportable != null){ if (!exportable.value()){ fieldIterator.remove(); } }else if (reflector.isHouseKeepingField(field)){ if (!field.equals("ID") || numUniqueKeys > 0){ fieldIterator.remove(); } } } exportxls(modelClass, wb, fieldsIncluded); } protected <M extends Model> void exportxls(Class<M> modelClass,Workbook wb , List<String> fieldsIncluded){ List<M> list = new Select().from(modelClass).where(getPath().getWhereClause(modelClass)).execute(modelClass,new DefaultModelFilter<M>(modelClass)); getXLSModelWriter(modelClass).write(list, wb,fieldsIncluded,new HashMap<>()); } protected <M extends Model> XLSModelWriter<M> getXLSModelWriter(Class<M> modelClass){ return new XLSModelWriter<M>(modelClass); } public static interface ImportSheetFilter { public boolean filter(Sheet sheet); } protected ImportSheetFilter getDefaultImportSheetFilter(){ return new ImportSheetFilter(){ @Override public boolean filter(Sheet sheet) { Table<? extends Model> table = getTable(sheet); if (table != null){ return true; } return false; } }; } }