Support Spring Expression Language in Spring AOP

User Case
We want to create @Loggable so developers can use it to specify log level and what to log before or after the method being called. developers can use #p0, #p1 to log param values, use #result to log response or specify any valid spring expression.

Learning How to Do it from Spring Code
We know that spring cache annotations supports spring expression language, we can use #p0, #p1 in @Cacheable, use #result in @CachePut. So we can debug spring cache code to figure it how it works.

Relate code in Spring CacheAspectSupport.execute
CacheAspectSupport.generateKey(CacheOperationContext, Object)

CacheOperationExpressionEvaluator.createEvaluationContext
if (result != NO_RESULT) evaluationContext.setVariable(RESULT_VARIABLE, result);
- To support #result, we just put method return result value into the varaible: result.

The Implementation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
    Level level() default Level.INFO;
    /**
     * This value has to be a valid spring expression<br>
     * Example: "'-begin'" or use #p0, #p1 to refer method params.
     * 
     * @return
     */
    String beginMessage() default "";
    /**
     * This value has to be a valid spring expression<br>
     * Example: "#result", "'-end'"
     * 
     * @return
     */
    String endMessage() default "";
}
@Aspect
@Component
public class LoggableAspect {
    @Value("${throw.exception.if.invalid.expression:true}")
    private boolean throwExceptionIfInvalidExpression;
    public static final String RESULT_VARIABLE = "result";
    /**
     * It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
     */
    private static final ParameterNameDiscoverer parameterNameDiscoverer =
            new LocalVariableTableParameterNameDiscoverer();
    /**
     * SpEL parser. Instances are reusable and thread-safe.
     */
    private static final ExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(com.sony.sie.kamaji.vue.metadata.aop.Loggable)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        final Class<?> declaringClass = method.getDeclaringClass();
        final Logger logger = LoggerFactory.getLogger(declaringClass);
        final Loggable loggable = method.getAnnotation(Loggable.class);
        Object result = null;
        if (loggable != null) {
            final Level logLevel = loggable.level();
            if (whetherParseExpression(loggable.beginMessage(), logger, logLevel)) {
                final EvaluationContext beginContext = new MethodBasedEvaluationContext(invocation.getThis(), method,
                        invocation.getArguments(), parameterNameDiscoverer);
                parseExpressionValue(logger, logLevel, loggable.beginMessage(), beginContext);
            }
            try {
                result = invocation.proceed();
                if (whetherParseExpression(loggable.endMessage(), logger, logLevel)) {
                    final EvaluationContext context = new MethodBasedEvaluationContext(invocation.getThis(), method,
                            invocation.getArguments(), parameterNameDiscoverer);
                    context.setVariable(RESULT_VARIABLE, result);
                    parseExpressionValue(logger, logLevel, loggable.endMessage(), context);
                }
                return result;
            } catch (final RuntimeException e) {
                logValues(logger, "Failed with exception: " + e.getMessage(), logLevel);
                throw e;
            }
        }
        return invocation.proceed();
    }

    private void parseExpressionValue(final Logger logger, final Level logLevel, final String expression,
            final EvaluationContext context) {
        if (StringUtils.isBlank(expression)) {
            return;
        }
        Object value = null;
        try {
            value = parser.parseExpression(expression).getValue(context);
        } catch (final Exception e) {
            if (throwExceptionIfInvalidExpression) {
                throw new VMSBusinessException(ErrorCode.internal_error, e,
                        "Failed to parse expression: " + expression);
            }
        }
        logValues(logger, value != null ? value.toString() : expression, logLevel);
    }

    /**
     * If the log level is not enabled, no need to do anything at all.
     */
    private boolean whetherParseExpression(String expression, final Logger logger, final Level logLevel) {
        if (StringUtils.isBlank(expression)) {
            return false;
        }
        switch (logLevel) {
            case INFO:
                return logger.isInfoEnabled();
            case ERROR:
                return logger.isErrorEnabled();
            case WARN:
                return logger.isWarnEnabled();
            case DEBUG:
                return logger.isDebugEnabled();
            case TRACE:
                return logger.isTraceEnabled();
            default:
                return false;
        }
    }
}

Cassandra in Practice

Not using the “in” query for multiple partitions
- Query them one by one instead

Primary key vs partition key
The first part of primary key is partition key which determines which node stores the data.
Composite/compound keys
skinny rows
- the primary key only contains the partition key
wide rows

- the primary key contains columns other than the partition key

primary key restrictions
- it must contain all the primary key columns of the base table. This ensures that every row of the view correspond to exactly one row of the base table.
- it can only contain a single column that is not a primary key column in the base table.

Materialized view
- implemented as normal Cassandra table which takes as the same amount of disk space as the base table

Table design
- Determine what queries to support, use different tables(or Materialized view) for different queries if needed
- Avoid hot spot and unbounded row growth
- Spreads data evenly
- Minimal partitions read
DESCending for time to search for recent, time-based data


We can only run EQ or IN in partition key.

How deletes are implemented and why
Delete and tombstones
Understanding Deletes
A row tombstone is a row with no liveness_info and no cells.
A cell tombstone: no liveness_info at the column level
Range delete
Partition delete


Local Index
Secondary index is slow, requires to access all nodes
- only suited for low cardinality data

SASI - SStable-Attached Secondary Indexing
- a new on-disk format based on B+ trees
- it attaches to each sstable/memtable its own immutable index file

clqsh
DESCRIBE keyspaces;
describe tables;

COPY keyspace.table to 'output.txt';
COPY keyspace.table(column1,c2) to 'output.txt';

Write query result to file
cqlsh -e'cqlQuery' > output.txt

Use CAPTURE command to export the query result to a file:
cqlsh> CAPTURE
cqlsh> CAPTURE '~/output.txt';

Iterator vs Iterable - Don't use Iterator as cache value

The Problem
Can you figure it out what's the issue in the following code?
@Cacheable(key = "#appName", sync = true)
public Iterator<Message> findActiveMessages(final String appName) {}

Iterator vs Iterable
In most case we can use either Iterable or Iterator, but there is one key difference: 
- if we need traverse and get the value multiple times, then we can't use Iterator.

As once you loop all the data in the iterator, the iterator points to the last position +1 and is not usable anymore. Now if we can iterator.haneNext(), it would be false; if we try to loop it again, we will get empty data.

Unless you define it as ListIterator and manually move back one by one till reach the beginning, which is inefficient and we don't usually code that way.

Takeaway
Don't use Iterator when need traverse multiple times
Don't use Iterator as cache value
Don't store Iterator in collection.

Labels

Java (159) Lucene-Solr (110) Interview (59) All (58) J2SE (53) Algorithm (45) Soft Skills (36) Eclipse (34) Code Example (31) Linux (24) JavaScript (23) Spring (22) Windows (22) Web Development (20) Nutch2 (18) Tools (18) Bugs (17) Debug (15) Defects (14) Text Mining (14) J2EE (13) Network (13) PowerShell (11) Chrome (9) Design (9) How to (9) Learning code (9) Performance (9) Troubleshooting (9) UIMA (9) html (9) Http Client (8) Maven (8) Security (8) bat (8) blogger (8) Big Data (7) Continuous Integration (7) Google (7) Guava (7) JSON (7) Problem Solving (7) ANT (6) Coding Skills (6) Database (6) Dynamic Languages (6) Scala (6) Shell (6) css (6) Algorithm Series (5) Cache (5) IDE (5) Lesson Learned (5) Programmer Skills (5) System Design (5) Tips (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) Miscs (4) OpenNLP (4) Project Managment (4) Spark (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Bit Operation (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Firefox (2) Google Drive (2) Gson (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Python (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts