001: /*
002: * Hammurapi
003: * Automated Java code review system.
004: * Copyright (C) 2004 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.org
021: * e-Mail: support@hammurapi.biz
022: */
023: package org.hammurapi.results.persistent.jdbc;
024:
025: import java.lang.reflect.Constructor;
026: import java.lang.reflect.InvocationHandler;
027: import java.lang.reflect.InvocationTargetException;
028: import java.lang.reflect.Method;
029: import java.lang.reflect.Proxy;
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: import java.util.LinkedList;
036: import java.util.Set;
037:
038: import org.hammurapi.HammurapiException;
039: import org.hammurapi.HammurapiRuntimeException;
040: import org.hammurapi.InspectorDescriptor;
041: import org.hammurapi.InspectorSet;
042: import org.hammurapi.WaiverSet;
043: import org.hammurapi.results.persistent.jdbc.sql.Report;
044: import org.hammurapi.results.persistent.jdbc.sql.ReportImpl;
045: import org.hammurapi.results.persistent.jdbc.sql.ResultsEngine;
046:
047: import com.pavelvlasov.cache.AbstractProducer;
048: import com.pavelvlasov.cache.Entry;
049: import com.pavelvlasov.cache.MemoryCache;
050: import com.pavelvlasov.convert.CompositeConverter;
051: import com.pavelvlasov.jsel.CompilationUnit;
052: import com.pavelvlasov.jsel.JselException;
053: import com.pavelvlasov.jsel.Repository;
054: import com.pavelvlasov.jsel.impl.DbRepositoryImpl;
055: import com.pavelvlasov.metrics.MeasurementCategoryFactory;
056: import com.pavelvlasov.persistence.Storage;
057: import com.pavelvlasov.sql.Parameterizer;
058: import com.pavelvlasov.sql.RowProcessor;
059: import com.pavelvlasov.sql.RowProcessorEx;
060: import com.pavelvlasov.sql.SQLProcessor;
061: import com.pavelvlasov.wrap.WrapperHandler;
062:
063: /**
064: * @author Pavel Vlasov
065: * @version $Revision: 1.16 $
066: */
067: public class ResultsFactory extends
068: org.hammurapi.results.ResultsFactory {
069: private final class Deferrer implements InvocationHandler {
070: private final AggregatedResults results;
071:
072: private Deferrer(AggregatedResults results) {
073: super ();
074: this .results = results;
075: }
076:
077: public Object invoke(Object proxy, final Method method,
078: final Object[] args) throws Throwable {
079: if (joined || Thread.currentThread() == taskThread) {
080: return method.invoke(results, args);
081: }
082:
083: if (method.getDeclaringClass().getName().equals(
084: "java.lang.Object")) {
085: return method.invoke(results, args);
086: } else if (method.getReturnType().equals(void.class)) {
087: execute(new Task() {
088:
089: public void execute() throws HammurapiException {
090: try {
091: method.invoke(results, args);
092: } catch (IllegalArgumentException e) {
093: throw new HammurapiException(e);
094: } catch (IllegalAccessException e) {
095: throw new HammurapiException(e);
096: } catch (InvocationTargetException e) {
097: throw new HammurapiException(e);
098: }
099: }
100:
101: });
102: return null;
103: } else {
104: throw new HammurapiException("Non-void method "
105: + method
106: + " can't be executed in a separate thread");
107: }
108: }
109: }
110:
111: private WaiverSet waiverSet;
112: private SQLProcessor sqlProcessor;
113:
114: private MemoryCache resultsCache = new MemoryCache(
115: new AbstractProducer() {
116:
117: public Entry get(Object key) {
118: try {
119: int resultId = ((Number) key).intValue();
120: // System.out.println("Key: "+key.getClass()+" - "+key);
121: Class resultClass = getClass()
122: .getClassLoader()
123: .loadClass(
124: getResultsEngine()
125: .getResultType(resultId));
126: // System.out.println("Result class: "+resultClass);
127: Constructor resultConstructor = resultClass
128: .getConstructor(new Class[] {
129: int.class, ResultsFactory.class });
130: final Object result = resultConstructor
131: .newInstance(new Object[] {
132: CompositeConverter
133: .getDefaultConverter()
134: .convert(key,
135: Integer.class,
136: false),
137: ResultsFactory.this });
138: return new Entry() {
139:
140: public long getExpirationTime() {
141: return 0;
142: }
143:
144: public long getTime() {
145: return 0;
146: }
147:
148: public Object get() {
149: return result;
150: }
151:
152: };
153:
154: } catch (IllegalArgumentException e) {
155: throw new HammurapiRuntimeException(e);
156: } catch (SecurityException e) {
157: throw new HammurapiRuntimeException(e);
158: } catch (InstantiationException e) {
159: throw new HammurapiRuntimeException(e);
160: } catch (IllegalAccessException e) {
161: throw new HammurapiRuntimeException(e);
162: } catch (InvocationTargetException e) {
163: throw new HammurapiRuntimeException(e);
164: } catch (NoSuchMethodException e) {
165: throw new HammurapiRuntimeException(e);
166: } catch (ClassNotFoundException e) {
167: throw new HammurapiRuntimeException(e);
168: } catch (SQLException e) {
169: throw new HammurapiRuntimeException(e);
170: }
171: }
172:
173: public Set keySet() {
174: return null;
175: }
176:
177: }, null, MeasurementCategoryFactory.getCategory(getClass()
178: .getName()
179: + ".resultsCache"));
180:
181: SQLProcessor getSQLProcessor() {
182: return sqlProcessor;
183: }
184:
185: private ResultsEngine resultsEngine;
186: private Number baseLineId;
187: private String name;
188:
189: ResultsEngine getResultsEngine() {
190: if (resultsEngine == null) {
191: resultsEngine = new ResultsEngine(getSQLProcessor());
192: }
193: return resultsEngine;
194: }
195:
196: int nextPK(final String keyName) throws SQLException {
197: Connection con = sqlProcessor.getConnection();
198: try {
199: boolean ac = con.getAutoCommit();
200: try {
201: con.setAutoCommit(false);
202: final Parameterizer parameterizer = new Parameterizer() {
203: public void parameterize(
204: PreparedStatement preparedStatement)
205: throws SQLException {
206: preparedStatement.setString(1, keyName);
207: }
208: };
209:
210: final int[] value = { 0 };
211:
212: sqlProcessor
213: .processSelect(
214: "SELECT KEY_VALUE FROM PRIMARY_KEY WHERE KEY_NAME=?",
215: parameterizer, new RowProcessorEx() {
216: public boolean process(
217: ResultSet resultSet)
218: throws SQLException {
219: value[0] = resultSet
220: .getInt("KEY_VALUE") + 1;
221: sqlProcessor
222: .processUpdate(
223: "UPDATE PRIMARY_KEY SET KEY_VALUE=KEY_VALUE+1 WHERE KEY_NAME=?",
224: parameterizer);
225: return false;
226: }
227:
228: public void onEmptyResultSet()
229: throws SQLException {
230: sqlProcessor
231: .processUpdate(
232: "INSERT INTO PRIMARY_KEY (KEY_NAME, KEY_VALUE) VALUES (?, 0)",
233: parameterizer);
234: }
235: });
236:
237: return value[0];
238: } finally {
239: con.setAutoCommit(ac);
240: }
241: } finally {
242: sqlProcessor.releaseConnection(con);
243: }
244: }
245:
246: public ResultsFactory(final ResultsFactoryConfig config)
247: throws SQLException, HammurapiException {
248: this .sqlProcessor = config.getSqlProcessor();
249: //sqlProcessor.setMetricConsumer(new StackCountingMetricConsumer(1));
250: this .waiverSet = config.getWaiverSet();
251: this .inspectorSet = config.getInspectorSet();
252: this .repository = config.getRepository();
253: this .storage = config.getStorage();
254: this .name = config.getName();
255:
256: reportId = nextPK("REPORT");
257: Report report = new ReportImpl(true);
258: report.setId(reportId);
259: report.setName(config.getName());
260: report.setDescription(config.getDescription());
261: report.setReportNumber(new Integer(config.getReportNumber()));
262: report.setHostId(config.getHostId());
263: report.setHostName(config.getHostName());
264: getResultsEngine().insertReport(report);
265:
266: if (config.getBaseLine() != null) {
267: baseLineId = getResultsEngine().getBaseLineIdByDate(
268: config.getName(),
269: new Timestamp(config.getBaseLine().getTime()));
270: if (baseLineId == null) {
271: throw new HammurapiException(
272: "Baseline report not found for "
273: + config.getBaseLine());
274: }
275: }
276: }
277:
278: public void setSummary(
279: final org.hammurapi.results.AggregatedResults results) {
280: try {
281: AggregatedResults aggregatedResults = unWrap(results);
282: getResultsEngine().setReportResult(
283: aggregatedResults.getId(), reportId);
284: aggregatedResults
285: .setBaseLineId((Integer) CompositeConverter
286: .getDefaultConverter().convert(baseLineId,
287: Integer.class, false));
288: } catch (SQLException e) {
289: throw new HammurapiRuntimeException(e);
290: }
291: }
292:
293: /**
294: * @param results
295: * @return Unwrapped result.
296: */
297: AggregatedResults unWrap(
298: final org.hammurapi.results.AggregatedResults results) {
299: return Proxy.isProxyClass(results.getClass()) ? ((Deferrer) Proxy
300: .getInvocationHandler(results)).results
301: : (AggregatedResults) results;
302: }
303:
304: public org.hammurapi.results.AggregatedResults newAggregatedResults() {
305: try {
306: return (org.hammurapi.results.AggregatedResults) proxy(new AggregatedResults(
307: waiverSet, this ));
308: } catch (SQLException e) {
309: throw new HammurapiRuntimeException(e);
310: }
311: }
312:
313: public org.hammurapi.results.NamedResults newNamedResults(
314: String name) {
315: try {
316: return (org.hammurapi.results.NamedResults) proxy(new NamedResults(
317: name, waiverSet, this ));
318: } catch (SQLException e) {
319: throw new HammurapiRuntimeException(e);
320: }
321: }
322:
323: public org.hammurapi.results.DetailedResults newDetailedResults(
324: String name) {
325: try {
326: return (org.hammurapi.results.DetailedResults) proxy(new DetailedResults(
327: name, waiverSet, this ));
328: } catch (SQLException e) {
329: throw new HammurapiRuntimeException(e);
330: }
331: }
332:
333: public org.hammurapi.results.CompositeResults newCompositeResults(
334: String name) {
335: try {
336: return (org.hammurapi.results.CompositeResults) proxy(new CompositeResults(
337: name, waiverSet, this ));
338: } catch (SQLException e) {
339: throw new HammurapiRuntimeException(e);
340: }
341: }
342:
343: public org.hammurapi.results.ReviewResults newReviewResults(
344: CompilationUnit compilationUnit) {
345: try {
346: return (org.hammurapi.results.ReviewResults) proxy(new ReviewResults(
347: compilationUnit, waiverSet, this ));
348: } catch (SQLException e) {
349: throw new HammurapiRuntimeException(e);
350: }
351: }
352:
353: private InspectorSet inspectorSet;
354: private int reportId;
355:
356: /**
357: * @return report Id
358: */
359: public int getReportId() {
360: return reportId;
361: }
362:
363: private Storage storage;
364:
365: public Storage getStorage() {
366: return storage;
367: }
368:
369: public InspectorDescriptor getInspectorDescriptor(String string) {
370: return inspectorSet.getDescriptor(string);
371: }
372:
373: private Repository repository;
374:
375: public static final String HYPERSONIC_INIT_SCRIPT = "org/hammurapi/results/persistent/jdbc/Hammurapi.Hypersonic.sql";
376: public static final String CLOUDSCAPE_INIT_SCRIPT = "org/hammurapi/results/persistent/jdbc/Hammurapi.Cloudscape.sql";
377:
378: public CompilationUnit getCompilationUnit(int id)
379: throws JselException {
380: return repository.getCompilationUnit(id);
381: }
382:
383: public org.hammurapi.results.ReviewResults findReviewResults(
384: final CompilationUnit cu) {
385: try {
386: final ReviewResults[] ret = { null };
387: getSQLProcessor()
388: .processSelect(
389: "SELECT ID, TYPE FROM RESULT WHERE COMPILATION_UNIT=? AND COMMITED=1",
390: new Parameterizer() {
391: public void parameterize(
392: PreparedStatement ps)
393: throws SQLException {
394: ps.setInt(1, cu.getSourceId()
395: .intValue());
396: }
397: }, new RowProcessor() {
398: public boolean process(
399: final ResultSet rs) {
400: try {
401: Constructor constructor = getClass()
402: .getClassLoader()
403: .loadClass(
404: rs
405: .getString("TYPE"))
406: .getConstructor(
407: new Class[] {
408: int.class,
409: ResultsFactory.class });
410: ret[0] = (ReviewResults) constructor
411: .newInstance(new Object[] {
412: new Integer(
413: rs
414: .getInt("ID")),
415: ResultsFactory.this });
416:
417: sqlProcessor
418: .processUpdate(
419: "UPDATE RESULT SET IS_NEW=0 WHERE ID=?",
420: new Parameterizer() {
421: public void parameterize(
422: PreparedStatement ps)
423: throws SQLException {
424: ps
425: .setInt(
426: 1,
427: rs
428: .getInt("ID"));
429: }
430: });
431: } catch (IllegalArgumentException e) {
432: throw new HammurapiRuntimeException(
433: e);
434: } catch (SecurityException e) {
435: throw new HammurapiRuntimeException(
436: e);
437: } catch (InstantiationException e) {
438: throw new HammurapiRuntimeException(
439: e);
440: } catch (IllegalAccessException e) {
441: throw new HammurapiRuntimeException(
442: e);
443: } catch (InvocationTargetException e) {
444: throw new HammurapiRuntimeException(
445: e);
446: } catch (NoSuchMethodException e) {
447: throw new HammurapiRuntimeException(
448: e);
449: } catch (ClassNotFoundException e) {
450: throw new HammurapiRuntimeException(
451: e);
452: } catch (SQLException e) {
453: throw new HammurapiRuntimeException(
454: e);
455: }
456: return false;
457: }
458: });
459: return ret[0];
460: } catch (SQLException e) {
461: throw new HammurapiRuntimeException(e);
462: }
463: }
464:
465: public void commit(long executionTime) {
466: try {
467: getResultsEngine().setReportExecutionTime(executionTime,
468: reportId);
469: } catch (SQLException e) {
470: throw new HammurapiRuntimeException(e);
471: }
472: }
473:
474: /**
475: * Adds a message to message table if it is not exists.
476: * Returns message id.
477: */
478: int addMessage(String message) {
479: return ((DbRepositoryImpl) repository).addMessage(message);
480: }
481:
482: public void shutdown() {
483: resultsCache.stop();
484: }
485:
486: /**
487: * @param results
488: */
489: void addToCache(BasicResults results) {
490: resultsCache.put(new Integer(results.getId()), results, 0, 0);
491: }
492:
493: BasicResults getResult(Object id) {
494: Entry entry = resultsCache.get(id);
495: return (BasicResults) (entry == null ? null : entry.get());
496: }
497:
498: /**
499: * @throws HammurapiException
500: *
501: */
502: public void cleanupOldReports() throws HammurapiException {
503: try {
504: getResultsEngine().setOldReports(getReportId(), name);
505: getResultsEngine().deleteOldResults();
506: } catch (SQLException e) {
507: throw new HammurapiException(
508: "Could not delete old results: " + e, e);
509: }
510:
511: }
512:
513: private LinkedList tasks = new LinkedList();
514: private static final int MAX_QUEUE = 1000;
515:
516: private boolean terminated;
517:
518: private void checkTerminated() throws HammurapiException {
519: synchronized (tasks) {
520: if (terminated) {
521: throw new HammurapiException(
522: "Results processing thread was terminated prematurely");
523: }
524: }
525: }
526:
527: private Thread taskThread = new Thread() {
528: {
529: setName("Results processing thread");
530: start();
531: }
532:
533: public void run() {
534: try {
535: while (true) {
536: Task task;
537: synchronized (tasks) {
538: while (tasks.isEmpty()) {
539: try {
540: tasks.wait();
541: } catch (InterruptedException e) {
542: return;
543: }
544: }
545:
546: task = (Task) tasks.removeFirst();
547: if (tasks.size() < MAX_QUEUE) {
548: tasks.notifyAll();
549: }
550: }
551:
552: if (task == null) {
553: return;
554: }
555:
556: task.execute();
557: }
558: } catch (Exception e) {
559: e.printStackTrace();
560: } finally {
561: synchronized (tasks) {
562: terminated = true;
563: tasks.notifyAll();
564: }
565: }
566: }
567: };
568:
569: /**
570: * Executes task in a separate thread
571: */
572: public void execute(Task task) throws HammurapiException {
573: if (task != null) {
574: synchronized (tasks) {
575: checkTerminated();
576:
577: if (tasks.size() >= MAX_QUEUE) {
578: try {
579: tasks.wait();
580: checkTerminated();
581: } catch (InterruptedException e) {
582: throw new HammurapiException(e);
583: }
584: }
585:
586: tasks.add(task);
587: tasks.notifyAll();
588: }
589: }
590: }
591:
592: private Object proxy(final AggregatedResults results) {
593: return Proxy.newProxyInstance(results.getClass()
594: .getClassLoader(), WrapperHandler
595: .getClassInterfaces(results.getClass()), new Deferrer(
596: results));
597: }
598:
599: private boolean joined;
600:
601: public void join() throws HammurapiException {
602: checkTerminated();
603:
604: synchronized (tasks) {
605: tasks.add(null);
606: tasks.notifyAll();
607: }
608:
609: try {
610: taskThread.join();
611: } catch (InterruptedException e) {
612: throw new HammurapiException(e);
613: }
614:
615: joined = true;
616: }
617:
618: String getName() {
619: return name;
620: }
621: }
|