comments for stock

comments for stock

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.util.Arrays;

import javax.swing.JCheckBox;
import javax.swing.JPanel;

import model.Schafkopfmodel;
import model.SystemValues.Games;
import model.SystemValues.Players;
import model.SystemValues.WIN;
import static model.SystemValues.rufspiel;
import model.db.DataObject;
import view.ConfigPopup;
import view.GUI;
import view.SchafkopfTableModel;
import view.ConfigSaveListener;

public privileged aspect Stock {

    private static GUI gui;
    private boolean useStock = false;

    /**
     * catch constructor call, so that we have the correct gui, to refer to everywhere
     */
    after() returning(GUI gui): call(GUI.new(*)) {
        Stock.gui = gui;
    }

    /** add stock variable to DataObject */
    private int DataObject.stock;

    /** catch constructor call and set stock to appropriate value */
    DataObject around(String[] values) : call ( DataObject.new(String[])) && args(values){
        DataObject obj = proceed(values);
        obj.stock = Integer.parseInt(values[9]);
        return obj;
    }

    /** add method to retrieve value of stock */
    public int DataObject.getStock() {
        return stock;
    }

    /** when a new dataobject is created, set to value of last dataobject or 0 */
    DataObject around(String player, String game, int gameVal, int valP1, int valP2, int valP3, int valP4, WIN winner)
            : call (DataObject.new(String, String, int, int, int, int, int, WIN))
            && args (player, game, gameVal, valP1, valP2, valP3, valP4, winner) {
        DataObject obj = proceed(player, game, gameVal, valP1, valP2, valP3, valP4, winner);
        if (gui != null) {
            obj.stock = gui.model.gameDB.getLast().getStock();
        } else {
            obj.stock = 0;
        }
        return obj;
    }

    /** add stock object to asTextArray method of DataObject */
    String[] around(int index) : call (* DataObject.asTextArray(int)) && args(index) && target(DataObject){
        String[] obj = proceed(index);
        String[] retStr = Arrays.copyOf(obj, obj.length+1);
        retStr[obj.length] = Integer.toString(((DataObject)thisJoinPoint.getTarget()).stock);
        return retStr;
    }

    /** addWeiter has to be changed if stock is used, such that the stock is increased then */
    void around() : call (* Schafkopfmodel.addWeiter()) {
        DataObject obj = gui.model.gameDB.getLast().copyOf();
        obj.player = "/";
        obj.game = Games.WEITER;
        obj.won = WIN.NOONE;
        obj.gameVal = 0;

        if (useStock) {
            obj.valP1 = obj.valP1 - rufspiel;
            obj.valP2 = obj.valP2 - rufspiel;
            obj.valP3 = obj.valP3 - rufspiel;
            obj.valP4 = obj.valP4 - rufspiel;
            obj.stock = gui.model.gameDB.getLast().getStock() + rufspiel*4;
            obj.gameVal = rufspiel;
        }

        gui.model.gameDB.addData(obj);

    }

    /** For Rufspiele we have to adjust the stock (if won, add stock to players,
     *  if lost double up stock */
    void around(int price, Players one, Players two, boolean won)
            : call (* Schafkopfmodel.addRufspielHelp(int, Players, Players, boolean))
            && args(price, one, two, won) {
        DataObject obj = gui.model.gameDB.getLast();
        int[] playerVals = {obj.getValP1(), obj.getValP2(), obj.getValP3(), obj.getValP4()};
        int stock = obj.getStock();

        if (!won) {
            price *= -1;
            if (useStock) {
                stock *= -1;
            }
        }

        for (int i = 0; i < playerVals.length; i++) {
            if (i == one.getNumber() || i == two.getNumber()) {
                playerVals[i] += price + (useStock ? (stock / 2) : 0);
            } else {
                playerVals[i] -= price;
            }
        }

        if (!won && useStock) {
            stock *= -2;
        } else if (useStock){
            stock = 0;
        }

        WIN win = won ? WIN.PLAYER : WIN.ENEMY;

        DataObject newObj = new DataObject(one.toString(), Games.RUFSPIEL, price,
                playerVals[0], playerVals[1], playerVals[2], playerVals[3], win);
        newObj.stock = stock;
        gui.model.gameDB.addData(newObj);
    }

    /** with stock we have one more columnn */
    int around() : execution (* SchafkopfTableModel.getColumnCount()) {
        return proceed() + 1;
    }

    /** retrieve stock value when needed */
    Object around(int rowIndex, int columnIndex)
    : execution (* SchafkopfTableModel.getValueAt(int, int))
    && args(rowIndex, columnIndex) {
        if (columnIndex < 9) {
            return proceed(rowIndex, columnIndex);
        } else {
            return gui.model.gameDB.data.get(gui.model.gameDB.size() - rowIndex - 1).getStock();
        }
    }

    /** Table header for stock */
    Object around(int index)
    : execution (* SchafkopfTableModel.getColumnName(int))
    && args(index) {
        if (index < 9) {
            return proceed(index);
        } else {
            return "Stock";
        }
    }

    private JCheckBox checkboxStock;

    /** Add stock checkbox to config screen*/
    after() returning(JPanel pane): execution (* ConfigPopup.createOptionsPane()) {
        JPanel panelStock = new JPanel();
        panelStock.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        checkboxStock = new JCheckBox("Stock");
        panelStock.add(checkboxStock);

        pane.add(panelStock, BorderLayout.NORTH);
    }

    after() : execution(* ConfigSaveListener.actionPerformed(*)) {
        useStock = checkboxStock.isSelected();
    }

    /** create html header with stock */
    String around() : execution(* ExportActionListener.createHeader()) {
        return proceed() + "
Stock: " + (useStock ? "ja" : "nein") + "
";
    }

    /** read stock value for Import */
    boolean around(String firstLine, String secondLine)
      : execution(* LoadFileListener.setSystemValues(String, String))
      && args(firstLine, secondLine) {

        if (proceed(firstLine, secondLine)) {
            if (secondLine.contains("Stock")) {
                useStock = secondLine.replaceAll("
", "").split("
")[3].replaceFirst("Stock: ", "").equals("ja");
                return true;
            }
        }
        return false;
    }
}

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.util.Arrays;

import javax.swing.JCheckBox;
import javax.swing.JPanel;

import model.Schafkopfmodel;
import model.SystemValues.Games;
import model.SystemValues.Players;
import model.SystemValues.WIN;
import static model.SystemValues.rufspiel;
import model.db.DataObject;
import view.ConfigPopup;
import view.GUI;
import view.SchafkopfTableModel;
import view.ConfigSaveListener;

public privileged aspect Stock {

    private static GUI gui;
    private boolean useStock = false;

    /**
     * catch constructor call, so that we have the correct gui, to refer to everywhere
     */
    after() returning(GUI gui): call(GUI.new(*)) {
        Stock.gui = gui;
    }

    private int DataObject.stock;

    DataObject around(String[] values) : call ( DataObject.new(String[])) && args(values){
        DataObject obj = proceed(values);
        obj.stock = Integer.parseInt(values[9]);
        return obj;
    }

    public int DataObject.getStock() {
        return stock;
    }

    DataObject around(String player, String game, int gameVal, int valP1, int valP2, int valP3, int valP4, WIN winner)
            : call (DataObject.new(String, String, int, int, int, int, int, WIN))
            && args (player, game, gameVal, valP1, valP2, valP3, valP4, winner) {
        DataObject obj = proceed(player, game, gameVal, valP1, valP2, valP3, valP4, winner);
        if (gui != null) {
            obj.stock = gui.model.gameDB.getLast().getStock();
        } else {
            obj.stock = 0;
        }
        return obj;
    }

    String[] around(int index) : call (* DataObject.asTextArray(int)) && args(index) && target(DataObject){
        String[] obj = proceed(index);
        String[] retStr = Arrays.copyOf(obj, obj.length+1);
        retStr[obj.length] = Integer.toString(((DataObject)thisJoinPoint.getTarget()).stock);
        return retStr;
    }

    void around() : call (* Schafkopfmodel.addWeiter()) {
        DataObject obj = gui.model.gameDB.getLast().copyOf();
        obj.player = "/";
        obj.game = Games.WEITER;
        obj.won = WIN.NOONE;
        obj.gameVal = 0;

        if (useStock) {
            obj.valP1 = obj.valP1 - rufspiel;
            obj.valP2 = obj.valP2 - rufspiel;
            obj.valP3 = obj.valP3 - rufspiel;
            obj.valP4 = obj.valP4 - rufspiel;
            obj.stock = gui.model.gameDB.getLast().getStock() + rufspiel*4;
            obj.gameVal = rufspiel;
        }

        gui.model.gameDB.addData(obj);

    }

    void around(int price, Players one, Players two, boolean won)
            : call (* Schafkopfmodel.addRufspielHelp(int, Players, Players, boolean))
            && args(price, one, two, won) {
        DataObject obj = gui.model.gameDB.getLast();
        int[] playerVals = {obj.getValP1(), obj.getValP2(), obj.getValP3(), obj.getValP4()};
        int stock = obj.getStock();

        if (!won) {
            price *= -1;
            if (useStock) {
                stock *= -1;
            }
        }

        for (int i = 0; i < playerVals.length; i++) {
            if (i == one.getNumber() || i == two.getNumber()) {
                playerVals[i] += price + (useStock ? (stock / 2) : 0);
            } else {
                playerVals[i] -= price;
            }
        }

        if (!won && useStock) {
            stock *= -2;
        } else if (useStock){
            stock = 0;
        }

        WIN win = won ? WIN.PLAYER : WIN.ENEMY;

        DataObject newObj = new DataObject(one.toString(), Games.RUFSPIEL, price,
                playerVals[0], playerVals[1], playerVals[2], playerVals[3], win);
        newObj.stock = stock;
        gui.model.gameDB.addData(newObj);
    }

    int around() : execution (* SchafkopfTableModel.getColumnCount()) {
        return proceed() + 1;
    }

    Object around(int rowIndex, int columnIndex)
    : execution (* SchafkopfTableModel.getValueAt(int, int))
    && args(rowIndex, columnIndex) {
        if (columnIndex < 9) {
            return proceed(rowIndex, columnIndex);
        } else {
            return gui.model.gameDB.data.get(gui.model.gameDB.size() - rowIndex - 1).getStock();
        }
    }

    Object around(int index)
    : execution (* SchafkopfTableModel.getColumnName(int))
    && args(index) {
        if (index < 9) {
            return proceed(index);
        } else {
            return "Stock";
        }
    }

    private JCheckBox checkboxStock;

    after() returning(JPanel pane): execution (* ConfigPopup.createOptionsPane()) {
        JPanel panelStock = new JPanel();
        panelStock.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        checkboxStock = new JCheckBox("Stock");
        panelStock.add(checkboxStock);

        pane.add(panelStock, BorderLayout.NORTH);
    }

    after() : execution(* ConfigSaveListener.actionPerformed(*)) {
        useStock = checkboxStock.isSelected();
    }

    String around() : execution(* ExportActionListener.createHeader()) {
        return proceed() + "
Stock: " + (useStock ? "ja" : "nein") + "
";
    }

    boolean around(String firstLine, String secondLine)
      : execution(* LoadFileListener.setSystemValues(String, String))
      && args(firstLine, secondLine) {

        if (proceed(firstLine, secondLine)) {
            if (secondLine.contains("Stock")) {
                useStock = secondLine.replaceAll("
", "").split("
")[3].replaceFirst("Stock: ", "").equals("ja");
                return true;
            }
        }
        return false;
    }
}


重构  采用接口的形式 不依赖上层api

重构  采用接口的形式 不依赖上层api

package com.kidswant.aop.statistics;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.lang.reflect.Method;

public aspect Statistics {

    /* 点击事件或者流程事件，参数通过CodeSignature反射获取*/
    pointcut doAllListener() : call(* *.*KisListener(..));

    /* Activity 页面事件   within限制具体类型，不然重载方法会多次调用
    pointcut doOnOpen() : execution(protected * *..BaseActivity.onResume()) && within(BaseActivity);
    pointcut doOnClose() : execution(protected * *..BaseActivity.onPause()) && within(BaseActivity);*/

    /* 页面事件 */
    pointcut doOnPage(boolean open) : execution(private void reportPagePoint(boolean)) && args(open);

    after() : doAllListener() {
        String methodName = getMethodPathName(thisJoinPoint);

        CodeSignature codeSignature = (CodeSignature) thisJoinPoint.getSignature();
        String[] parameterNames = codeSignature.getParameterNames();
        Object[] parameterValues = thisJoinPoint.getArgs();

        String param = null;
        for (int i = 0; i < parameterNames.length; i++) {
            String name = parameterNames[i];
            if("param".equals(name))
            {
                param = parameterValues[i] != null ? parameterValues[i].toString() : null;
                break;
            }
        }

        reportEvent(methodName, param);
    }

    /*  void around() : doOnOpen() {
        proceed();

        reportPage(thisJoinPoint, true);
     }

    void around() : doOnClose() {
        proceed();

        reportPage(thisJoinPoint, false);
    }*/

    before(boolean open) : doOnPage(open) {
        reportPage(thisJoinPoint, open);
    }

    /** 上报点击事件或者流程事件 **/
    private void reportEvent(String name, String param)
    {
        /** android.util.Log.i("aaaaaaaaaaaa", "reportEvent.name: " + name+ "-"+ param); **/

        String value = com.kidswant.aop.statistics.ReportDataUtil.getPropertyValue(name);
        if(value == null || "".equals(value)) return;

        String[] values = value.split(":");
        if(values.length < 3) return;

        com.kidswant.aop.statistics.AopStatistics.reportEvent(values[0], values[2], param);
    }

    /** 上报页面事件 **/
    private void reportPage(JoinPoint joinPoint, boolean open)
    {
        Signature signature = joinPoint.getSignature();
        Object target = joinPoint.getTarget();
        String typeName = target.getClass().getName();/*signature.getDeclaringTypeName() 是匹配方法声明的类，是基类里面的*/;
        Object param = getPropertyValue(target, "getReportParam");
        Object flag = getPropertyValue(target, "getReportFlag");
        if(flag != null && !"".equals(flag))
        {
            typeName = typeName + "&" + flag;
        }

       /** android.util.Log.i("aaaaaaaaaaaa", "reportPage.name: " + typeName+ "-"+ param + "-" + open); **/

        String value = com.kidswant.aop.statistics.ReportDataUtil.getPropertyValue(typeName);
        if(value == null || "".equals(value)) return;
        String[] values = value.split(":");
        if(values.length < 3) return;

        if(open)
        {
            com.kidswant.aop.statistics.AopStatistics.reportPageOnResume(values[0], values[1], values[2], (String)param);
        }
        else
        {
            com.kidswant.aop.statistics.AopStatistics.reportPageOnPause(values[0], values[1], values[2], (String)param);
        }
    }

    /** 获取方法名全路径 **/
    private String getMethodPathName(JoinPoint joinPoint)
    {
        Signature signature = joinPoint.getSignature();
        String typeName = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        String methodPathName = typeName + "." + methodName;
        return methodPathName;
    }

    /** 反射获取参数值 **/
    private Object getPropertyValue(Object o, String methodName){
       Object value = null;
       try {
           Method m = o.getClass().getMethod(methodName, null);
           value = m.invoke(o, null);
       } catch (Exception e) {
           e.printStackTrace();
       }

       return value;
    }
}

package com.kidswant.aop.statistics;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.lang.reflect.Method;

public aspect Statistics {

    /* 点击事件或者流程事件，参数通过CodeSignature反射获取*/
    pointcut doAllListener() : call(* *.*KisListener(..));

    /* Activity 页面事件   within限制具体类型，不然重载方法会多次调用
    pointcut doOnOpen() : execution(protected * *..BaseActivity.onResume()) && within(BaseActivity);
    pointcut doOnClose() : execution(protected * *..BaseActivity.onPause()) && within(BaseActivity);*/

    /* fragment页面事件 */
    pointcut doOnPage(boolean open) : execution(private void reportPagePoint(boolean)) && args(open);

    after() : doAllListener() {
        String methodName = getMethodPathName(thisJoinPoint);

        CodeSignature codeSignature = (CodeSignature) thisJoinPoint.getSignature();
        String[] parameterNames = codeSignature.getParameterNames();
        Object[] parameterValues = thisJoinPoint.getArgs();

        String param = null;
        for (int i = 0; i < parameterNames.length; i++) {
            String name = parameterNames[i];
            if("param".equals(name))
            {
                param = parameterValues[i] != null ? parameterValues[i].toString() : null;
                break;
            }
        }

        reportEvent(methodName, param);
    }

    /*  void around() : doOnOpen() {
        proceed();

        reportPage(thisJoinPoint, true);
     }

    void around() : doOnClose() {
        proceed();

        reportPage(thisJoinPoint, false);
    }*/

    before(boolean open) : doOnPage(open) {
        reportPage(thisJoinPoint, open);
    }

    /** 上报点击事件或者流程事件 **/
    private void reportEvent(String name, String param)
    {
        /** android.util.Log.i("aaaaaaaaaaaa", "reportEvent.name: " + name+ "-"+ param); **/

        String value = com.kidswant.aop.statistics.ReportDataUtil.getPropertyValue(name);
        if(value == null || "".equals(value)) return;

        String[] values = value.split(":");
        if(values.length < 3) return;

        com.kidswant.aop.statistics.AopStatistics.reportEvent(values[0], values[2], param);
    }

    /** 上报页面事件 **/
    private void reportPage(JoinPoint joinPoint, boolean open)
    {
        Signature signature = joinPoint.getSignature();
        Object target = joinPoint.getTarget();
        String typeName = target.getClass().getName();/*signature.getDeclaringTypeName() 是匹配方法声明的类，是基类里面的*/;
        Object param = getPropertyValue(target, "getReportParam");
        Object flag = getPropertyValue(target, "getReportFlag");
        if(flag != null && !"".equals(flag))
        {
            typeName = typeName + "&" + flag;
        }

       /** android.util.Log.i("aaaaaaaaaaaa", "reportPage.name: " + typeName+ "-"+ param + "-" + open); **/

        String value = com.kidswant.aop.statistics.ReportDataUtil.getPropertyValue(typeName);
        if(value == null || "".equals(value)) return;
        String[] values = value.split(":");
        if(values.length < 3) return;

        if(open)
        {
            com.kidswant.aop.statistics.AopStatistics.reportPageOnResume(values[0], values[1], values[2], (String)param);
        }
        else
        {
            com.kidswant.aop.statistics.AopStatistics.reportPageOnPause(values[0], values[1], values[2], (String)param);
        }
    }

    /** 获取方法名全路径 **/
    private String getMethodPathName(JoinPoint joinPoint)
    {
        Signature signature = joinPoint.getSignature();
        String typeName = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        String methodPathName = typeName + "." + methodName;
        return methodPathName;
    }

    /** 反射获取参数值 **/
    private Object getPropertyValue(Object o, String methodName){
       Object value = null;
       try {
           Method m = o.getClass().getMethod(methodName, null);
           value = m.invoke(o, null);
       } catch (Exception e) {
           e.printStackTrace();
       }

       return value;
    }
}


improved HtmlElement toString with parent

/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 *
 * 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.seleniumtests.core.aspects;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import com.seleniumtests.core.runner.SeleniumRobotRunner;
import com.seleniumtests.reporter.TestAction;
import com.seleniumtests.reporter.TestLogging;
import com.seleniumtests.reporter.TestStep;
import com.seleniumtests.uipage.PageObject;
import com.seleniumtests.uipage.htmlelements.HtmlElement;

/**
 * Aspect to intercept calls to methods of HtmlElement. It allows to retry discovery and action
 * when something goes wrong with the driver
 *
 * @author behe
 *
 */
@Aspect
public class LogAction {

	private Date testStepStart = null;

	/**
	 * Intercept actions done through browser
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around("(execution(public * com.seleniumtests.uipage.htmlelements..*Element..* (..)) "
    		+ "|| execution(public * com.seleniumtests.uipage.htmlelements.SelectList..* (..)) "
    		+ "|| execution(public * com.seleniumtests.uipage.htmlelements.Table..* (..))) "
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.toString (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.toString (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.get* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.get* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.set* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.set* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.is* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.find* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.wait* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.toHTML (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.Table.find* (..))"
    		)
	public Object logAction(ProceedingJoinPoint joinPoint) throws Throwable {
		String targetName;

		try {
			HtmlElement element = (HtmlElement)joinPoint.getTarget();
			targetName = element.toString();
		} catch (ClassCastException e) {
			targetName = joinPoint.getTarget().toString();
		}

		Object reply = null;
		boolean actionFailed = false;

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			actionFailed = true;
			throw e;
		} finally {
			if (isHtmlElementDirectlyCalled(Thread.currentThread().getStackTrace()) && TestLogging.getParentTestStep() != null) {
				String actionName = String.format("%s on %s %s", joinPoint.getSignature().getName(), targetName, buildArgString(joinPoint));
				TestLogging.getParentTestStep().addAction(new TestAction(actionName, actionFailed));
			}
		}
		return reply;
	}

	/**
	 * Intercept actions
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around("execution(public * com.seleniumtests.uipage.PageObject..* (..)) "
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.get* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.close* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.param (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.assert* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.capture*Snapshot (..))"
    		)
	public Object logPageObjectAction(ProceedingJoinPoint joinPoint) throws Throwable {
		PageObject page = (PageObject)joinPoint.getTarget();
		String pageName = page == null ? "": "on page " + page.getClass().getSimpleName();
		String actionName = String.format("%s %s %s", joinPoint.getSignature().getName(), pageName, buildArgString(joinPoint));
		Object reply = null;
		boolean actionFailed = false;

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			actionFailed = true;
			throw e;
		} finally {
			if (TestLogging.getParentTestStep() != null) {
				TestLogging.getParentTestStep().addAction(new TestAction(actionName, actionFailed));
			}
		}
		return reply;
	}

	/**
	 * Log any call to test steps (page object calls inside a PageObject subclass)
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around("this(com.seleniumtests.uipage.PageObject) && " +					// caller is a PageObject
			"(call(public * com.seleniumtests.uipage.PageObject+.* (..))"
			+ "&& !call(public * com.seleniumtests.uipage.PageObject.* (..)))"
			)
	public Object logSubTestStep(ProceedingJoinPoint joinPoint) throws Throwable {
		Object reply = null;

		String stepName = String.format("%s %s", joinPoint.getSignature().getName(), buildArgString(joinPoint));
		TestStep currentStep = new TestStep(stepName);
		TestStep previousParent = TestLogging.getParentTestStep();
		if (TestLogging.getParentTestStep() != null) {
			TestLogging.getParentTestStep().addStep(currentStep);
			TestLogging.setParentTestStep(currentStep);
		}

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			currentStep.setFailed(true);
			currentStep.setActionException(e);
			throw e;
		} finally {
			if (TestLogging.getParentTestStep() != null && previousParent != null) {
				TestLogging.setParentTestStep(previousParent);
			}
		}
		return reply;
	}

	/**
	 * Log calls to cucumber annotated methods
	 * In case we are not inside a cucumber test, this method won't intercept anything because action will already be performed
	 * by logNonCucumberTestStep
	 * @param joinPoint
	 * @return
	 * @throws Throwable
	 */
	@Pointcut("(execution(@cucumber.api.java.en.When public * * (..)) || execution(@cucumber.api.java.en.Given public * * (..))) && if()")
	public static boolean isCucumberTest(ProceedingJoinPoint joinPoint) {
		return SeleniumRobotRunner.isCucumberTest();
	}

	@Around("isCucumberTest(joinPoint)")
	public Object logCucumberTestStep(ProceedingJoinPoint joinPoint) throws Throwable {
		return logTestStep(joinPoint);
	}

	/**
	 * Log any call to test steps (page object calls inside a SeleniumTestPlan subclass)
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around(
			"(!this(com.seleniumtests.uipage.PageObject) && " +							// caller is not a PageObject
			"(call(public * com.seleniumtests.uipage.PageObject+.* (..))"				// calls to methods in subclasses of PageObject class
			+ "&& !call(public * com.seleniumtests.uipage.PageObject.* (..)))"			// not calls to methods inside PageObject class
			+ "|| call(private * com.seleniumtests.uipage.PageObject.openPage (..)))"
			)
	public Object logNonCucumberTestStep(ProceedingJoinPoint joinPoint) throws Throwable {
		return logTestStep(joinPoint);
	}

	private Object logTestStep(ProceedingJoinPoint joinPoint)  throws Throwable {
		Object reply = null;
		boolean rootStep = false;
		TestStep previousParent = null;
		TestStep currentStep = new TestStep(buildRootStepName(joinPoint));

		// check if any root step is already registered
		// happens when using cucumber where a cucumber method can call an other method intercepted by this pointcut
		// ex: Given (url "www.somesite.com") calls "open(url)"
		// In this case, open becomes a child of Given
		if (TestLogging.getCurrentRootTestStep() == null) {
			TestLogging.setCurrentRootTestStep(currentStep); // will also set parent step
			testStepStart = new Date();
			rootStep = true;
		} else {
			TestLogging.getParentTestStep().addStep(currentStep);
			TestLogging.setParentTestStep(currentStep);
			previousParent = TestLogging.getParentTestStep();
		}

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			currentStep.setFailed(true);
			currentStep.setActionException(e);
			throw e;
		} finally {
			if (rootStep) {
				TestLogging.getCurrentRootTestStep().setDuration(new Date().getTime() - testStepStart.getTime());
				TestLogging.logTestStep(TestLogging.getCurrentRootTestStep());
			} else {
				TestLogging.setParentTestStep(previousParent);
			}
		}
		return reply;
	}

	@Around("execution(public * com.seleniumtests.reporter.TestLogging.logWebOutput (..))"
			+ " || execution(public * com.seleniumtests.reporter.TestLogging.logWebStep (..))")
	public Object interceptLogging(ProceedingJoinPoint joinPoint) throws Throwable {
		if (TestLogging.getParentTestStep() != null) {
			Boolean actionFailed;
			try {
				actionFailed = (Boolean)joinPoint.getArgs()[1];
			} catch (ClassCastException | IndexOutOfBoundsException e) {
				actionFailed = false;
			}
			TestLogging.getParentTestStep().addAction(new TestAction(joinPoint.getArgs()[0].toString(), actionFailed));
			return null;
		} else {
			return joinPoint.proceed(joinPoint.getArgs());
		}
	}

	/**
	 * Build argument string of the join point
	 * @param joinPoint
	 * @return
	 */
	private String buildArgString(JoinPoint joinPoint) {
		String argString = "";
		if (joinPoint.getArgs().length > 0) {
			argString = "with args: (";
			for (Object arg: joinPoint.getArgs()) {
				if (arg instanceof CharSequence[]) {
					argString += "[";
					for (Object obj: (CharSequence[])arg) {
						argString += obj.toString() + ",";
					}
					argString += "]";
				} else {
					argString += (arg == null ? "null": arg.toString()) + ", ";
				}
			}
			argString += ")";
		}
		return argString;
	}

	/**
	 * Returns step name depending on step type
	 * In case of cucumber step, get the annotation value.
	 * /!\ THIS WORKS ONLY IF
	 * 	parameters of the annotated method are the Object version ones. Use 'Integer' instead of 'int' for example, when declaring
	 * a cucumber method which uses an integer as parameter. Else method discovery won't find it and step name will fall back to method name
	 *
	 * Else, get method name
	 * @param joinPoint
	 * @return
	 */
	private String buildRootStepName(JoinPoint joinPoint) {
		String stepName = String.format("%s %s", joinPoint.getSignature().getName(), buildArgString(joinPoint));

		List<Class<?>> parameters = new ArrayList<>();
		for (Object arg: joinPoint.getArgs()) {
			parameters.add(arg == null ? Object.class: arg.getClass());
		}
		Class<?>[] pType  = parameters.toArray(new Class<?>[0]);
		Method method;
		try {
			method = joinPoint.getSignature().getDeclaringType().getDeclaredMethod(joinPoint.getSignature().getName(), pType);
		} catch (NoSuchMethodException | SecurityException e) {
			return stepName;
		}

		for (Annotation cucumberAnnotation: method.getAnnotations()) {
			if (cucumberAnnotation.annotationType().getCanonicalName().contains("cucumber.api.java.en")) {
				stepName = getAnnotationValue(cucumberAnnotation);
				stepName += " " + buildArgString(joinPoint);
				break;
			}
		}
		return stepName;
	}

	/**
	 * Returns the value of cucumber annotation to get corresponding text
	 * @param annotation
	 * @return
	 */
	private String getAnnotationValue(Annotation annotation) {
		return annotation.toString().replaceFirst("timeout=\\d+", "")
			.replace("@" + annotation.annotationType().getCanonicalName() + "(", "")
			.replaceFirst(",?\\s?value=", "")
			.replaceFirst("\\)$", "");
	}

	/**
	 * Check whether this action has directly been performed on the HtmlElement (e.g: click)
	 * or through an other type of element (e.g: clic on LinkElement, redirected to HtmlElement)
	 * In this last case, do not log action as it has already been logged by the specific type of
	 * element
	 * @param stack
	 * @return
	 */
	private boolean isHtmlElementDirectlyCalled(StackTraceElement[] stack) {
		String stackClass = null;
		boolean specificElementFound = false;
		boolean htmlElementFound = false;

		for(int i=0; i < stack.length; i++) {

			// when using aspects, class name may contain a "$", remove everything after that symbol
			stackClass = stack[i].getClassName().split("\\$")[0];
			if (stackClass.equals("com.seleniumtests.uipage.htmlelements.HtmlElement")) {
				htmlElementFound = true;
			} else if (stackClass.startsWith("com.seleniumtests.uipage.htmlelements.")) {
				specificElementFound = true;
			}
		}
		if (htmlElementFound && specificElementFound) {
			return false;
		} else {
			return true;
		}
	}
}

/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 *
 * 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.seleniumtests.core.aspects;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import com.seleniumtests.core.runner.SeleniumRobotRunner;
import com.seleniumtests.reporter.TestAction;
import com.seleniumtests.reporter.TestLogging;
import com.seleniumtests.reporter.TestStep;
import com.seleniumtests.uipage.PageObject;
import com.seleniumtests.uipage.htmlelements.HtmlElement;

/**
 * Aspect to intercept calls to methods of HtmlElement. It allows to retry discovery and action
 * when something goes wrong with the driver
 *
 * @author behe
 *
 */
@Aspect
public class LogAction {

	private Date testStepStart = null;

	/**
	 * Intercept actions done through browser
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around("(execution(public * com.seleniumtests.uipage.htmlelements..*Element..* (..)) "
    		+ "|| execution(public * com.seleniumtests.uipage.htmlelements.SelectList..* (..)) "
    		+ "|| execution(public * com.seleniumtests.uipage.htmlelements.Table..* (..))) "
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.toString (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.toString (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.get* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.get* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.set* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.set* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.PictureElement.is* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.find* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.wait* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.HtmlElement.toHTML (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.htmlelements.Table.find* (..))"
    		)
	public Object logAction(ProceedingJoinPoint joinPoint) throws Throwable {
		String targetName;

		try {
			HtmlElement element = (HtmlElement)joinPoint.getTarget();
			targetName = element.toString();
		} catch (ClassCastException e) {
			targetName = joinPoint.getTarget().toString();
		}

		Object reply = null;
		boolean actionFailed = false;

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			actionFailed = true;
			throw e;
		} finally {
			if (isHtmlElementDirectlyCalled(Thread.currentThread().getStackTrace()) && TestLogging.getParentTestStep() != null) {
				String actionName = String.format("%s on %s %s", joinPoint.getSignature().getName(), targetName, buildArgString(joinPoint));
				TestLogging.getParentTestStep().addAction(new TestAction(actionName, actionFailed));
			}
		}
		return reply;
	}

	/**
	 * Intercept actions
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around("execution(public * com.seleniumtests.uipage.PageObject..* (..)) "
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.get* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.close* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.param (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.assert* (..))"
    		+ "&& !execution(* com.seleniumtests.uipage.PageObject.capture*Snapshot (..))"
    		)
	public Object logPageObjectAction(ProceedingJoinPoint joinPoint) throws Throwable {
		PageObject page = (PageObject)joinPoint.getTarget();
		String pageName = page == null ? "": "on page " + page.getClass().getSimpleName();
		String actionName = String.format("%s %s %s", joinPoint.getSignature().getName(), pageName, buildArgString(joinPoint));
		Object reply = null;
		boolean actionFailed = false;

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			actionFailed = true;
			throw e;
		} finally {
			if (TestLogging.getParentTestStep() != null) {
				TestLogging.getParentTestStep().addAction(new TestAction(actionName, actionFailed));
			}
		}
		return reply;
	}

	/**
	 * Log any call to test steps (page object calls inside a PageObject subclass)
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around("this(com.seleniumtests.uipage.PageObject) && " +					// caller is a PageObject
			"(call(public * com.seleniumtests.uipage.PageObject+.* (..))"
			+ "&& !call(public * com.seleniumtests.uipage.PageObject.* (..)))"
			)
	public Object logSubTestStep(ProceedingJoinPoint joinPoint) throws Throwable {
		Object reply = null;

		String stepName = String.format("%s %s", joinPoint.getSignature().getName(), buildArgString(joinPoint));
		TestStep currentStep = new TestStep(stepName);
		TestStep previousParent = TestLogging.getParentTestStep();
		if (TestLogging.getParentTestStep() != null) {
			TestLogging.getParentTestStep().addStep(currentStep);
			TestLogging.setParentTestStep(currentStep);
		}

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			currentStep.setFailed(true);
			currentStep.setActionException(e);
			throw e;
		} finally {
			if (TestLogging.getParentTestStep() != null && previousParent != null) {
				TestLogging.setParentTestStep(previousParent);
			}
		}
		return reply;
	}

	/**
	 * Log calls to cucumber annotated methods
	 * In case we are not inside a cucumber test, this method won't intercept anything because action will already be performed
	 * by logNonCucumberTestStep
	 * @param joinPoint
	 * @return
	 * @throws Throwable
	 */
	@Pointcut("(execution(@cucumber.api.java.en.When public * * (..)) || execution(@cucumber.api.java.en.Given public * * (..))) && if()")
	public static boolean isCucumberTest(ProceedingJoinPoint joinPoint) {
		return SeleniumRobotRunner.isCucumberTest();
	}

	@Around("isCucumberTest(joinPoint)")
	public Object logCucumberTestStep(ProceedingJoinPoint joinPoint) throws Throwable {
		return logTestStep(joinPoint);
	}

	/**
	 * Log any call to test steps (page object calls inside a SeleniumTestPlan subclass)
	 * @param joinPoint
	 * @throws Throwable
	 */
	@Around(
			"(!this(com.seleniumtests.uipage.PageObject) && " +							// caller is not a PageObject
			"(call(public * com.seleniumtests.uipage.PageObject+.* (..))"				// calls to methods in subclasses of PageObject class
			+ "&& !call(public * com.seleniumtests.uipage.PageObject.* (..)))"			// not calls to methods inside PageObject class
			+ "|| call(private * com.seleniumtests.uipage.PageObject.openPage (..)))"
			)
	public Object logNonCucumberTestStep(ProceedingJoinPoint joinPoint) throws Throwable {
		return logTestStep(joinPoint);
	}

	private Object logTestStep(ProceedingJoinPoint joinPoint)  throws Throwable {
		Object reply = null;
		boolean rootStep = false;
		TestStep previousParent = null;
		TestStep currentStep = new TestStep(buildRootStepName(joinPoint));

		// check if any root step is already registered
		// happens when using cucumber where a cucumber method can call an other method intercepted by this pointcut
		// ex: Given (url "www.somesite.com") calls "open(url)"
		// In this case, open becomes a child of Given
		if (TestLogging.getCurrentRootTestStep() == null) {
			TestLogging.setCurrentRootTestStep(currentStep); // will also set parent step
			testStepStart = new Date();
			rootStep = true;
		} else {
			TestLogging.getParentTestStep().addStep(currentStep);
			TestLogging.setParentTestStep(currentStep);
			previousParent = TestLogging.getParentTestStep();
		}

		try {
			reply = joinPoint.proceed(joinPoint.getArgs());
		} catch (Throwable e) {
			currentStep.setFailed(true);
			currentStep.setActionException(e);
			throw e;
		} finally {
			if (rootStep) {
				TestLogging.getCurrentRootTestStep().setDuration(new Date().getTime() - testStepStart.getTime());
				TestLogging.logTestStep(TestLogging.getCurrentRootTestStep());
			} else {
				TestLogging.setParentTestStep(previousParent);
			}
		}
		return reply;
	}

	@Around("execution(public * com.seleniumtests.reporter.TestLogging.logWebOutput (..))"
			+ " || execution(public * com.seleniumtests.reporter.TestLogging.logWebStep (..))")
	public Object interceptLogging(ProceedingJoinPoint joinPoint) throws Throwable {
		if (TestLogging.getParentTestStep() != null) {
			Boolean actionFailed;
			try {
				actionFailed = (Boolean)joinPoint.getArgs()[1];
			} catch (ClassCastException | IndexOutOfBoundsException e) {
				actionFailed = false;
			}
			TestLogging.getParentTestStep().addAction(new TestAction(joinPoint.getArgs()[0].toString(), actionFailed));
			return null;
		} else {
			return joinPoint.proceed(joinPoint.getArgs());
		}
	}

	/**
	 * Build argument string of the join point
	 * @param joinPoint
	 * @return
	 */
	private String buildArgString(JoinPoint joinPoint) {
		String argString = "";
		if (joinPoint.getArgs().length > 0) {
			argString = "with args: (";
			for (Object arg: joinPoint.getArgs()) {
				argString += (arg == null ? "null": arg.toString()) + ", ";
			}
			argString += ")";
		}
		return argString;
	}

	/**
	 * Returns step name depending on step type
	 * In case of cucumber step, get the annotation value.
	 * /!\ THIS WORKS ONLY IF
	 * 	parameters of the annotated method are the Object version ones. Use 'Integer' instead of 'int' for example, when declaring
	 * a cucumber method which uses an integer as parameter. Else method discovery won't find it and step name will fall back to method name
	 *
	 * Else, get method name
	 * @param joinPoint
	 * @return
	 */
	private String buildRootStepName(JoinPoint joinPoint) {
		String stepName = String.format("%s %s", joinPoint.getSignature().getName(), buildArgString(joinPoint));

		List<Class<?>> parameters = new ArrayList<>();
		for (Object arg: joinPoint.getArgs()) {
			parameters.add(arg == null ? Object.class: arg.getClass());
		}
		Class<?>[] pType  = parameters.toArray(new Class<?>[0]);
		Method method;
		try {
			method = joinPoint.getSignature().getDeclaringType().getDeclaredMethod(joinPoint.getSignature().getName(), pType);
		} catch (NoSuchMethodException | SecurityException e) {
			return stepName;
		}

		for (Annotation cucumberAnnotation: method.getAnnotations()) {
			if (cucumberAnnotation.annotationType().getCanonicalName().contains("cucumber.api.java.en")) {
				stepName = getAnnotationValue(cucumberAnnotation);
				stepName += " " + buildArgString(joinPoint);
				break;
			}
		}
		return stepName;
	}

	/**
	 * Returns the value of cucumber annotation to get corresponding text
	 * @param annotation
	 * @return
	 */
	private String getAnnotationValue(Annotation annotation) {
		return annotation.toString().replaceFirst("timeout=\\d+", "")
			.replace("@" + annotation.annotationType().getCanonicalName() + "(", "")
			.replaceFirst(",?\\s?value=", "")
			.replaceFirst("\\)$", "");
	}

	/**
	 * Check whether this action has directly been performed on the HtmlElement (e.g: click)
	 * or through an other type of element (e.g: clic on LinkElement, redirected to HtmlElement)
	 * In this last case, do not log action as it has already been logged by the specific type of
	 * element
	 * @param stack
	 * @return
	 */
	private boolean isHtmlElementDirectlyCalled(StackTraceElement[] stack) {
		String stackClass = null;
		boolean specificElementFound = false;
		boolean htmlElementFound = false;

		for(int i=0; i < stack.length; i++) {

			// when using aspects, class name may contain a "$", remove everything after that symbol
			stackClass = stack[i].getClassName().split("\\$")[0];
			if (stackClass.equals("com.seleniumtests.uipage.htmlelements.HtmlElement")) {
				htmlElementFound = true;
			} else if (stackClass.startsWith("com.seleniumtests.uipage.htmlelements.")) {
				specificElementFound = true;
			}
		}
		if (htmlElementFound && specificElementFound) {
			return false;
		} else {
			return true;
		}
	}
}


Delete commented out code.

Delete commented out code.

package com.chitter.aspect;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jdo.Extent;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
import javax.jdo.annotations.PersistenceCapable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

import com.chitter.persistence.Announcement;
import com.chitter.persistence.UserAccount;
import com.chitter.persistence.UserStatistic;
import com.chitter.persistence.UserTwitterTimeline;
import com.chitter.utility.ExceptionPrinter;

public aspect Persistence {
	private static Cache cache;
	static
	{
		try
		{
			CacheManager cacheManager = CacheManager.getInstance();
			CacheFactory cacheFactory = cacheManager.getCacheFactory();
			cache = cacheFactory.createCache(Collections.EMPTY_MAP);
		} catch (CacheException e) {
			ExceptionPrinter.print(System.err, e, "Couldn't create cache at PersistenceAspect");
		}
	}

	/**
	 * captures ctors with more than one arg and
	 * all setters of @PersistenceCapable objects.
	 */
	pointcut pushObject(Object o): target(o) && (call(* (@PersistenceCapable *).set*(..)) || execution((@PersistenceCapable *).new(*,*,..)));
	pointcut pullObject(Object pk): args(pk) && call((@PersistenceCapable *).new(*));
	pointcut pullList(): call(List (@PersistenceCapable *).get*List());
	pointcut pullExtent(): call(Extent (@PersistenceCapable *).get*Extent());

	pointcut pmShouldClose() : call(* com.chitter..*Servlet.processRequest(HttpServletRequest,HttpServletResponse));

	pointcut withinEscapePersistence(): withincode(@EscapePersistence * *(..));

	@SuppressWarnings("all")
	public static Extent UserAccount.getUserAccountExtent(){
		return null;
	}

	@SuppressWarnings("all")
	public static List UserAccount.getUserAccountList(){
		return new ArrayList<UserAccount>();
	}

	@SuppressWarnings("all")
	public static Set<String> UserAccount.getOnlineUserGtalkIds(){
		HashSet<String> onlineUserGtalkIdsSet = (HashSet<String>) cache.peek("onlineUserGtalkIdsSet");
		if (onlineUserGtalkIdsSet == null) {
			onlineUserGtalkIdsSet = new HashSet<String>();
			cache.put("onlineUserGtalkIdsSet",onlineUserGtalkIdsSet);
		}
		return onlineUserGtalkIdsSet;
	}

	@SuppressWarnings("all")
	public static List UserAccount.getTimelineActiveUsers(){
		PersistenceManager pm = getPM();

		try {
			Query q = pm.newQuery(UserAccount.class, "this.isTimelineActive == true");
			return (List) q.execute();
		} catch (Exception e) {
			ExceptionPrinter.print(System.err,e,"I couldn't fetch timeline active users");
			return (List)(new ArrayList<Object>());
		}
	}

	@SuppressWarnings("all")
	public static List UserAccount.getOnlineUsers(){
		PersistenceManager pm = getPM();

		try {
			Query q = pm.newQuery(UserAccount.class, "this.isOnline == true");
			return (List) q.execute();
		} catch (Exception e) {
			ExceptionPrinter.print(System.err,e,"I couldn't fetch online users");
			return (List)(new ArrayList<Object>());
		}
	}

	@SuppressWarnings("all")
	public static List UserStatistic.getUserStatisticList() {
		return new ArrayList<UserStatistic> ();
	}

	@SuppressWarnings("all")
	public static List UserTwitterTimeline.getUserTwitterTimelineList() {
		return new ArrayList<UserTwitterTimeline> ();
	}

	@SuppressWarnings("all")
	public static List Announcement.getAnnouncementList() {
		return new ArrayList<Announcement> ();
	}

	private static final PersistenceManagerFactory PMF =
			JDOHelper.getPersistenceManagerFactory("transactions-optional");
	private static final ThreadLocal<PersistenceManager> perThreadPM
	= new ThreadLocal<PersistenceManager>();

	private Persistence() {};

	public static PersistenceManager getPM() {
		PersistenceManager PM = perThreadPM.get();
		if (PM == null) {
			PM = PMF.getPersistenceManager();
			perThreadPM.set(PM);
		}
		return PM;
	}

	after(Object o) : pushObject(o) && !withinEscapePersistence(){
		PersistenceManager pm = getPM();
		/**
		 * First try to convert object to UserStatistic.
		 * If you can, that means the persistent object was UserStatistic,
		 * so you should flag statistics array as dirty.
		 * (o.w. it will not persist changes to the array.)
		 */
		if(o.getClass().equals(UserStatistic.class)) {
			UserStatistic statistic=(UserStatistic) o;
			JDOHelper.makeDirty(statistic, "statistics");
		}
		pm.makePersistent(o.getClass().cast(o));
		cache.put(o.getClass().getName() + "_" + pm.getObjectId(o), o);
		pm.flush();
	}

	@SuppressWarnings("all")
	Object around(Object pk) : pullObject(pk) && !withinEscapePersistence(){
		PersistenceManager pm = getPM();
		try{
			Object r = cache.peek(thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + "_" + pk);
			if (r == null) {
				r = pm.getObjectById(thisJoinPoint.getSignature().getDeclaringType(),pk);
				cache.put(thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + "_" + pk, r);
			}
			return r;
		} catch (Exception e) {
			cache.remove(thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + "_" + pk);
			ExceptionPrinter.print(System.err, e, "I couldn't fetch persistent object for " +
					thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + " " + pk);
			return null;
		}
	}

	@SuppressWarnings("all")
	List around() : pullList() {
		PersistenceManager pm = getPM();

		try {
			List list = (List) pm.newQuery(thisJoinPoint.getSignature().getDeclaringType()).execute();
			return list;
		} catch (Exception e) {
			ExceptionPrinter.print(System.err,e,"I couldn't fetch persistent list for "+getClass());
			return (List)(new ArrayList<Object>());
		}
	}

	after() : pmShouldClose() {
		PersistenceManager PM = perThreadPM.get();
		if (PM != null) {
			perThreadPM.remove();
			Transaction tx = PM.currentTransaction();
			if (tx.isActive()) {
				tx.rollback();
			}
			PM.close();
		}
	}

}

package com.chitter.aspect;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jdo.Extent;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
import javax.jdo.annotations.PersistenceCapable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

import com.chitter.persistence.Announcement;
import com.chitter.persistence.UserAccount;
import com.chitter.persistence.UserStatistic;
import com.chitter.persistence.UserTwitterTimeline;
import com.chitter.utility.ExceptionPrinter;

public aspect Persistence {
	private static Cache cache;
	static
	{
		try
		{
			CacheManager cacheManager = CacheManager.getInstance();
			CacheFactory cacheFactory = cacheManager.getCacheFactory();
			cache = cacheFactory.createCache(Collections.EMPTY_MAP);
		} catch (CacheException e) {
			ExceptionPrinter.print(System.err, e, "Couldn't create cache at PersistenceAspect");
		}
	}

	/**
	 * captures ctors with more than one arg and
	 * all setters of @PersistenceCapable objects.
	 */
	pointcut pushObject(Object o): target(o) && (call(* (@PersistenceCapable *).set*(..)) || execution((@PersistenceCapable *).new(*,*,..)));
	pointcut pullObject(Object pk): args(pk) && call((@PersistenceCapable *).new(*));
	pointcut pullList(): call(List (@PersistenceCapable *).get*List());
	pointcut pullExtent(): call(Extent (@PersistenceCapable *).get*Extent());

	pointcut pmShouldClose() : call(* com.chitter..*Servlet.processRequest(HttpServletRequest,HttpServletResponse));

	pointcut withinEscapePersistence(): withincode(@EscapePersistence * *(..));

	@SuppressWarnings("all")
	public static Extent UserAccount.getUserAccountExtent(){
		return null;
	}

	@SuppressWarnings("all")
	public static List UserAccount.getUserAccountList(){
		return new ArrayList<UserAccount>();
	}

	@SuppressWarnings("all")
	public static Set<String> UserAccount.getOnlineUserGtalkIds(){
		HashSet<String> onlineUserGtalkIdsSet = (HashSet<String>) cache.peek("onlineUserGtalkIdsSet");
		if (onlineUserGtalkIdsSet == null) {
			onlineUserGtalkIdsSet = new HashSet<String>();
			cache.put("onlineUserGtalkIdsSet",onlineUserGtalkIdsSet);
		}
		return onlineUserGtalkIdsSet;
	}

	@SuppressWarnings("all")
	public static List UserAccount.getTimelineActiveUsers(){
		PersistenceManager pm = getPM();

		try {
			Query q = pm.newQuery(UserAccount.class, "this.isTimelineActive == true");
			return (List) q.execute();
		} catch (Exception e) {
			ExceptionPrinter.print(System.err,e,"I couldn't fetch timeline active users");
			return (List)(new ArrayList<Object>());
		}
	}

	@SuppressWarnings("all")
	public static List UserAccount.getOnlineUsers(){
		PersistenceManager pm = getPM();

		try {
			Query q = pm.newQuery(UserAccount.class, "this.isOnline == true");
			return (List) q.execute();
		} catch (Exception e) {
			ExceptionPrinter.print(System.err,e,"I couldn't fetch online users");
			return (List)(new ArrayList<Object>());
		}
	}

	@SuppressWarnings("all")
	public static List UserStatistic.getUserStatisticList() {
		return new ArrayList<UserStatistic> ();
	}

	@SuppressWarnings("all")
	public static List UserTwitterTimeline.getUserTwitterTimelineList() {
		return new ArrayList<UserTwitterTimeline> ();
	}

	@SuppressWarnings("all")
	public static List Announcement.getAnnouncementList() {
		return new ArrayList<Announcement> ();
	}

	private static final PersistenceManagerFactory PMF =
			JDOHelper.getPersistenceManagerFactory("transactions-optional");
	private static final ThreadLocal<PersistenceManager> perThreadPM
	= new ThreadLocal<PersistenceManager>();

	private Persistence() {};

	public static PersistenceManager getPM() {
		PersistenceManager PM = perThreadPM.get();
		if (PM == null) {
			PM = PMF.getPersistenceManager();
			perThreadPM.set(PM);
		}
		return PM;
	}

	after(Object o) : pushObject(o) && !withinEscapePersistence(){
		PersistenceManager pm = getPM();
		/**
		 * First try to convert object to UserStatistic.
		 * If you can, that means the persistent object was UserStatistic,
		 * so you should flag statistics array as dirty.
		 * (o.w. it will not persist changes to the array.)
		 */
		if(o.getClass().equals(UserStatistic.class)) {
			UserStatistic statistic=(UserStatistic) o;
			JDOHelper.makeDirty(statistic, "statistics");
		}
		pm.makePersistent(o.getClass().cast(o));
		cache.put(o.getClass().getName() + "_" + pm.getObjectId(o), o);
		pm.flush();
	}

	@SuppressWarnings("all")
	Object around(Object pk) : pullObject(pk) && !withinEscapePersistence(){
		PersistenceManager pm = getPM();
		try{
			Object r = cache.peek(thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + "_" + pk);
			if (r == null) {
				r = pm.getObjectById(thisJoinPoint.getSignature().getDeclaringType(),pk);
				cache.put(thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + "_" + pk, r);
			}
			return r;
		} catch (Exception e) {
			cache.remove(thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + "_" + pk);
			ExceptionPrinter.print(System.err, e, "I couldn't fetch persistent object for " +
					thisJoinPointStaticPart.getSignature().getDeclaringTypeName() + " " + pk);
			return null;
		}
	}

	@SuppressWarnings("all")
	List around() : pullList() {
		PersistenceManager pm = getPM();

		try {
			List list = (List) pm.newQuery(thisJoinPoint.getSignature().getDeclaringType()).execute();
			return list;
		} catch (Exception e) {
			ExceptionPrinter.print(System.err,e,"I couldn't fetch persistent list for "+getClass());
			return (List)(new ArrayList<Object>());
		}
	}

	@SuppressWarnings("all")
	/*Extent around() : pullExtent() {
		PersistenceManager pm = getPM();

		try {
			Extent extent = pm.getExtent(thisJoinPoint.getSignature().getDeclaringType(), false);
			return extent;
		} catch (Exception e) {
			ExceptionPrinter.print(System.err, e, "I couldn't fetch persistent extent for "+getClass());
			return null;
		}
    }*/

	after() : pmShouldClose() {
		PersistenceManager PM = perThreadPM.get();
		if (PM != null) {
			perThreadPM.remove();
			Transaction tx = PM.currentTransaction();
			if (tx.isActive()) {
				tx.rollback();
			}
			PM.close();
		}
	}

}


show millies

show millies

package com.mebigfatguy.hashshmash;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public aspect HashCollector {

    private static final Journaller journaller = new Journaller();

    after() returning (Map<?,?> m): call(HashMap.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashMapDetails details = new HashMapDetails(m, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    after() returning (Set<?> s): call(HashSet.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashSetDetails details = new HashSetDetails(s, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    before(): execution(public static void *.main(String[])) {
        Class<?> c = HashCollector.class;
    }
}

final class Journaller implements Runnable {

    private static final int SLEEP_TIME = 5 * 1000;

    private Field tableField;
    private ConcurrentHashMap<HashDetails, HashDetails> hDetails = new ConcurrentHashMap<HashDetails, HashDetails>();
    private PrintWriter writer;
    private boolean operational = false;

    public Journaller() {
        try {
            System.out.println("*************************");
            System.out.println("HASHSHMASH ASPECT ENABLED");
            System.out.println("*************************");

            tableField = HashMap.class.getDeclaredField("table");
            tableField.setAccessible(true);

            File dir = new File(System.getProperty("user.home"), ".hashshmash");
            dir.mkdirs();

            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, new Date().toString() + ".txt")))));

            Thread t = new Thread(this);
            t.setDaemon(true);
            operational = true;
            t.start();
        } catch (Exception e) {
        }
    }

    public void add(HashDetails details) {
        if (operational)
            hDetails.put(details,  details);
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(SLEEP_TIME);

                for (HashDetails details : hDetails.keySet()) {
                    try {
                        Map<?, ?> map = details.getMap();
                        if (map != null) {
                            int newSize = details.getMap().size();
                            if (newSize == details.size) {
                                Entry<?, ?>[] table = (Entry<?, ?>[])tableField.get(map);
                                details.totalSlots = table.length;
                                for (Entry<?, ?> e : table) {
                                    if (e != null) {
                                        details.usedSlots++;
                                    }
                                }
                                writer.println(details);
                                hDetails.remove(details);
                            } else
                                details.size = newSize;
                        } else {
                            hDetails.remove(details);
                        }
                    } catch (Exception e) {
                        //might get ConcurrentModificationException or such, just ignore
                    }
                }
                writer.flush();
            }
        } catch (InterruptedException ie) {
        }
    }
}

abstract class HashDetails {
    private static SimpleDateFormat FORMATTER = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss-SSS");

    public Date allocTime;
    public String caller;
    public int size;
    public int usedSlots;
    public int totalSlots;

    public abstract Map<?,?> getMap();

    public abstract String getDetailType();

    public String toString() {
        return getDetailType() + "\t" + FORMATTER.format(allocTime) + "\t" + caller + "\t" + getMap().size() + "\t" + totalSlots + "\t" + usedSlots;
    }
}

class HashMapDetails extends HashDetails {
    public Map<?, ?> map;

    public HashMapDetails(Map<?, ?> m, Date d, String c) {
        map = m;
        allocTime = d;
        caller = c;
        size = map.size();
    }

    public Map<?,?> getMap() {
        return map;
    }

    public String getDetailType() {
        return map.getClass().getSimpleName();
    }
}

class HashSetDetails extends HashDetails {
    private static Field MAP_FIELD;
    static {
        try {
            MAP_FIELD = HashSet.class.getDeclaredField("map");
            MAP_FIELD.setAccessible(true);
        } catch (Exception e) {
            MAP_FIELD = null;
        }
    }

    public Set<?> set;

    public HashSetDetails(Set<?> s, Date d, String c) {
        set = s;
        allocTime = d;
        caller = c;
        size = set.size();
    }

    public Map<?,?> getMap() {
        try {
            if (MAP_FIELD == null)
                return null;

            return (Map<?,?>) MAP_FIELD.get(set);
        } catch (Exception e) {
            return null;
        }
    }

    public String getDetailType() {
        return set.getClass().getSimpleName();
    }
}

package com.mebigfatguy.hashshmash;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public aspect HashCollector {

    private static final Journaller journaller = new Journaller();

    after() returning (Map<?,?> m): call(HashMap.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashMapDetails details = new HashMapDetails(m, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    after() returning (Set<?> s): call(HashSet.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashSetDetails details = new HashSetDetails(s, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    before(): execution(public static void *.main(String[])) {
        Class<?> c = HashCollector.class;
    }
}

final class Journaller implements Runnable {

    private static final int SLEEP_TIME = 5 * 1000;

    private Field tableField;
    private ConcurrentHashMap<HashDetails, HashDetails> hDetails = new ConcurrentHashMap<HashDetails, HashDetails>();
    private PrintWriter writer;
    private boolean operational = false;

    public Journaller() {
        try {
            System.out.println("*************************");
            System.out.println("HASHSHMASH ASPECT ENABLED");
            System.out.println("*************************");

            tableField = HashMap.class.getDeclaredField("table");
            tableField.setAccessible(true);

            File dir = new File(System.getProperty("user.home"), ".hashshmash");
            dir.mkdirs();

            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, new Date().toString() + ".txt")))));

            Thread t = new Thread(this);
            t.setDaemon(true);
            operational = true;
            t.start();
        } catch (Exception e) {
        }
    }

    public void add(HashDetails details) {
        if (operational)
            hDetails.put(details,  details);
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(SLEEP_TIME);

                for (HashDetails details : hDetails.keySet()) {
                    try {
                        Map<?, ?> map = details.getMap();
                        if (map != null) {
                            int newSize = details.getMap().size();
                            if (newSize == details.size) {
                                Entry<?, ?>[] table = (Entry<?, ?>[])tableField.get(map);
                                details.totalSlots = table.length;
                                for (Entry<?, ?> e : table) {
                                    if (e != null) {
                                        details.usedSlots++;
                                    }
                                }
                                writer.println(details);
                                hDetails.remove(details);
                            } else
                                details.size = newSize;
                        } else {
                            hDetails.remove(details);
                        }
                    } catch (Exception e) {
                        //might get ConcurrentModificationException or such, just ignore
                    }
                }
                writer.flush();
            }
        } catch (InterruptedException ie) {
        }
    }
}

abstract class HashDetails {
    private static SimpleDateFormat FORMATTER = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

    public Date allocTime;
    public String caller;
    public int size;
    public int usedSlots;
    public int totalSlots;

    public abstract Map<?,?> getMap();

    public abstract String getDetailType();

    public String toString() {
        return getDetailType() + "\t" + FORMATTER.format(allocTime) + "\t" + caller + "\t" + getMap().size() + "\t" + totalSlots + "\t" + usedSlots;
    }
}

class HashMapDetails extends HashDetails {
    public Map<?, ?> map;

    public HashMapDetails(Map<?, ?> m, Date d, String c) {
        map = m;
        allocTime = d;
        caller = c;
        size = map.size();
    }

    public Map<?,?> getMap() {
        return map;
    }

    public String getDetailType() {
        return map.getClass().getSimpleName();
    }
}

class HashSetDetails extends HashDetails {
    private static Field MAP_FIELD;
    static {
        try {
            MAP_FIELD = HashSet.class.getDeclaredField("map");
            MAP_FIELD.setAccessible(true);
        } catch (Exception e) {
            MAP_FIELD = null;
        }
    }

    public Set<?> set;

    public HashSetDetails(Set<?> s, Date d, String c) {
        set = s;
        allocTime = d;
        caller = c;
        size = set.size();
    }

    public Map<?,?> getMap() {
        try {
            if (MAP_FIELD == null)
                return null;

            return (Map<?,?>) MAP_FIELD.get(set);
        } catch (Exception e) {
            return null;
        }
    }

    public String getDetailType() {
        return set.getClass().getSimpleName();
    }
}


Renamed inter-type declared fields to avoid conflicts with possible implementing subclasses (id is a common field name)

/**
 * EntityConcern.aj
 *
 * Created on 27.07.2013
 */

package at.pria.koza.harmonic;

/**
 *

 * {@code EntityConcern} provides inter-type declarations and advices that ensure consistent behavior for all
 * {@linkplain Entity entities}.
 *

 *
 * @version V1.0 27.07.2013
 * @author SillyFreak
 */
public aspect EntityConcern {
    private Engine Entity.harmonic$engine;
    private int    Entity.harmonic$id;

    public void Entity.setEngine(Engine engine, int id) {
        harmonic$engine = engine;
        harmonic$id = id;
    }

    public Engine Entity.getEngine() {
        return harmonic$engine;
    }

    public int Entity.getId() {
        return harmonic$id;
    }

    /**
     *

     * Matches the initialization of an {@link Entity} class.
     *

     *
     * TODO an improvement would be to determine here if the constructor is the last one being called belonging to
     * an {@code Entity} class.
     *
     * @param engine the {@link Engine} given as the first argument of the constructor
     * @param entity the {@link Entity} being initialized
     */
    pointcut newEntity(Engine engine, Entity entity):
        initialization(Entity+.new(Engine, ..)) && this(entity) && args(engine, ..);

    /**
     *

     * Advises {@link Entity} constructors to register the entity in their {@link Engine}. This happens before any
     * other initialization of the entity class, i.e. the constructor can access engine and id via
     * {@link Entity#getEngine() getEngine()} and {@link Entity#getId() getId()}. Specifically, it happens directly
     * after the most specific non-entity superclass constructor was run.
     *

     *
     * TODO this is implemented by checking whether the engine was already set; the code is generated even for
     * constructors calling a {@code this()} or {@code super()} entity constructors. Strictly speaking, this does
     * not advise only those pointcuts where the code belongs.
     *
     * @param engine the {@link Engine} given as the first argument of the constructor
     * @param entity the {@link Entity} being initialized
     */
    before(Engine engine, Entity entity): newEntity(engine, entity) {
        if(entity.harmonic$engine == null) engine.put(entity);
    }

    /**
     *

     * Declares it as an error to call {@link Entity#setEngine(Engine, int) setEngine()} by yourself. This is done
     * by the {@link Engine#put(Entity) put()} method, and should be done nowhere else.
     *

     */
    declare error: !within(Engine) && call(public void Entity.setEngine(Engine, int)):
        "setEngine() must not be called explicitly; it is called by Engine";

    /**
     *

     * Declares it as an error to call {@link Engine#put(Entity) put()} by yourself. This is done by this aspect,
     * and should be done nowhere else.
     *

     *

     * Note that projects that do not process this library's aspects have to call the method themselves; but as
     * they don't process this aspect, they also don't get an error for doing so.
     *

     */
    declare error: !within(EntityConcern) && call(public void Engine.put(Entity)):
        "put() must not be called explicitly; it is called by an advice of Entity";

    /**
     *

     * Declares it as an error to declare an entity constructor that does not use an Engine as its first parameter.
     * This parameter is necessary for the advice above to work.
     *

     *

     * Note that projects that do not process this library's aspects may do this another way; but as they don't
     * process this aspect, they also don't get an error for doing so.
     *

     */
    declare error: execution(Entity+.new(..)) && !execution(Entity+.new(Engine, ..)):
        "Entity constructors must provide an engine as their first argument";
}

/**
 * EntityConcern.aj
 *
 * Created on 27.07.2013
 */

package at.pria.koza.harmonic;

/**
 *

 * {@code EntityConcern} provides inter-type declarations and advices that ensure consistent behavior for all
 * {@linkplain Entity entities}.
 *

 *
 * @version V1.0 27.07.2013
 * @author SillyFreak
 */
public aspect EntityConcern {
    private Engine Entity.engine;
    private int    Entity.id;

    public void Entity.setEngine(Engine engine, int id) {
        this.engine = engine;
        this.id = id;
    }

    public Engine Entity.getEngine() {
        return engine;
    }

    public int Entity.getId() {
        return id;
    }

    /**
     *

     * Matches the initialization of an {@link Entity} class.
     *

     *
     * TODO an improvement would be to determine here if the constructor is the last one being called belonging to
     * an {@code Entity} class.
     *
     * @param engine the {@link Engine} given as the first argument of the constructor
     * @param entity the {@link Entity} being initialized
     */
    pointcut newEntity(Engine engine, Entity entity):
        initialization(Entity+.new(Engine, ..)) && this(entity) && args(engine, ..);

    /**
     *

     * Advises {@link Entity} constructors to register the entity in their {@link Engine}. This happens before any
     * other initialization of the entity class, i.e. the constructor can access engine and id via
     * {@link Entity#getEngine() getEngine()} and {@link Entity#getId() getId()}. Specifically, it happens directly
     * after the most specific non-entity superclass constructor was run.
     *

     *
     * TODO this is implemented by checking whether the engine was already set; the code is generated even for
     * constructors calling a {@code this()} or {@code super()} entity constructors. Strictly speaking, this does
     * not advise only those pointcuts where the code belongs.
     *
     * @param engine the {@link Engine} given as the first argument of the constructor
     * @param entity the {@link Entity} being initialized
     */
    before(Engine engine, Entity entity): newEntity(engine, entity) {
        if(entity.engine == null) engine.put(entity);
    }

    /**
     *

     * Declares it as an error to call {@link Entity#setEngine(Engine, int) setEngine()} by yourself. This is done
     * by the {@link Engine#put(Entity) put()} method, and should be done nowhere else.
     *

     */
    declare error: !within(Engine) && call(public void Entity.setEngine(Engine, int)):
        "setEngine() must not be called explicitly; it is called by Engine";

    /**
     *

     * Declares it as an error to call {@link Engine#put(Entity) put()} by yourself. This is done by this aspect,
     * and should be done nowhere else.
     *

     *

     * Note that projects that do not process this library's aspects have to call the method themselves; but as
     * they don't process this aspect, they also don't get an error for doing so.
     *

     */
    declare error: !within(EntityConcern) && call(public void Engine.put(Entity)):
        "put() must not be called explicitly; it is called by an advice of Entity";

    /**
     *

     * Declares it as an error to declare an entity constructor that does not use an Engine as its first parameter.
     * This parameter is necessary for the advice above to work.
     *

     *

     * Note that projects that do not process this library's aspects may do this another way; but as they don't
     * process this aspect, they also don't get an error for doing so.
     *

     */
    declare error: execution(Entity+.new(..)) && !execution(Entity+.new(Engine, ..)):
        "Entity constructors must provide an engine as their first argument";
}


fixed aspect

fixed aspect

package org.springframework.datastore.graph.neo4j.spi.node;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.datastore.graph.api.GraphEntity;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.datastore.graph.neo4j.fieldaccess.*;
import org.springframework.datastore.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;

import java.lang.reflect.Field;

import static org.springframework.datastore.graph.neo4j.fieldaccess.DoReturn.unwrap;
import org.springframework.datastore.graph.neo4j.fieldaccess.DetachableEntityStateAccessorsFactory;

/**
 * Aspect to turn an object annotated with GraphEntity into a graph entity using Neo4J.
 * Delegates all field access (except for fields assumed to be transient)
 * to an underlying Neo4 graph node.
 *
 * @author Rod Johnson
 */
public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEntity, NodeBacked> {
    private GraphDatabaseContext graphDatabaseContext;
    private DetachableEntityStateAccessorsFactory entityStateAccessorsFactory;

    @Autowired
	public void init(GraphDatabaseContext ctx, DetachableEntityStateAccessorsFactory entityStateAccessorsFactory) {
        this.graphDatabaseContext = ctx;
        this.entityStateAccessorsFactory = entityStateAccessorsFactory;
	}

	//-------------------------------------------------------------------------
	// Advise user-defined constructors of NodeBacked objects to create a new Neo4J backing node
	//-------------------------------------------------------------------------
	pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
		execution((@GraphEntity *).new(..)) &&
		!execution((@GraphEntity *).new(Node)) &&
		this(entity);  // && !cflow(execution(* fromStateInternal(..));

	// Create a new node in the Graph if no Node was passed in a constructor
	before(NodeBacked entity) : arbitraryUserConstructorOfNodeBackedObject(entity) {
        entity.underlyingState = entityStateAccessorsFactory.getEntityStateAccessors(entity);
        if (graphDatabaseContext.transactionIsRunning()) {
            entity.underlyingState.createAndAssignNode();
        } else {
            log.warn("New Nodebacked created outside of transaction "+ entity.getClass());
        }
	}

    // Introduced field
	private Node NodeBacked.underlyingNode;
    private EntityStateAccessors<NodeBacked> NodeBacked.underlyingState;

	public void NodeBacked.setUnderlyingNode(Node n) {
		this.underlyingNode = n;
        if (this.underlyingState == null) {
            this.underlyingState = Neo4jNodeBacking.aspectOf().entityStateAccessorsFactory.getEntityStateAccessors(this);
        } else {
            this.underlyingState.setNode(n);
        }
	}

	public Node NodeBacked.getUnderlyingNode() {
		return underlyingNode;
	}

    public boolean NodeBacked.hasUnderlyingNode() {
        return underlyingNode!=null;
    }

	public Relationship NodeBacked.relateTo(NodeBacked nb, RelationshipType type) {
		return this.underlyingNode.createRelationshipTo(nb.getUnderlyingNode(), type);
	}

	public Long NodeBacked.getNodeId() {
        if (!hasUnderlyingNode()) return null;
		return underlyingNode.getId();
	}

    public  Iterable<? extends NodeBacked> NodeBacked.find(final Class<? extends NodeBacked> targetType, TraversalDescription traversalDescription) {
        if (!hasUnderlyingNode()) throw new IllegalStateException("No node attached to " + this);
        final Traverser traverser = traversalDescription.traverse(this.getUnderlyingNode());
        return new NodeBackedNodeIterableWrapper(traverser, targetType, Neo4jNodeBacking.aspectOf().graphDatabaseContext);
    }
    /* todo Andy Clement
    public Iterable<? extends NodeBacked> NodeBacked.traverse(TraversalDescription traversalDescription) {
        final Class<? extends NodeBacked> target = this.getClass();
        return this.traverse(target,traversalDescription);
    }
    */

    /* todo Andy Clement
    public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N node, Class<R> relationshipType, String type) {
        Relationship rel = this.getUnderlyingNode().createRelationshipTo(node.getUnderlyingNode(), DynamicRelationshipType.withName(type));
        Neo4jNodeBacking.aspectOf().relationshipEntityInstantiator.createEntityFromState(rel, relationshipType);
    }
    */
    public RelationshipBacked NodeBacked.relateTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Relationship rel = this.getUnderlyingNode().createRelationshipTo(node.getUnderlyingNode(), DynamicRelationshipType.withName(type));
        return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
    }

    public RelationshipBacked NodeBacked.getRelationshipTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Node myNode=this.getUnderlyingNode();
        Node otherNode=node.getUnderlyingNode();
        for (Relationship rel : this.getUnderlyingNode().getRelationships(DynamicRelationshipType.withName(type))) {
            if (rel.getOtherNode(myNode).equals(otherNode)) return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
        }
        return null;
    }

	public final boolean NodeBacked.equals(Object obj) {
        if (obj == this) return true;
        if (!hasUnderlyingNode()) return false;
		if (obj instanceof NodeBacked) {
			return this.getUnderlyingNode().equals(((NodeBacked) obj).getUnderlyingNode());
		}
		return false;
	}

	public final int NodeBacked.hashCode() {
        if (!hasUnderlyingNode()) return System.identityHashCode(this);
		return getUnderlyingNode().hashCode();
	}

    Object around(NodeBacked entity): entityFieldGet(entity) {
        Object result=entity.underlyingState.getValue(field(thisJoinPoint));
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity);
    }

    Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
        Object result=entity.underlyingState.setValue(field(thisJoinPoint),newVal);
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity,result);
	}

    Field field(JoinPoint joinPoint) {
        FieldSignature fieldSignature = (FieldSignature)joinPoint.getSignature();
        return fieldSignature.getField();
    }
}

package org.springframework.datastore.graph.neo4j.spi.node;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.datastore.graph.api.GraphEntity;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.datastore.graph.neo4j.fieldaccess.*;
import org.springframework.datastore.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;

import java.lang.reflect.Field;

import static org.springframework.datastore.graph.neo4j.fieldaccess.DoReturn.unwrap;
import org.springframework.datastore.graph.neo4j.fieldaccess.DetachableEntityStateAccessorsFactory;

/**
 * Aspect to turn an object annotated with GraphEntity into a graph entity using Neo4J.
 * Delegates all field access (except for fields assumed to be transient)
 * to an underlying Neo4 graph node.
 *
 * @author Rod Johnson
 */
public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEntity, NodeBacked> {
    private GraphDatabaseContext graphDatabaseContext;
    private DetachableEntityStateAccessorsFactory entityStateAccessorsFactory;

    @Autowired
	public void init(GraphDatabaseContext ctx, DetachableEntityStateAccessorsFactory entityStateAccessorsFactory) {
        this.graphDatabaseContext = ctx;
        this.entityStateAccessorsFactory = entityStateAccessorsFactory;
	}

	//-------------------------------------------------------------------------
	// Advise user-defined constructors of NodeBacked objects to create a new Neo4J backing node
	//-------------------------------------------------------------------------
	pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
		execution((@GraphEntity *).new(..)) &&
		!execution((@GraphEntity *).new(Node)) &&
		this(entity);  // && !cflow(execution(* fromStateInternal(..));

	// Create a new node in the Graph if no Node was passed in a constructor
	before(NodeBacked entity) : arbitraryUserConstructorOfNodeBackedObject(entity) {
        entity.underlyingState = entityStateAccessorsFactory.getEntityStateAccessors(entity);
        if (graphDatabaseContext.transactionIsRunning()) {
            entity.underlyingState.createAndAssignNode();
        } else {
            log.warn("New Nodebacked created outside of transaction "+ entity.getClass());
        }
	}

    // Introduced field
	private Node NodeBacked.underlyingNode;
    private EntityStateAccessors<NodeBacked> NodeBacked.underlyingState;

	public void NodeBacked.setUnderlyingNode(Node n) {
		this.underlyingNode = n;
        if (this.underlyingState == null) {
            this.underlyingState = entityStateAccessorsFactory.getEntityStateAccessors(this);
        } else {
            this.underlyingState.setNode(n);
        }
	}

	public Node NodeBacked.getUnderlyingNode() {
		return underlyingNode;
	}

    public boolean NodeBacked.hasUnderlyingNode() {
        return underlyingNode!=null;
    }

	public Relationship NodeBacked.relateTo(NodeBacked nb, RelationshipType type) {
		return this.underlyingNode.createRelationshipTo(nb.getUnderlyingNode(), type);
	}

	public Long NodeBacked.getNodeId() {
        if (!hasUnderlyingNode()) return null;
		return underlyingNode.getId();
	}

    public  Iterable<? extends NodeBacked> NodeBacked.find(final Class<? extends NodeBacked> targetType, TraversalDescription traversalDescription) {
        if (!hasUnderlyingNode()) throw new IllegalStateException("No node attached to " + this);
        final Traverser traverser = traversalDescription.traverse(this.getUnderlyingNode());
        return new NodeBackedNodeIterableWrapper(traverser, targetType, Neo4jNodeBacking.aspectOf().graphDatabaseContext);
    }
    /* todo Andy Clement
    public Iterable<? extends NodeBacked> NodeBacked.traverse(TraversalDescription traversalDescription) {
        final Class<? extends NodeBacked> target = this.getClass();
        return this.traverse(target,traversalDescription);
    }
    */

    /* todo Andy Clement
    public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N node, Class<R> relationshipType, String type) {
        Relationship rel = this.getUnderlyingNode().createRelationshipTo(node.getUnderlyingNode(), DynamicRelationshipType.withName(type));
        Neo4jNodeBacking.aspectOf().relationshipEntityInstantiator.createEntityFromState(rel, relationshipType);
    }
    */
    public RelationshipBacked NodeBacked.relateTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Relationship rel = this.getUnderlyingNode().createRelationshipTo(node.getUnderlyingNode(), DynamicRelationshipType.withName(type));
        return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
    }

    public RelationshipBacked NodeBacked.getRelationshipTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Node myNode=this.getUnderlyingNode();
        Node otherNode=node.getUnderlyingNode();
        for (Relationship rel : this.getUnderlyingNode().getRelationships(DynamicRelationshipType.withName(type))) {
            if (rel.getOtherNode(myNode).equals(otherNode)) return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
        }
        return null;
    }

	public final boolean NodeBacked.equals(Object obj) {
        if (obj == this) return true;
        if (!hasUnderlyingNode()) return false;
		if (obj instanceof NodeBacked) {
			return this.getUnderlyingNode().equals(((NodeBacked) obj).getUnderlyingNode());
		}
		return false;
	}

	public final int NodeBacked.hashCode() {
        if (!hasUnderlyingNode()) return System.identityHashCode(this);
		return getUnderlyingNode().hashCode();
	}

    Object around(NodeBacked entity): entityFieldGet(entity) {
        Object result=entity.underlyingState.getValue(field(thisJoinPoint));
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity);
    }

    Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
        Object result=entity.underlyingState.setValue(field(thisJoinPoint),newVal);
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity,result);
	}

    Field field(JoinPoint joinPoint) {
        FieldSignature fieldSignature = (FieldSignature)joinPoint.getSignature();
        return fieldSignature.getField();
    }
}


Tweak X-Trace API call logging

Tweak X-Trace API call logging

package edu.brown.cs.systems.retro.aspects.cpu;

import edu.brown.cs.systems.baggage.Baggage;
import edu.brown.cs.systems.baggage.DetachedBaggage;
import edu.brown.cs.systems.retro.resources.CPUTracking;
import edu.brown.cs.systems.retro.resources.Execution;
import edu.brown.cs.systems.retro.throttling.ThrottlingPoint;

/** Intercepts X-Trace API calls in user code. Don't apply these pointcuts to the X-Trace library itself!
 *
 * @author jon */
public aspect XTraceAPICalls {

    before(): call(void Baggage.start(..)) || call(DetachedBaggage Baggage.swap(..)) {
        Execution.CPU.finished(thisJoinPointStaticPart);
    }

    after(): call(void Baggage.start(..)) || call(DetachedBaggage Baggage.swap(..)) {
        Execution.CPU.starting(thisJoinPointStaticPart);
    }

    /** Whenever the XTraceContext is cleared, log an event to indicate the end of CPU processing bounds */
    before(): call(* Baggage.stop(..)) || call(* Baggage.discard(..)) {
        Execution.CPU.finished(thisJoinPointStaticPart);
    }

    before(): call(void ThrottlingPoint+.throttle()) {
        CPUTracking.finishTracking(); // finish and log current cycles
    }

    after():  call(void ThrottlingPoint+.throttle()) {
        CPUTracking.startTracking(); // start tracking cycles
    }

}

package edu.brown.cs.systems.retro.aspects.cpu;

import edu.brown.cs.systems.baggage.Baggage;
import edu.brown.cs.systems.baggage.DetachedBaggage;
import edu.brown.cs.systems.retro.resources.CPUTracking;
import edu.brown.cs.systems.retro.resources.Execution;
import edu.brown.cs.systems.retro.throttling.ThrottlingPoint;

/** Intercepts X-Trace API calls in user code. Don't apply these pointcuts to the X-Trace library itself!
 *
 * @author jon */
public aspect XTraceAPICalls {

    before(): call(void Baggage.start(..)) || call(DetachedBaggage Baggage.swap(..)) {
        Execution.CPU.finished(thisJoinPointStaticPart);
    }

    after(): call(void Baggage.start(..)) || call(DetachedBaggage Baggage.swap(..)) || call(* Baggage.join(..)) {
        Execution.CPU.starting(thisJoinPointStaticPart);
    }

    /** Whenever the XTraceContext is cleared, log an event to indicate the end of CPU processing bounds */
    before(): call(* Baggage.stop(..)) || call(* Baggage.discard(..)) {
        Execution.CPU.finished(thisJoinPointStaticPart);
    }

    before(): call(void ThrottlingPoint+.throttle()) {
        CPUTracking.finishTracking(); // finish and log current cycles
    }

    after():  call(void ThrottlingPoint+.throttle()) {
        CPUTracking.startTracking(); // start tracking cycles
    }

}